github.com/cosmos/cosmos-sdk@v0.50.10/runtime/services/autocli.go (about)

     1  package services
     2  
     3  import (
     4  	"context"
     5  
     6  	gogogrpc "github.com/cosmos/gogoproto/grpc"
     7  	"github.com/cosmos/gogoproto/proto"
     8  	"google.golang.org/grpc"
     9  	protobuf "google.golang.org/protobuf/proto"
    10  	"google.golang.org/protobuf/reflect/protoreflect"
    11  	"google.golang.org/protobuf/reflect/protoregistry"
    12  
    13  	autocliv1 "cosmossdk.io/api/cosmos/autocli/v1"
    14  	cosmosmsg "cosmossdk.io/api/cosmos/msg/v1"
    15  	"cosmossdk.io/core/appmodule"
    16  
    17  	"github.com/cosmos/cosmos-sdk/types/module"
    18  )
    19  
    20  // AutoCLIQueryService implements the cosmos.autocli.v1.Query service.
    21  type AutoCLIQueryService struct {
    22  	autocliv1.UnimplementedQueryServer
    23  
    24  	moduleOptions map[string]*autocliv1.ModuleOptions
    25  }
    26  
    27  // NewAutoCLIQueryService returns a AutoCLIQueryService for the provided modules.
    28  func NewAutoCLIQueryService(appModules map[string]interface{}) *AutoCLIQueryService {
    29  	return &AutoCLIQueryService{
    30  		moduleOptions: ExtractAutoCLIOptions(appModules),
    31  	}
    32  }
    33  
    34  // ExtractAutoCLIOptions extracts autocli ModuleOptions from the provided app modules.
    35  //
    36  // Example Usage:
    37  //
    38  //	ExtractAutoCLIOptions(ModuleManager.Modules)
    39  func ExtractAutoCLIOptions(appModules map[string]interface{}) map[string]*autocliv1.ModuleOptions {
    40  	moduleOptions := map[string]*autocliv1.ModuleOptions{}
    41  	for modName, mod := range appModules {
    42  		if autoCliMod, ok := mod.(interface {
    43  			AutoCLIOptions() *autocliv1.ModuleOptions
    44  		}); ok {
    45  			moduleOptions[modName] = autoCliMod.AutoCLIOptions()
    46  			continue
    47  		}
    48  
    49  		cfg := &autocliConfigurator{}
    50  
    51  		// try to auto-discover options based on the last msg and query
    52  		// services registered for the module
    53  		if mod, ok := mod.(module.HasServices); ok {
    54  			mod.RegisterServices(cfg)
    55  		}
    56  
    57  		if mod, ok := mod.(appmodule.HasServices); ok {
    58  			err := mod.RegisterServices(cfg)
    59  			if err != nil {
    60  				panic(err)
    61  			}
    62  		}
    63  
    64  		// check for errors in the configurator
    65  		if cfg.Error() != nil {
    66  			panic(cfg.Error())
    67  		}
    68  
    69  		haveServices := false
    70  		modOptions := &autocliv1.ModuleOptions{}
    71  		if cfg.msgServer.serviceName != "" {
    72  			haveServices = true
    73  			modOptions.Tx = &autocliv1.ServiceCommandDescriptor{
    74  				Service: cfg.msgServer.serviceName,
    75  			}
    76  		}
    77  
    78  		if cfg.queryServer.serviceName != "" {
    79  			haveServices = true
    80  			modOptions.Query = &autocliv1.ServiceCommandDescriptor{
    81  				Service: cfg.queryServer.serviceName,
    82  			}
    83  		}
    84  
    85  		if haveServices {
    86  			moduleOptions[modName] = modOptions
    87  		}
    88  	}
    89  	return moduleOptions
    90  }
    91  
    92  func (a AutoCLIQueryService) AppOptions(context.Context, *autocliv1.AppOptionsRequest) (*autocliv1.AppOptionsResponse, error) {
    93  	return &autocliv1.AppOptionsResponse{
    94  		ModuleOptions: a.moduleOptions,
    95  	}, nil
    96  }
    97  
    98  // autocliConfigurator allows us to call RegisterServices and introspect the services
    99  type autocliConfigurator struct {
   100  	msgServer     autocliServiceRegistrar
   101  	queryServer   autocliServiceRegistrar
   102  	registryCache *protoregistry.Files
   103  	err           error
   104  }
   105  
   106  var _ module.Configurator = &autocliConfigurator{}
   107  
   108  func (a *autocliConfigurator) MsgServer() gogogrpc.Server { return &a.msgServer }
   109  
   110  func (a *autocliConfigurator) QueryServer() gogogrpc.Server { return &a.queryServer }
   111  
   112  func (a *autocliConfigurator) RegisterMigration(string, uint64, module.MigrationHandler) error {
   113  	return nil
   114  }
   115  
   116  func (a *autocliConfigurator) RegisterService(sd *grpc.ServiceDesc, ss interface{}) {
   117  	if a.registryCache == nil {
   118  		a.registryCache, a.err = proto.MergedRegistry()
   119  	}
   120  
   121  	desc, err := a.registryCache.FindDescriptorByName(protoreflect.FullName(sd.ServiceName))
   122  	if err != nil {
   123  		a.err = err
   124  		return
   125  	}
   126  
   127  	if protobuf.HasExtension(desc.Options(), cosmosmsg.E_Service) {
   128  		a.msgServer.RegisterService(sd, ss)
   129  	} else {
   130  		a.queryServer.RegisterService(sd, ss)
   131  	}
   132  }
   133  func (a *autocliConfigurator) Error() error { return nil }
   134  
   135  // autocliServiceRegistrar is used to capture the service name for registered services
   136  type autocliServiceRegistrar struct {
   137  	serviceName string
   138  }
   139  
   140  func (a *autocliServiceRegistrar) RegisterService(sd *grpc.ServiceDesc, _ interface{}) {
   141  	a.serviceName = sd.ServiceName
   142  }
   143  
   144  var _ autocliv1.QueryServer = &AutoCLIQueryService{}