diff --git a/core/appmodule/v2/handlers.go b/core/appmodule/v2/handlers.go index c446a46cebd2..6cc54c8208bc 100644 --- a/core/appmodule/v2/handlers.go +++ b/core/appmodule/v2/handlers.go @@ -2,6 +2,7 @@ package appmodulev2 import ( "context" + "fmt" transaction "cosmossdk.io/core/transaction" ) @@ -16,47 +17,136 @@ type ( PostMsgHandler = func(ctx context.Context, msg, msgResp transaction.Msg) error ) -// msg handler - +// PreMsgRouter is a router that allows you to register PreMsgHandlers for specific message types. type PreMsgRouter interface { // RegisterPreHandler will register a specific message handler hooking into the message with // the provided name. - RegisterPreHandler(msgName string, handler PreMsgHandler) + RegisterPreHandler(handler PreMsgHandler) // RegisterGlobalPreHandler will register a global message handler hooking into any message // being executed. RegisterGlobalPreHandler(handler PreMsgHandler) } +// HasPreMsgHandlers is an interface that modules must implement if they want to register PreMsgHandlers. type HasPreMsgHandlers interface { RegisterPreMsgHandlers(router PreMsgRouter) } +// RegisterPreHandler is a helper function that modules can use to not lose type safety when registering PreMsgHandler to the +// PreMsgRouter. Example usage: +// ```go +// +// func (k Keeper) BeforeSend(ctx context.Context, req *types.MsgSend) (*types.QueryBalanceResponse, error) { +// ... before send logic ... +// } +// +// func (m Module) RegisterPreMsgHandlers(router appmodule.PreMsgRouter) { +// appmodule.RegisterPreHandler(router, keeper.BeforeSend) +// } +// +// ``` +func RegisterPreHandler[Req transaction.Msg]( + router PreMsgRouter, + handler func(ctx context.Context, msg Req) error, +) { + untypedHandler := func(ctx context.Context, m transaction.Msg) error { + typed, ok := m.(Req) + if !ok { + return fmt.Errorf("unexpected type %T, wanted: %T", m, *new(Req)) + } + return handler(ctx, typed) + } + router.RegisterPreHandler(untypedHandler) +} + +// MsgRouter is a router that allows you to register Handlers for specific message types. type MsgRouter interface { - Register(msgName string, handler Handler) + RegisterHandler(handler Handler) } +// HasMsgHandlers is an interface that modules must implement if they want to register Handlers. type HasMsgHandlers interface { RegisterMsgHandlers(router MsgRouter) } +// RegisterHandler is a helper function that modules can use to not lose type safety when registering handlers to the +// QueryRouter or MsgRouter. Example usage: +// ```go +// +// func (k Keeper) QueryBalance(ctx context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) { +// ... query logic ... +// } +// +// func (m Module) RegisterQueryHandlers(router appmodule.QueryRouter) { +// appmodule.RegisterHandler(router, keeper.QueryBalance) +// } +// +// ``` +func RegisterHandler[R MsgRouter, Req, Resp transaction.Msg]( + router R, + handler func(ctx context.Context, msg Req) (msgResp Resp, err error), +) { + untypedHandler := func(ctx context.Context, m transaction.Msg) (transaction.Msg, error) { + typed, ok := m.(Req) + if !ok { + return nil, fmt.Errorf("unexpected type %T, wanted: %T", m, *new(Req)) + } + return handler(ctx, typed) + } + router.RegisterHandler(untypedHandler) +} + +// PostMsgRouter is a router that allows you to register PostMsgHandlers for specific message types. type PostMsgRouter interface { // RegisterPostHandler will register a specific message handler hooking after the execution of message with // the provided name. - RegisterPostHandler(msgName string, handler PostMsgHandler) + RegisterPostHandler(handler PostMsgHandler) // RegisterGlobalPostHandler will register a global message handler hooking after the execution of any message. RegisterGlobalPostHandler(handler PostMsgHandler) } +// HasPostMsgHandlers is an interface that modules must implement if they want to register PostMsgHandlers. type HasPostMsgHandlers interface { RegisterPostMsgHandlers(router PostMsgRouter) } -// query handler +// RegisterPostHandler is a helper function that modules can use to not lose type safety when registering handlers to the +// PostMsgRouter. Example usage: +// ```go +// +// func (k Keeper) AfterSend(ctx context.Context, req *types.MsgSend, resp *types.MsgSendResponse) error { +// ... query logic ... +// } +// +// func (m Module) RegisterPostMsgHandlers(router appmodule.PostMsgRouter) { +// appmodule.RegisterPostHandler(router, keeper.AfterSend) +// } +// +// ``` +func RegisterPostHandler[Req, Resp transaction.Msg]( + router PostMsgRouter, + handler func(ctx context.Context, msg Req, msgResp Resp) error, +) { + untypedHandler := func(ctx context.Context, m, mResp transaction.Msg) error { + typed, ok := m.(Req) + if !ok { + return fmt.Errorf("unexpected type %T, wanted: %T", m, *new(Req)) + } + typedResp, ok := mResp.(Resp) + if !ok { + return fmt.Errorf("unexpected type %T, wanted: %T", m, *new(Resp)) + } + return handler(ctx, typed, typedResp) + } + router.RegisterPostHandler(untypedHandler) +} +// QueryRouter is a router that allows you to register QueryHandlers for specific query types. type QueryRouter interface { - Register(queryName string, handler Handler) + Register(handler Handler) } +// HasQueryHandlers is an interface that modules must implement if they want to register QueryHandlers. type HasQueryHandlers interface { RegisterQueryHandlers(router QueryRouter) }