feat: add basic autocli config to every module (#13786)

* feat: add autocli configs to every module

* auto-discover config

* tests + gov, auth config tweaks

* docs

* Update x/auth/autocli.go

Co-authored-by: Julien Robert <julien@rbrt.fr>

* Update x/auth/autocli.go

Co-authored-by: Julien Robert <julien@rbrt.fr>

* cleanup

Co-authored-by: Julien Robert <julien@rbrt.fr>
Co-authored-by: Amaury <1293565+amaurym@users.noreply.github.com>
This commit is contained in:
Aaron Craelius 2022-11-09 06:44:42 -05:00 committed by GitHub
parent 4f7d9ea233
commit bcdf81cbaf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 131 additions and 34 deletions

View File

@ -4,6 +4,8 @@ import (
"context"
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
gogogrpc "github.com/cosmos/gogoproto/grpc"
"google.golang.org/grpc"
"github.com/cosmos/cosmos-sdk/types/module"
)
@ -22,6 +24,31 @@ func NewAutoCLIQueryService(appModules map[string]module.AppModule) *AutoCLIQuer
AutoCLIOptions() *autocliv1.ModuleOptions
}); ok {
moduleOptions[modName] = autoCliMod.AutoCLIOptions()
} else {
// try to auto-discover options based on the last msg and query
// services registered for the module
cfg := &autocliConfigurator{}
mod.RegisterServices(cfg)
modOptions := &autocliv1.ModuleOptions{}
haveServices := false
if cfg.msgServer.serviceName != "" {
haveServices = true
modOptions.Tx = &autocliv1.ServiceCommandDescriptor{
Service: cfg.msgServer.serviceName,
}
}
if cfg.queryServer.serviceName != "" {
haveServices = true
modOptions.Query = &autocliv1.ServiceCommandDescriptor{
Service: cfg.queryServer.serviceName,
}
}
if haveServices {
moduleOptions[modName] = modOptions
}
}
}
return &AutoCLIQueryService{
@ -35,4 +62,27 @@ func (a AutoCLIQueryService) AppOptions(context.Context, *autocliv1.AppOptionsRe
}, nil
}
// autocliConfigurator allows us to call RegisterServices and introspect the services
type autocliConfigurator struct {
msgServer autocliServiceRegistrar
queryServer autocliServiceRegistrar
}
func (a *autocliConfigurator) MsgServer() gogogrpc.Server { return &a.msgServer }
func (a *autocliConfigurator) QueryServer() gogogrpc.Server { return &a.queryServer }
func (a *autocliConfigurator) RegisterMigration(string, uint64, module.MigrationHandler) error {
return nil
}
// autocliServiceRegistrar is used to capture the service name for registered services
type autocliServiceRegistrar struct {
serviceName string
}
func (a *autocliServiceRegistrar) RegisterService(sd *grpc.ServiceDesc, _ interface{}) {
a.serviceName = sd.ServiceName
}
var _ autocliv1.QueryServer = &AutoCLIQueryService{}

View File

@ -127,8 +127,21 @@ func TestQueryAutoCLIAppOptions(t *testing.T) {
assert.NilError(t, err)
assert.Assert(t, res != nil && res.ModuleOptions != nil)
// make sure we at least have x/auth autocli options
// make sure we have x/auth autocli options which were configured manually
authOpts := res.ModuleOptions["auth"]
assert.Assert(t, authOpts != nil)
assert.Assert(t, authOpts.Query != nil && authOpts.Query.Service != "")
assert.Assert(t, authOpts.Query != nil)
assert.Equal(t, "cosmos.auth.v1beta1.Query", authOpts.Query.Service)
// make sure we have some custom options
assert.Assert(t, len(authOpts.Query.RpcCommandOptions) != 0)
// make sure we have x/staking autocli options which should have been auto-discovered
stakingOpts := res.ModuleOptions["staking"]
assert.Assert(t, stakingOpts != nil)
assert.Assert(t, stakingOpts.Query != nil && stakingOpts.Tx != nil)
assert.Equal(t, "cosmos.staking.v1beta1.Query", stakingOpts.Query.Service)
assert.Equal(t, "cosmos.staking.v1beta1.Msg", stakingOpts.Tx.Service)
// make sure tx module has no autocli options because it has no services
assert.Assert(t, res.ModuleOptions["tx"] == nil)
}

32
x/auth/autocli.go Normal file
View File

@ -0,0 +1,32 @@
package auth
import (
authv1beta1 "cosmossdk.io/api/cosmos/auth/v1beta1"
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
)
// AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
return &autocliv1.ModuleOptions{
Query: &autocliv1.ServiceCommandDescriptor{
Service: authv1beta1.Query_ServiceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "Account",
Use: "account [address]",
Short: "query account by address",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "address"}},
},
{
RpcMethod: "AccountAddressByID",
Use: "address-by-id [acc-num]",
Short: "query account address by account number",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "id"}},
},
},
},
Tx: &autocliv1.ServiceCommandDescriptor{
Service: authv1beta1.Msg_ServiceDesc.ServiceName,
},
}
}

View File

@ -11,7 +11,6 @@ import (
"cosmossdk.io/depinject"
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
"cosmossdk.io/core/appmodule"
modulev1 "cosmossdk.io/api/cosmos/auth/module/v1"
@ -111,11 +110,6 @@ func (am AppModule) IsOnePerModuleType() {}
// IsAppModule implements the appmodule.AppModule interface.
func (am AppModule) IsAppModule() {}
// AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
return types.AutoCLIOptions
}
// NewAppModule creates a new AppModule object
func NewAppModule(cdc codec.Codec, accountKeeper keeper.AccountKeeper, randGenAccountsFn types.RandomGenesisAccountsFn, ss exported.Subspace) AppModule {
return AppModule{

View File

@ -1,26 +0,0 @@
package types
import autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
var AutoCLIOptions = &autocliv1.ModuleOptions{
Query: &autocliv1.ServiceCommandDescriptor{
Service: _Query_serviceDesc.ServiceName,
RpcCommandOptions: []*autocliv1.RpcCommandOptions{
{
RpcMethod: "Account",
Use: "account [address]",
Short: "query account by address",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "address"}},
},
{
RpcMethod: "AccountAddressByID",
Use: "address-by-id [id]",
Short: "query account address by account ID",
PositionalArgs: []*autocliv1.PositionalArgDescriptor{{ProtoField: "id"}},
},
},
},
Tx: &autocliv1.ServiceCommandDescriptor{
Service: _Msg_serviceDesc.ServiceName,
},
}

27
x/gov/autocli.go Normal file
View File

@ -0,0 +1,27 @@
package gov
import (
autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
govv1 "cosmossdk.io/api/cosmos/gov/v1"
govv1beta1 "cosmossdk.io/api/cosmos/gov/v1beta1"
)
// AutoCLIOptions implements the autocli.HasAutoCLIConfig interface.
func (am AppModule) AutoCLIOptions() *autocliv1.ModuleOptions {
return &autocliv1.ModuleOptions{
Tx: &autocliv1.ServiceCommandDescriptor{
Service: govv1.Msg_ServiceDesc.ServiceName,
// map v1beta1 as a sub-command
SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{
"v1beta1": {Service: govv1beta1.Msg_ServiceDesc.ServiceName},
},
},
Query: &autocliv1.ServiceCommandDescriptor{
Service: govv1.Query_ServiceDesc.ServiceName,
// map v1beta1 as a sub-command
SubCommands: map[string]*autocliv1.ServiceCommandDescriptor{
"v1beta1": {Service: govv1beta1.Query_ServiceDesc.ServiceName},
},
},
}
}

View File

@ -17,6 +17,7 @@ import (
modulev1 "cosmossdk.io/api/cosmos/gov/module/v1"
"cosmossdk.io/core/appmodule"
"cosmossdk.io/depinject"
"github.com/cosmos/cosmos-sdk/baseapp"
"github.com/cosmos/cosmos-sdk/client"
"github.com/cosmos/cosmos-sdk/codec"
@ -121,6 +122,12 @@ func (a AppModuleBasic) RegisterInterfaces(registry codectypes.InterfaceRegistry
v1beta1.RegisterInterfaces(registry)
}
// IsOnePerModuleType implements the depinject.OnePerModuleType interface.
func (am AppModule) IsOnePerModuleType() {}
// IsAppModule implements the appmodule.AppModule interface.
func (am AppModule) IsAppModule() {}
// AppModule implements an application module for the gov module.
type AppModule struct {
AppModuleBasic