diff --git a/client/v2/autocli/app.go b/client/v2/autocli/app.go index 20b36391d965..367622a2ce21 100644 --- a/client/v2/autocli/app.go +++ b/client/v2/autocli/app.go @@ -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 } @@ -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 } diff --git a/client/v2/autocli/common.go b/client/v2/autocli/common.go index 44c7de453a7e..9fbba5eaccc4 100644 --- a/client/v2/autocli/common.go +++ b/client/v2/autocli/common.go @@ -16,6 +16,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 @@ -73,9 +80,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 { @@ -89,38 +96,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 @@ -134,8 +157,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 @@ -147,6 +169,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 diff --git a/client/v2/autocli/msg.go b/client/v2/autocli/msg.go index 2548e508deda..80f0f7cfa307 100644 --- a/client/v2/autocli/msg.go +++ b/client/v2/autocli/msg.go @@ -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 } @@ -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) } diff --git a/client/v2/autocli/msg_test.go b/client/v2/autocli/msg_test.go index ed70e203f937..e474c38eac38 100644 --- a/client/v2/autocli/msg_test.go +++ b/client/v2/autocli/msg_test.go @@ -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 } @@ -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()) @@ -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") } @@ -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"} @@ -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"} @@ -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) } diff --git a/client/v2/autocli/query.go b/client/v2/autocli/query.go index 4a217a9dd142..6b1c1789eb87 100644 --- a/client/v2/autocli/query.go +++ b/client/v2/autocli/query.go @@ -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 } @@ -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) } diff --git a/client/v2/autocli/query_test.go b/client/v2/autocli/query_test.go index 52afac41e19d..d20b1914f704 100644 --- a/client/v2/autocli/query_test.go +++ b/client/v2/autocli/query_test.go @@ -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()) @@ -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 } diff --git a/docs/docs/building-modules/10-autocli.md b/docs/docs/building-modules/10-autocli.md index 500ba39920c0..711fc19ada79 100644 --- a/docs/docs/building-modules/10-autocli.md +++ b/docs/docs/building-modules/10-autocli.md @@ -21,12 +21,16 @@ The `autocli` package is a [Go library](https://pkg.go.dev/cosmossdk.io/client/v ## Getting Started -Here are the steps to use the `autocli` package: +Here are the steps to use AutoCLI: -1. Define your app's modules that implement the `appmodule.AppModule` interface. -2. Configure how behave `autocli` command generation, by implementing the `func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions` method on the module. Learn more [here](#advanced-usage). -3. Use the `autocli.AppOptions` struct to specifies the modules you defined. If you are using the `depinject` package to manage your app's dependencies, it can automatically create an instance of `autocli.AppOptions` based on your app's configuration. -4. Use the `EnhanceRootCommand()` method provided by `autocli` to add the CLI commands for the specified modules to your root command and can also be found in the `client/v2/autocli/app.go` file. Additionally, this method adds the `autocli` functionality to your app's root command. This method is additive only, meaning that it does not create commands if they are already registered for a module. Instead, it adds any missing commands to the root command. +1. Ensure your app's modules implements the `appmodule.AppModule` interface. +2. (optional) Configure how behave `autocli` command generation, by implementing the `func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions` method on the module. Learn more [here](#advanced-usage). +3. Use the `autocli.AppOptions` struct to specify the modules you defined. If you are using `depinject` / app v2, it can automatically create an instance of `autocli.AppOptions` based on your app's configuration. +4. Use the `EnhanceRootCommand()` method provided by `autocli` to add the CLI commands for the specified modules to your root command. + +:::tip +AutoCLI is additive only, meaning _enhancing_ the root command will only add subcommands that are not already registered. This means that you can use AutoCLI alongside other custom commands within your app. +::: Here's an example of how to use `autocli`: @@ -41,13 +45,14 @@ autoCliOpts := autocli.AppOptions{ Modules: testModules, } -// Get the root command +// Create the root command rootCmd := &cobra.Command{ Use: "app", } -// Enhance the root command with autocli -autocli.EnhanceRootCommand(rootCmd, autoCliOpts) +if err := appOptions.EnhanceRootCommand(rootCmd); err != nil { + return err +} // Run the root command if err := rootCmd.Execute(); err != nil { @@ -55,23 +60,9 @@ if err := rootCmd.Execute(); err != nil { } ``` -## Flags - -`autocli` generates flags for each field in a protobuf message. By default, the names of the flags are generated based on the names of the fields in the message. You can customise the flag names using the `namingOptions` parameter of the `Builder.AddMessageFlags()` method. - -To define flags for a message, you can use the `Builder.AddMessageFlags()` method. This method takes the `cobra.Command` instance and the message type as input, and generates flags for each field in the message. - -```go reference -https://github.com/cosmos/cosmos-sdk/blob/1ac260cb1c6f05666f47e67f8b2cfd6229a55c3b/client/v2/autocli/common.go#L44-L49 -``` - -The `binder` variable returned by the `AddMessageFlags()` method is used to bind the command-line arguments to the fields in the message. - -You can also customise the behavior of the flags using the `namingOptions` parameter of the `Builder.AddMessageFlags()` method. This parameter allows you to specify a custom prefix for the flags, and to specify whether to generate flags for repeated fields and whether to generate flags for fields with default values. - ## Commands and Queries -The `autocli` package generates CLI commands and flags for each method defined in your gRPC service. By default, it generates commands for each RPC method that does not return a stream of messages. The commands are named based on the name of the service method. +`autocli` generates CLI commands and flags for each method defined in your gRPC service. By default, it generates commands for each gRPC services. The commands are named based on the name of the service method. For example, given the following protobuf definition for a service: @@ -81,77 +72,88 @@ service MyService { } ``` -`autocli` will generate a command named `my-method` for the `MyMethod` method. The command will have flags for each field in the `MyRequest` message. +For instance, `autocli` would generate a command named `my-method` for the `MyMethod` method. The command will have flags for each field in the `MyRequest` message. -If you want to customise the behavior of a command, you can define a custom command by implementing the `autocli.Command` interface. You can then register the command with the `autocli.Builder` instance for your application. +It is possible to customize the generation of commands and queries by defining options for each service. -Similarly, you can define a custom query by implementing the `autocli.Query` interface. You can then register the query with the `autocli.Builder` instance for your application. +## Customize generated commands -To add a custom command or query, you can use the `Builder.AddCustomCommand` or `Builder.AddCustomQuery` methods, respectively. These methods take a `cobra.Command` or `cobra.Command` instance, respectively, which can be used to define the behavior of the command or query. +The `AutoCLIOptions()` method on your module allows to specify custom commands, sub-commands or flags for each service, as it was a `cobra.Command` instance, within the `RpcCommandOptions` struct. -## Advanced Usage +```go +*autocliv1.RpcCommandOptions{ + RpcMethod: "Params", // The name of the gRPC service + Use: "params", // Command usage that is displayed in the help + Short: "Query the parameters of the governance process", // Short description of the command + Long: "Query the parameters of the governance process. Specify specific param types (voting|tallying|deposit) to filter results.", // Long description of the command + PositionalArgs: []*autocliv1.PositionalArgDescriptor{ + {ProtoField: "params_type", Optional: true}, // Transform a flag into a positional argument + }, +} +``` ### Specifying Subcommands -By default, `autocli` generates a command for each method in your gRPC service. However, you can specify subcommands to group related commands together. To specify subcommands, you can use the `autocliv1.ServiceCommandDescriptor` struct. +By default, `autocli` generates a command for each method in your gRPC service. However, you can specify subcommands to group related commands together. To specify subcommands, use the `autocliv1.ServiceCommandDescriptor` struct. -This example shows how to use the `autocliv1.ServiceCommandDescriptor` struct to group related commands together and specify subcommands in your gRPC service by defining an instance of `autocliv1.ModuleOptions` in your `autocli.go` file. +This example shows how to use the `autocliv1.ServiceCommandDescriptor` struct to group related commands together and specify subcommands in your gRPC service by defining an instance of `autocliv1.ModuleOptions` in your `autocli.go`. ```go reference -https://github.com/cosmos/cosmos-sdk/blob/release/v0.50.x/x/gov/autocli.go#L13C1-L107 +https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-beta.0/x/gov/autocli.go#L94-L97 ``` -The `AutoCLIOptions()` method in the autocli package allows you to specify the services and sub-commands to be mapped for your app. In the example code, an instance of the `autocliv1.ModuleOptions` struct is defined in the `appmodule.AppModule` implementation located in the `x/gov/autocli.go` file. This configuration groups related commands together and specifies subcommands for each service. - ### Positional Arguments -Positional arguments are arguments that are passed to a command without being specified as a flag. They are typically used for providing additional context to a command, such as a filename or search query. +By default `autocli` generates a flag for each field in your protobuf message. However, you can choose to use positional arguments instead of flags for certain fields. -To add positional arguments to a command, you can use the `autocliv1.PositionalArgDescriptor` struct, as seen in the example below. You need to specify the `ProtoField` parameter, which is the name of the protobuf field that should be used as the positional argument. In addition, if the parameter is a variable-length argument, you can specify the `Varargs` parameter as `true`. This can only be applied to the last positional parameter, and the `ProtoField` must be a repeated field. +To add positional arguments to a command, use the `autocliv1.PositionalArgDescriptor` struct, as seen in the example below. Specify the `ProtoField` parameter, which is the name of the protobuf field that should be used as the positional argument. In addition, if the parameter is a variable-length argument, you can specify the `Varargs` parameter as `true`. This can only be applied to the last positional parameter, and the `ProtoField` must be a repeated field. Here's an example of how to define a positional argument for the `Account` method of the `auth` service: ```go reference -https://github.com/cosmos/cosmos-sdk/blob/release/v0.50.x/x/gov/autocli.go#L80-L86 +https://github.com/cosmos/cosmos-sdk/blob/v0.50.0-beta.0/x/auth/autocli.go#L25-L30 ``` -Here are some example commands that use the positional arguments we defined above: - -To query an account by address: +Then the command can be used as follows, instead of having to specify the `--address` flag: ```bash query auth account cosmos1abcd...xyz ``` -To query an account address by account number: - -```bash - query auth address-by-acc-num 1 -``` - -In both of these commands, the `auth` service is being queried with the `query` subcommand, followed by the specific method being called (`account` or `address-by-acc-num`). The positional argument is included at the end of the command (`cosmos1abcd...xyz` or `1`) to specify the address or account number, respectively. - ### Customising Flag Names -By default, `autocli` generates flag names based on the names of the fields in your protobuf message. However, you can customise the flag names by providing a `FlagOptions` parameter to the `Builder.AddMessageFlags()` method. This parameter allows you to specify custom names for flags based on the names of the message fields. For example, if you have a message with the fields `test` and `test1`, you can use the following naming options to customise the flags +By default, `autocli` generates flag names based on the names of the fields in your protobuf message. However, you can customise the flag names by providing a `FlagOptions`. This parameter allows you to specify custom names for flags based on the names of the message fields. + +For example, if you have a message with the fields `test` and `test1`, you can use the following naming options to customise the flags: ``` go -options := autocliv1.RpcCommandOptions{ +autocliv1.RpcCommandOptions{ FlagOptions: map[string]*autocliv1.FlagOptions{ "test": { Name: "custom_name", }, "test1": { Name: "other_name", }, }, } +``` + +`FlagsOptions` is defined like sub commands in the `AutoCLIOptions()` method on your module. + +### Combining AutoCLI with Other Commands Within A Module + +AutoCLI can be used alongside other commands within a module. For example, the `gov` module uses AutoCLI to generate commands for the `query` subcommand, but also defines custom commands for the `proposer` subcommands. -builder.AddMessageFlags(message, options) +In order to enable this behavior, set in `AutoCLIOptions()` the `EnhanceCustomCommand` field to `true`, for the command type (queries and/or transactions) you want to enhance. + + +```go reference +https://github.com/cosmos/cosmos-sdk/blob/julien/custom-commands/x/gov/autocli.go#L98 ``` -Note that `autocliv1.RpcCommandOptions` is a field of the `autocliv1.ServiceCommandDescriptor` struct, which is defined in the `autocliv1` package. To use this option, you can define an instance of `autocliv1.ModuleOptions` in your `appmodule.AppModule` implementation and specify the `FlagOptions` for the relevant service command descriptor. +If not set to true, `AutoCLI` will not generate commands for the module if there are already commands registered for the module (when `GetTxCmd()` or `GetTxCmd()` are defined). ## Conclusion -`autocli` is a powerful tool for adding CLI interfaces to your Cosmos SDK-based applications. It allows you to easily generate CLI commands and flags from your protobuf messages, and provides many options for customising the behavior of your CLI application. +`autocli` let you generate CLI to your Cosmos SDK-based applications without any cobra boilerplate. It allows you to easily generate CLI commands and flags from your protobuf messages, and provides many options for customising the behavior of your CLI application. -To further enhance your CLI experience with Cosmos SDK-based blockchains, you can use `Hubl`. `Hubl` is a tool that allows you to query any Cosmos SDK-based blockchain using the new AutoCLI feature of the Cosmos SDK. With hubl, you can easily configure a new chain and query modules with just a few simple commands. +To further enhance your CLI experience with Cosmos SDK-based blockchains, you can use `hubl`. `hubl` is a tool that allows you to query any Cosmos SDK-based blockchain using the new AutoCLI feature of the Cosmos SDK. With `hubl`, you can easily configure a new chain and query modules with just a few simple commands. -For more information on `Hubl`, including how to configure a new chain and query a module, see the [Hubl documentation](https://docs.cosmos.network/main/tooling/hubl). +For more information on `hubl`, including how to configure a new chain and query a module, see the [Hubl documentation](https://docs.cosmos.network/main/tooling/hubl). diff --git a/x/bank/autocli.go b/x/bank/autocli.go index 6d839f94ca08..25d65feecb97 100644 --- a/x/bank/autocli.go +++ b/x/bank/autocli.go @@ -34,7 +34,7 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { }, { RpcMethod: "SpendableBalanceByDenom", - Use: "spendable-balances [address] [denom]", + Use: "spendable-balance [address] [denom]", Short: "Query the spendable balance of a single denom for a single account.", PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "address"}, {ProtoField: "denom"}}, }, diff --git a/x/gov/autocli.go b/x/gov/autocli.go index dbd6b0b750c3..fca6466e4d96 100644 --- a/x/gov/autocli.go +++ b/x/gov/autocli.go @@ -95,6 +95,7 @@ func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions { SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{ "v1beta1": {Service: govv1beta1.Query_ServiceDesc.ServiceName}, }, + EnhanceCustomCommand: true, // We still have manual commands in gov that we want to keep }, Tx: &autocliv1.ServiceCommandDescriptor{ Service: govv1.Msg_ServiceDesc.ServiceName, diff --git a/x/gov/client/cli/query.go b/x/gov/client/cli/query.go index d8b4b35f02fc..9ef1dbf729a9 100644 --- a/x/gov/client/cli/query.go +++ b/x/gov/client/cli/query.go @@ -17,12 +17,11 @@ import ( // GetCustomQueryCmd returns the cli query commands for this module // These commands do not rely on gRPC and cannot be autogenerated -// TODO(@julienrbrt) https://github.com/cosmos/cosmos-sdk/issues/16836 func GetCustomQueryCmd(ac address.Codec) *cobra.Command { // Group gov queries under a subcommand govQueryCmd := &cobra.Command{ Use: types.ModuleName, - Short: "Querying commands for the governance module", + Short: "Querying commands for the gov module", DisableFlagParsing: true, SuggestionsMinimumDistance: 2, RunE: client.ValidateCmd, diff --git a/x/gov/module.go b/x/gov/module.go index 146672ac9c28..305174ecd199 100644 --- a/x/gov/module.go +++ b/x/gov/module.go @@ -101,6 +101,12 @@ func (ab AppModuleBasic) GetTxCmd() *cobra.Command { return cli.NewTxCmd(legacyProposalCLIHandlers) } +// GetQueryCmd returns the custom query commands for the gov modules. +// This command will be enhanced by AutoCLI as defined in the AutoCLIOptions() method. +func (ab AppModuleBasic) GetQueryCmd() *cobra.Command { + return cli.GetCustomQueryCmd(ab.ac) +} + func getProposalCLIHandlers(handlers []govclient.ProposalHandler) []*cobra.Command { proposalCLIHandlers := make([]*cobra.Command, 0, len(handlers)) for _, proposalHandler := range handlers {