From 7b5e41adf6ee1d3d838128c75416e154f72471c3 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Thu, 29 Jun 2017 15:40:26 +0200 Subject: [PATCH] Move middleware interface into stack, testable handlers --- handler.go | 22 ------------------ handlers/base.go | 3 ++- handlers/sigs.go | 3 ++- stack/helpers.go | 54 ++++++++++++++++++++++++++++++++++++++++++++- stack/interface.go | 24 ++++++++++++++++++++ stack/middleware.go | 8 +++---- 6 files changed, 85 insertions(+), 29 deletions(-) create mode 100644 stack/interface.go diff --git a/handler.go b/handler.go index 14bb31a79b..6a6d556191 100644 --- a/handler.go +++ b/handler.go @@ -15,10 +15,6 @@ type Checker interface { CheckTx(ctx Context, store types.KVStore, tx Tx) (Result, error) } -type CheckerMiddle interface { - CheckTx(ctx Context, store types.KVStore, tx Tx, next Checker) (Result, error) -} - // CheckerFunc (like http.HandlerFunc) is a shortcut for making wrapers type CheckerFunc func(Context, types.KVStore, Tx) (Result, error) @@ -26,16 +22,10 @@ func (c CheckerFunc) CheckTx(ctx Context, store types.KVStore, tx Tx) (Result, e return c(ctx, store, tx) } -var _ Checker = CheckerFunc(nil) - type Deliver interface { DeliverTx(ctx Context, store types.KVStore, tx Tx) (Result, error) } -type DeliverMiddle interface { - DeliverTx(ctx Context, store types.KVStore, tx Tx, next Deliver) (Result, error) -} - // DeliverFunc (like http.HandlerFunc) is a shortcut for making wrapers type DeliverFunc func(Context, types.KVStore, Tx) (Result, error) @@ -43,8 +33,6 @@ func (c DeliverFunc) DeliverTx(ctx Context, store types.KVStore, tx Tx) (Result, return c(ctx, store, tx) } -var _ Deliver = DeliverFunc(nil) - // Handler is anything that processes a transaction type Handler interface { Checker @@ -57,16 +45,6 @@ type Handler interface { // EndBlock(store types.KVStore, height uint64) abci.ResponseEndBlock } -// Middleware is anything that wraps another handler to enhance functionality. -// -// You can use utilities in handlers to construct them, the interfaces -// are exposed in the top-level package to avoid import loops. -type Middleware interface { - CheckerMiddle - DeliverMiddle - Named -} - // Result captures any non-error abci result // to make sure people use error for error cases type Result struct { diff --git a/handlers/base.go b/handlers/base.go index 0d6781940d..0728bc341a 100644 --- a/handlers/base.go +++ b/handlers/base.go @@ -3,6 +3,7 @@ package handlers import ( "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/errors" + "github.com/tendermint/basecoin/stack" "github.com/tendermint/basecoin/txs" "github.com/tendermint/basecoin/types" ) @@ -29,7 +30,7 @@ func (_ SimpleFeeHandler) Name() string { return NameFee } -var _ basecoin.Middleware = SimpleFeeHandler{} +var _ stack.Middleware = SimpleFeeHandler{} // Yes, I know refactor a bit... really too late already diff --git a/handlers/sigs.go b/handlers/sigs.go index 81292222cc..2ebbf6b5c9 100644 --- a/handlers/sigs.go +++ b/handlers/sigs.go @@ -5,6 +5,7 @@ import ( "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/errors" + "github.com/tendermint/basecoin/stack" "github.com/tendermint/basecoin/types" ) @@ -21,7 +22,7 @@ func (_ SignedHandler) Name() string { return NameSigs } -var _ basecoin.Middleware = SignedHandler{} +var _ stack.Middleware = SignedHandler{} func SigPerm(addr []byte) basecoin.Actor { return basecoin.NewActor(NameSigs, addr) diff --git a/stack/helpers.go b/stack/helpers.go index 14fd26557c..ce99e29e68 100644 --- a/stack/helpers.go +++ b/stack/helpers.go @@ -1,12 +1,15 @@ package stack import ( + "github.com/pkg/errors" "github.com/tendermint/basecoin" "github.com/tendermint/basecoin/types" ) const ( - NameVoid = "void" + NameVoid = "void" + NameFail = "fail" + NamePanic = "panic" ) // voidHandler just used to return okay to everything @@ -27,3 +30,52 @@ func (_ voidHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basec func (_ voidHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) { return } + +// failHandler always returns an error +type failHandler struct { + err error +} + +var _ basecoin.Handler = failHandler{} + +func (_ failHandler) Name() string { + return NameFail +} + +// CheckTx always returns the given error +func (f failHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) { + return res, errors.WithStack(f.err) +} + +// DeliverTx always returns the given error +func (f failHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) { + return res, errors.WithStack(f.err) +} + +// panicHandler always panics, using the given error (first choice) or msg (fallback) +type panicHandler struct { + msg string + err error +} + +var _ basecoin.Handler = panicHandler{} + +func (_ panicHandler) Name() string { + return NamePanic +} + +// CheckTx always panics +func (p panicHandler) CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) { + if p.err != nil { + panic(p.err) + } + panic(p.msg) +} + +// DeliverTx always panics +func (p panicHandler) DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx) (res basecoin.Result, err error) { + if p.err != nil { + panic(p.err) + } + panic(p.msg) +} diff --git a/stack/interface.go b/stack/interface.go new file mode 100644 index 0000000000..b57d6dbd04 --- /dev/null +++ b/stack/interface.go @@ -0,0 +1,24 @@ +package stack + +import ( + "github.com/tendermint/basecoin" + "github.com/tendermint/basecoin/types" +) + +type CheckerMiddle interface { + CheckTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Checker) (basecoin.Result, error) +} + +type DeliverMiddle interface { + DeliverTx(ctx basecoin.Context, store types.KVStore, tx basecoin.Tx, next basecoin.Deliver) (basecoin.Result, error) +} + +// Middleware is anything that wraps another handler to enhance functionality. +// +// You can use utilities in handlers to construct them, the interfaces +// are exposed in the top-level package to avoid import loops. +type Middleware interface { + CheckerMiddle + DeliverMiddle + basecoin.Named +} diff --git a/stack/middleware.go b/stack/middleware.go index 0747b849d9..a1b8e58b72 100644 --- a/stack/middleware.go +++ b/stack/middleware.go @@ -9,7 +9,7 @@ import ( // // heavily inspired by negroni's design type middleware struct { - middleware basecoin.Middleware + middleware Middleware next basecoin.Handler } @@ -39,7 +39,7 @@ func (m *middleware) DeliverTx(ctx basecoin.Context, store types.KVStore, tx bas // Stack is the entire application stack type Stack struct { - middles []basecoin.Middleware + middles []Middleware handler basecoin.Handler basecoin.Handler // the compiled version, which we expose } @@ -48,7 +48,7 @@ var _ basecoin.Handler = &Stack{} // NewStack prepares a middleware stack, you must `.Use()` a Handler // before you can execute it. -func NewStack(middlewares ...basecoin.Middleware) *Stack { +func NewStack(middlewares ...Middleware) *Stack { return &Stack{ middles: middlewares, } @@ -64,7 +64,7 @@ func (s *Stack) Use(handler basecoin.Handler) *Stack { return s } -func build(mid []basecoin.Middleware, end basecoin.Handler) basecoin.Handler { +func build(mid []Middleware, end basecoin.Handler) basecoin.Handler { if len(mid) == 0 { return end }