From a3729c1ad6ba2fb46f879ec7ea67c3afc02e9859 Mon Sep 17 00:00:00 2001 From: testinginprod <98415576+testinginprod@users.noreply.github.com> Date: Tue, 1 Oct 2024 21:16:20 +0200 Subject: [PATCH] feat(core/handlers): improve handlers registration DevX (#22007) --- core/appmodule/v2/handlers.go | 50 ++++++++++++++++++++++----------- core/transaction/transaction.go | 8 ++++++ simapp/v2/go.mod | 3 +- simapp/v2/go.sum | 2 ++ 4 files changed, 45 insertions(+), 18 deletions(-) diff --git a/core/appmodule/v2/handlers.go b/core/appmodule/v2/handlers.go index b42698b7e86f..aa11ff58a70c 100644 --- a/core/appmodule/v2/handlers.go +++ b/core/appmodule/v2/handlers.go @@ -10,8 +10,8 @@ import ( type ( // PreMsgHandler is a handler that is executed before Handler. If it errors the execution reverts. PreMsgHandler = func(ctx context.Context, msg transaction.Msg) error - // Handler handles the state transition of the provided message. - Handler = func(ctx context.Context, msg transaction.Msg) (msgResp transaction.Msg, err error) + // HandlerFunc handles the state transition of the provided message. + HandlerFunc = func(ctx context.Context, msg transaction.Msg) (msgResp transaction.Msg, err error) // PostMsgHandler runs after Handler, only if Handler does not error. If PostMsgHandler errors // then the execution is reverted. PostMsgHandler = func(ctx context.Context, msg, msgResp transaction.Msg) error @@ -19,10 +19,10 @@ type ( // 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 + // RegisterPreMsgHandler will register a specific message handler hooking into the message with // the provided name. RegisterPreMsgHandler(msgName string, handler PreMsgHandler) - // RegisterGlobalPreHandler will register a global message handler hooking into any message + // RegisterGlobalPreMsgHandler will register a global message handler hooking into any message // being executed. RegisterGlobalPreMsgHandler(handler PreMsgHandler) } @@ -64,10 +64,10 @@ func RegisterMsgPreHandler[Req transaction.Msg]( // 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 + // RegisterPostMsgHandler will register a specific message handler hooking after the execution of message with // the provided name. RegisterPostMsgHandler(msgName string, handler PostMsgHandler) - // RegisterGlobalPostHandler will register a global message handler hooking after the execution of any message. + // RegisterGlobalPostMsgHandler will register a global message handler hooking after the execution of any message. RegisterGlobalPostMsgHandler(handler PostMsgHandler) } @@ -76,7 +76,7 @@ type HasPostMsgHandlers interface { RegisterPostMsgHandlers(router PostMsgRouter) } -// RegisterPostHandler is a helper function that modules can use to not lose type safety when registering handlers to the +// RegisterPostMsgHandler is a helper function that modules can use to not lose type safety when registering handlers to the // PostMsgRouter. Example usage: // ```go // @@ -110,9 +110,20 @@ func RegisterPostMsgHandler[Req, Resp transaction.Msg]( router.RegisterPostMsgHandler(msgName, untypedHandler) } +// Handler defines a handler descriptor. +type Handler struct { + // Func defines the actual handler, the function that runs a request and returns a response. + // Can be query handler or msg handler. + Func HandlerFunc + // MakeMsg instantiates the type of the request, can be used in decoding contexts. + MakeMsg func() transaction.Msg + // MakeMsgResp instantiates a new response, can be used in decoding contexts. + MakeMsgResp func() transaction.Msg +} + // MsgRouter is a router that allows you to register Handlers for specific message types. type MsgRouter = interface { - RegisterHandler(msgName string, handler Handler) error + RegisterHandler(handler Handler) } // HasMsgHandlers is an interface that modules must implement if they want to register Handlers. @@ -142,27 +153,34 @@ type HasQueryHandlers interface { // // func (m Module) RegisterMsgHandlers(router appmodule.MsgRouter) { // handlers := keeper.NewHandlers(m.keeper) -// err := appmodule.RegisterHandler(router, gogoproto.MessageName(types.MsgMint{}), handlers.MsgMint) +// err := appmodule.RegisterHandler(router, handlers.MsgMint) // } // // func (m Module) RegisterQueryHandlers(router appmodule.QueryRouter) { // handlers := keeper.NewHandlers(m.keeper) -// err := appmodule.RegisterHandler(router, gogoproto.MessageName(types.QueryBalanceRequest{}), handlers.QueryBalance) +// err := appmodule.RegisterHandler(router, handlers.QueryBalance) // } // // ``` -func RegisterHandler[Req, Resp transaction.Msg]( +func RegisterMsgHandler[Req, Resp any, PReq transaction.GenericMsg[Req], PResp transaction.GenericMsg[Resp]]( router MsgRouter, - msgName string, - handler func(ctx context.Context, msg Req) (msgResp Resp, err error), -) error { + handler func(ctx context.Context, msg PReq) (msgResp PResp, err error), +) { untypedHandler := func(ctx context.Context, m transaction.Msg) (transaction.Msg, error) { - typed, ok := m.(Req) + typed, ok := m.(PReq) if !ok { return nil, fmt.Errorf("unexpected type %T, wanted: %T", m, *new(Req)) } return handler(ctx, typed) } - return router.RegisterHandler(msgName, untypedHandler) + router.RegisterHandler(Handler{ + Func: untypedHandler, + MakeMsg: func() transaction.Msg { + return PReq(new(Req)) + }, + MakeMsgResp: func() transaction.Msg { + return PResp(new(Resp)) + }, + }) } diff --git a/core/transaction/transaction.go b/core/transaction/transaction.go index 5211c54c66e2..3efd88b5aa29 100644 --- a/core/transaction/transaction.go +++ b/core/transaction/transaction.go @@ -10,6 +10,14 @@ type ( Identity = []byte ) +// GenericMsg defines a generic version of a Msg. +// The GenericMsg refers to the non pointer version of Msg, +// and is required to allow its instantiations in generic contexts. +type GenericMsg[T any] interface { + *T + Msg +} + // Codec defines the TX codec, which converts a TX from bytes to its concrete representation. type Codec[T Tx] interface { // Decode decodes the tx bytes into a DecodedTx, containing diff --git a/simapp/v2/go.mod b/simapp/v2/go.mod index fdbbb0cbf559..1684757a0905 100644 --- a/simapp/v2/go.mod +++ b/simapp/v2/go.mod @@ -5,7 +5,7 @@ go 1.23.1 require ( cosmossdk.io/api v0.7.6 cosmossdk.io/client/v2 v2.0.0-00010101000000-000000000000 - cosmossdk.io/core v1.0.0-alpha.3 + cosmossdk.io/core v1.0.0-alpha.3.0.20241001182821-3f9c9a087760 cosmossdk.io/depinject v1.0.0 cosmossdk.io/log v1.4.1 cosmossdk.io/math v1.3.0 @@ -291,7 +291,6 @@ replace ( // server v2 integration replace ( cosmossdk.io/api => ../../api - cosmossdk.io/core => ../../core cosmossdk.io/runtime/v2 => ../../runtime/v2 cosmossdk.io/server/v2 => ../../server/v2 cosmossdk.io/server/v2/appmanager => ../../server/v2/appmanager diff --git a/simapp/v2/go.sum b/simapp/v2/go.sum index b4a90565cd5b..18f8ff580475 100644 --- a/simapp/v2/go.sum +++ b/simapp/v2/go.sum @@ -192,6 +192,8 @@ cloud.google.com/go/webrisk v1.4.0/go.mod h1:Hn8X6Zr+ziE2aNd8SliSDWpEnSS1u4R9+xX cloud.google.com/go/webrisk v1.5.0/go.mod h1:iPG6fr52Tv7sGk0H6qUFzmL3HHZev1htXuWDEEsqMTg= cloud.google.com/go/workflows v1.6.0/go.mod h1:6t9F5h/unJz41YqfBmqSASJSXccBLtD1Vwf+KmJENM0= cloud.google.com/go/workflows v1.7.0/go.mod h1:JhSrZuVZWuiDfKEFxU0/F1PQjmpnpcoISEXH2bcHC3M= +cosmossdk.io/core v1.0.0-alpha.3.0.20241001182821-3f9c9a087760 h1:jyldjo99XdhT94cIyoSVBT2hxNoI4X/1pEUh4+659e0= +cosmossdk.io/core v1.0.0-alpha.3.0.20241001182821-3f9c9a087760/go.mod h1:3u9cWq1FAVtiiCrDPpo4LhR+9V6k/ycSG4/Y/tREWCY= cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29 h1:NxxUo0GMJUbIuVg0R70e3cbn9eFTEuMr7ev1AFvypdY= cosmossdk.io/core/testing v0.0.0-20240923163230-04da382a9f29/go.mod h1:8s2tPeJtSiQuoyPmr2Ag7meikonISO4Fv4MoO8+ORrs= cosmossdk.io/depinject v1.0.0 h1:dQaTu6+O6askNXO06+jyeUAnF2/ssKwrrszP9t5q050=