Skip to content

Commit

Permalink
feat(client/v2): combine autocli and custom cmd within a module (#17088)
Browse files Browse the repository at this point in the history
(cherry picked from commit fa4d87e)

# Conflicts:
#	docs/docs/building-modules/10-autocli.md
  • Loading branch information
julienrbrt authored and mergify[bot] committed Jul 25, 2023
1 parent d234c6a commit f28adc7
Show file tree
Hide file tree
Showing 11 changed files with 155 additions and 87 deletions.
8 changes: 4 additions & 4 deletions client/v2/autocli/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,11 +102,11 @@ func (appOptions AppOptions) EnhanceRootCommandWithBuilder(rootCmd *cobra.Comman
}

if queryCmd := findSubCommand(rootCmd, "query"); queryCmd != nil {
if err := builder.enhanceCommandCommon(queryCmd, appOptions, customQueryCmds, enhanceQuery); err != nil {
if err := builder.enhanceCommandCommon(queryCmd, queryCmdType, appOptions, customQueryCmds); err != nil {
return err
}
} else {
queryCmd, err := builder.BuildQueryCommand(appOptions, customQueryCmds, enhanceQuery)
queryCmd, err := builder.BuildQueryCommand(appOptions, customQueryCmds)
if err != nil {
return err
}
Expand All @@ -115,11 +115,11 @@ func (appOptions AppOptions) EnhanceRootCommandWithBuilder(rootCmd *cobra.Comman
}

if msgCmd := findSubCommand(rootCmd, "tx"); msgCmd != nil {
if err := builder.enhanceCommandCommon(msgCmd, appOptions, customMsgCmds, enhanceMsg); err != nil {
if err := builder.enhanceCommandCommon(msgCmd, msgCmdType, appOptions, customMsgCmds); err != nil {
return err
}
} else {
subCmd, err := builder.BuildMsgCommand(appOptions, customMsgCmds, enhanceMsg)
subCmd, err := builder.BuildMsgCommand(appOptions, customMsgCmds)
if err != nil {
return err
}
Expand Down
72 changes: 57 additions & 15 deletions client/v2/autocli/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,13 @@ import (
"github.com/cosmos/cosmos-sdk/client/flags"
)

type cmdType int

const (
queryCmdType cmdType = iota
msgCmdType
)

func (b *Builder) buildMethodCommandCommon(descriptor protoreflect.MethodDescriptor, options *autocliv1.RpcCommandOptions, exec func(cmd *cobra.Command, input protoreflect.Message) error) (*cobra.Command, error) {
if options == nil {
// use the defaults
Expand Down Expand Up @@ -72,9 +79,9 @@ func (b *Builder) buildMethodCommandCommon(descriptor protoreflect.MethodDescrip
// automatically fill in missing commands.
func (b *Builder) enhanceCommandCommon(
cmd *cobra.Command,
cmdType cmdType,
appOptions AppOptions,
customCmds map[string]*cobra.Command,
buildModuleCommand enhanceCommandFunc,
) error {
moduleOptions := appOptions.ModuleOptions
if len(moduleOptions) == 0 {
Expand All @@ -88,38 +95,54 @@ func (b *Builder) enhanceCommandCommon(

modules := append(maps.Keys(appOptions.Modules), maps.Keys(moduleOptions)...)
for _, moduleName := range modules {
modOpts, hasModuleOptions := moduleOptions[moduleName]

// if we have an existing command skip adding one here
if findSubCommand(cmd, moduleName) != nil {
if subCmd := findSubCommand(cmd, moduleName); subCmd != nil {
if hasModuleOptions {
if err := enhanceCustomCmd(b, subCmd, cmdType, modOpts); err != nil {
return err
}
}

continue
}

// if we have a custom command use that instead of generating one
if custom := customCmds[moduleName]; custom != nil {
// custom commands get added lower down
if custom, ok := customCmds[moduleName]; ok {
if hasModuleOptions {
if err := enhanceCustomCmd(b, custom, cmdType, modOpts); err != nil {
return err
}
}

cmd.AddCommand(custom)
continue
}

// check for autocli options
modOpts := moduleOptions[moduleName]
if modOpts == nil {
// if we don't have module options, skip adding a command as we don't have anything to add
if !hasModuleOptions {
continue
}

if err := buildModuleCommand(b, moduleName, cmd, modOpts); err != nil {
return err
switch cmdType {
case queryCmdType:
if err := enhanceQuery(b, moduleName, cmd, modOpts); err != nil {
return err
}
case msgCmdType:
if err := enhanceMsg(b, moduleName, cmd, modOpts); err != nil {
return err
}
}
}

return nil
}

type enhanceCommandFunc func(builder *Builder, moduleName string, cmd *cobra.Command, modOpts *autocliv1.ModuleOptions) error

// enhanceQuery enhances the provided query command with the autocli commands for a module.
func enhanceQuery(builder *Builder, moduleName string, cmd *cobra.Command, modOpts *autocliv1.ModuleOptions) error {
queryCmdDesc := modOpts.Query
if queryCmdDesc != nil {
if queryCmdDesc := modOpts.Query; queryCmdDesc != nil {
subCmd := topLevelCmd(moduleName, fmt.Sprintf("Querying commands for the %s module", moduleName))
if err := builder.AddQueryServiceCommands(subCmd, queryCmdDesc); err != nil {
return err
Expand All @@ -133,8 +156,7 @@ func enhanceQuery(builder *Builder, moduleName string, cmd *cobra.Command, modOp

// enhanceMsg enhances the provided msg command with the autocli commands for a module.
func enhanceMsg(builder *Builder, moduleName string, cmd *cobra.Command, modOpts *autocliv1.ModuleOptions) error {
txCmdDesc := modOpts.Tx
if txCmdDesc != nil {
if txCmdDesc := modOpts.Tx; txCmdDesc != nil {
subCmd := topLevelCmd(moduleName, fmt.Sprintf("Transactions commands for the %s module", moduleName))
if err := builder.AddMsgServiceCommands(subCmd, txCmdDesc); err != nil {
return err
Expand All @@ -146,6 +168,26 @@ func enhanceMsg(builder *Builder, moduleName string, cmd *cobra.Command, modOpts
return nil
}

// enhanceCustomCmd enhances the provided custom query or msg command autocli commands for a module.
func enhanceCustomCmd(builder *Builder, cmd *cobra.Command, cmdType cmdType, modOpts *autocliv1.ModuleOptions) error {
switch cmdType {
case queryCmdType:
if modOpts.Query != nil && modOpts.Query.EnhanceCustomCommand {
if err := builder.AddQueryServiceCommands(cmd, modOpts.Query); err != nil {
return err
}
}
case msgCmdType:
if modOpts.Tx != nil && modOpts.Tx.EnhanceCustomCommand {
if err := builder.AddMsgServiceCommands(cmd, modOpts.Tx); err != nil {
return err
}
}
}

return nil
}

// outOrStdoutFormat formats the output based on the output flag and writes it to the command's output stream.
func (b *Builder) outOrStdoutFormat(cmd *cobra.Command, out []byte) error {
var err error
Expand Down
10 changes: 8 additions & 2 deletions client/v2/autocli/msg.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,9 @@ import (
// BuildMsgCommand builds the msg commands for all the provided modules. If a custom command is provided for a
// module, this is used instead of any automatically generated CLI commands. This allows apps to a fully dynamic client
// with a more customized experience if a binary with custom commands is downloaded.
func (b *Builder) BuildMsgCommand(appOptions AppOptions, customCmds map[string]*cobra.Command, buildModuleCommand enhanceCommandFunc) (*cobra.Command, error) {
func (b *Builder) BuildMsgCommand(appOptions AppOptions, customCmds map[string]*cobra.Command) (*cobra.Command, error) {
msgCmd := topLevelCmd("tx", "Transaction subcommands")
if err := b.enhanceCommandCommon(msgCmd, appOptions, customCmds, enhanceMsg); err != nil {
if err := b.enhanceCommandCommon(msgCmd, msgCmdType, appOptions, customCmds); err != nil {
return nil, err
}

Expand Down Expand Up @@ -75,6 +75,12 @@ func (b *Builder) AddMsgServiceCommands(cmd *cobra.Command, cmdDescriptor *autoc
return err
}

if findSubCommand(cmd, methodCmd.Name()) != nil {
// do not overwrite existing commands
// @julienrbrt: should we display a warning?
continue
}

if methodCmd != nil {
cmd.AddCommand(methodCmd)
}
Expand Down
13 changes: 6 additions & 7 deletions client/v2/autocli/msg_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import (

var buildModuleMsgCommand = func(moduleName string, b *Builder) (*cobra.Command, error) {
cmd := topLevelCmd(moduleName, fmt.Sprintf("Transactions commands for the %s module", moduleName))

err := b.AddMsgServiceCommands(cmd, testCmdMsgDesc)
return cmd, err
}
Expand Down Expand Up @@ -258,7 +257,7 @@ func TestBuildMsgCommand(t *testing.T) {
"test": {Use: "test", Run: func(cmd *cobra.Command, args []string) {
customCommandCalled = true
}},
}, enhanceMsg)
})
assert.NilError(t, err)
cmd.SetArgs([]string{"test", "tx"})
assert.NilError(t, cmd.Execute())
Expand Down Expand Up @@ -295,12 +294,12 @@ func TestErrorBuildMsgCommand(t *testing.T) {
ValidatorAddressCodec: b.ValidatorAddressCodec,
}

_, err := b.BuildMsgCommand(appOptions, nil, enhanceMsg)
_, err := b.BuildMsgCommand(appOptions, nil)
assert.ErrorContains(t, err, "can't find field un-existent-proto-field")

nonExistentService := &autocliv1.ServiceCommandDescriptor{Service: "un-existent-service"}
appOptions.ModuleOptions["test"].Tx = nonExistentService
_, err = b.BuildMsgCommand(appOptions, nil, enhanceMsg)
_, err = b.BuildMsgCommand(appOptions, nil)
assert.ErrorContains(t, err, "can't find service un-existent-service")
}

Expand Down Expand Up @@ -368,7 +367,7 @@ func TestEnhanceMessageCommand(t *testing.T) {
},
}

err := b.enhanceCommandCommon(cmd, appOptions, map[string]*cobra.Command{}, enhanceMsg)
err := b.enhanceCommandCommon(cmd, msgCmdType, appOptions, map[string]*cobra.Command{})
assert.NilError(t, err)

cmd = &cobra.Command{Use: "test"}
Expand All @@ -377,7 +376,7 @@ func TestEnhanceMessageCommand(t *testing.T) {
customCommands := map[string]*cobra.Command{
"test2": {Use: "test"},
}
err = b.enhanceCommandCommon(cmd, appOptions, customCommands, enhanceMsg)
err = b.enhanceCommandCommon(cmd, msgCmdType, appOptions, customCommands)
assert.NilError(t, err)

cmd = &cobra.Command{Use: "test"}
Expand All @@ -387,6 +386,6 @@ func TestEnhanceMessageCommand(t *testing.T) {
},
}
customCommands = map[string]*cobra.Command{}
err = b.enhanceCommandCommon(cmd, appOptions, customCommands, enhanceMsg)
err = b.enhanceCommandCommon(cmd, msgCmdType, appOptions, customCommands)
assert.NilError(t, err)
}
10 changes: 8 additions & 2 deletions client/v2/autocli/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@ import (
// BuildQueryCommand builds the query commands for all the provided modules. If a custom command is provided for a
// module, this is used instead of any automatically generated CLI commands. This allows apps to a fully dynamic client
// with a more customized experience if a binary with custom commands is downloaded.
func (b *Builder) BuildQueryCommand(appOptions AppOptions, customCmds map[string]*cobra.Command, enhanceQuery enhanceCommandFunc) (*cobra.Command, error) {
func (b *Builder) BuildQueryCommand(appOptions AppOptions, customCmds map[string]*cobra.Command) (*cobra.Command, error) {
queryCmd := topLevelCmd("query", "Querying subcommands")
queryCmd.Aliases = []string{"q"}

if err := b.enhanceCommandCommon(queryCmd, appOptions, customCmds, enhanceQuery); err != nil {
if err := b.enhanceCommandCommon(queryCmd, queryCmdType, appOptions, customCmds); err != nil {
return nil, err
}

Expand Down Expand Up @@ -80,6 +80,12 @@ func (b *Builder) AddQueryServiceCommands(cmd *cobra.Command, cmdDescriptor *aut
return err
}

if findSubCommand(cmd, methodCmd.Name()) != nil {
// do not overwrite existing commands
// @julienrbrt: should we display a warning?
continue
}

cmd.AddCommand(methodCmd)
}

Expand Down
3 changes: 1 addition & 2 deletions client/v2/autocli/query_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -576,7 +576,7 @@ func TestBuildCustomQueryCommand(t *testing.T) {
"test": {Use: "test", Run: func(cmd *cobra.Command, args []string) {
customCommandCalled = true
}},
}, enhanceQuery)
})
assert.NilError(t, err)
cmd.SetArgs([]string{"test", "query"})
assert.NilError(t, cmd.Execute())
Expand All @@ -591,7 +591,6 @@ func TestNotFoundErrors(t *testing.T) {

buildModuleQueryCommand := func(moduleName string, cmdDescriptor *autocliv1.ServiceCommandDescriptor) (*cobra.Command, error) {
cmd := topLevelCmd("query", "Querying subcommands")

err := b.AddMsgServiceCommands(cmd, cmdDescriptor)
return cmd, err
}
Expand Down
Loading

0 comments on commit f28adc7

Please sign in to comment.