Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

refactor(core): re-add handlers #21575

Merged
merged 11 commits into from
Sep 9, 2024
137 changes: 129 additions & 8 deletions core/appmodule/v2/handlers.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package appmodulev2

import (
"context"
"fmt"

transaction "cosmossdk.io/core/transaction"
)
Expand All @@ -16,47 +17,167 @@ 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)
}

// RegisterMsgPreHandler is a helper function that modules can use to not lose type safety when registering PreMsgHandler to the
// PreMsgRouter. Example usage:
// ```go
//
// func (h Handlers) BeforeSend(ctx context.Context, req *types.MsgSend) (*types.QueryBalanceResponse, error) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Response type here is invalid

// ... before send logic ...
// }
//
// func (m Module) RegisterPreMsgHandlers(router appmodule.PreMsgRouter) {
// handlers := keeper.NewHandlers(m.keeper)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

a way to use it. it's to simply define before send on the keeper and handle it via keeper, if we wanto to do it msg server style and have a wrapper like handlers then that is also fine.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like to think of the keeper as something only for state (basically, just holding public/private collections), and the business logic is somewhere else.

// appmodule.RegisterMsgPreHandler(router, handlers.BeforeSend)
// }
//
// ```
func RegisterMsgPreHandler[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)
RegisterMsgHandler(handler Handler)
}

// HasMsgHandlers is an interface that modules must implement if they want to register Handlers.
type HasMsgHandlers interface {
RegisterMsgHandlers(router MsgRouter)
}

// RegisterMsgHandler is a helper function that modules can use to not lose type safety when registering handlers to the MsgRouter.
// Example usage:
// ```go
//
// func (h Handlers) Mint(ctx context.Context, req *types.MsgMintRequest) (*types.MsgMintResponse, error) {
// ... query logic ...
// }
//
// func (m Module) RegisterMsgHandlers(router appmodule.MsgRouter) {
// handlers := keeper.NewHandlers(m.keeper)
// appmodule.RegisterMsgHandler(router, handlers.MsgMint)
// }
//
// ```
func RegisterMsgHandler[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.RegisterMsgHandler(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)
RegisterPostMsgHandler(handler PostMsgHandler)
// RegisterGlobalPostHandler will register a global message handler hooking after the execution of any message.
RegisterGlobalPostHandler(handler PostMsgHandler)
RegisterGlobalPostMsgHandler(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 (h Handlers) AfterSend(ctx context.Context, req *types.MsgSend, resp *types.MsgSendResponse) error {
// ... query logic ...
// }
//
// func (m Module) RegisterPostMsgHandlers(router appmodule.PostMsgRouter) {
// handlers := keeper.NewHandlers(m.keeper)
// appmodule.RegisterPostMsgHandler(router, handlers.AfterSend)
// }
//
// ```
func RegisterPostMsgHandler[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.RegisterPostMsgHandler(untypedHandler)
}

// QueryRouter is a router that allows you to register QueryHandlers for specific query types.
type QueryRouter interface {
Register(queryName string, handler Handler)
RegisterQueryHandler(handler Handler)
}

// HasQueryHandlers is an interface that modules must implement if they want to register QueryHandlers.
type HasQueryHandlers interface {
RegisterQueryHandlers(router QueryRouter)
}

// RegisterQueryHandler is a helper function that modules can use to not lose type safety when registering handlers to the Query.Router
// Example usage:
// ```go
//
// func (h Handkers) QueryBalance(ctx context.Context, req *types.QueryBalanceRequest) (*types.QueryBalanceResponse, error) {
// ... query logic ...
// }
//
// func (m Module) RegisterQueryHandlers(router appmodule.QueryRouter) {
// handlers := keeper.NewHandlers(m.keeper)
// appmodule.RegisterHandler(router, handlers.QueryBalance)
// }
//
// ```
func RegisterQueryHandler[R QueryRouter, 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.RegisterQueryHandler(untypedHandler)
}
Loading