From a73b3f5e971a4473f1a6defd4f5a4ece3ac7b1b2 Mon Sep 17 00:00:00 2001 From: Ethan Frey Date: Mon, 5 Aug 2019 20:17:37 +0200 Subject: [PATCH] Merge PR #4849: Result from error --- types/errors.go | 32 ++++++++++++++++++++++++++++---- types/errors_test.go | 42 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 4 deletions(-) diff --git a/types/errors.go b/types/errors.go index e5c3b1a26b80..7a7a0d4a9dcd 100644 --- a/types/errors.go +++ b/types/errors.go @@ -7,9 +7,11 @@ import ( "strings" "github.com/pkg/errors" - cmn "github.com/tendermint/tendermint/libs/common" abci "github.com/tendermint/tendermint/abci/types" + cmn "github.com/tendermint/tendermint/libs/common" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) // CodeType - ABCI code identifier within codespace @@ -248,10 +250,14 @@ func (err *sdkError) Code() CodeType { // Implements ABCIError. func (err *sdkError) ABCILog() string { errMsg := err.cmnError.Error() + return encodeErrorLog(err.codespace, err.code, errMsg) +} + +func encodeErrorLog(codespace CodespaceType, code CodeType, msg string) string { jsonErr := humanReadableError{ - Codespace: err.codespace, - Code: err.code, - Message: errMsg, + Codespace: codespace, + Code: code, + Message: msg, } var buff bytes.Buffer @@ -282,6 +288,24 @@ func (err *sdkError) QueryResult() abci.ResponseQuery { } } +// ResultFromError will return err.Result() if it implements sdk.Error +// Otherwise, it will use the reflecton from types/error to determine +// the code, codespace, and log. +// +// This is intended to provide a bridge to allow both error types +// to live side-by-side. +func ResultFromError(err error) Result { + if sdk, ok := err.(Error); ok { + return sdk.Result() + } + space, code, log := sdkerrors.ABCIInfo(err, false) + return Result{ + Codespace: CodespaceType(space), + Code: CodeType(code), + Log: encodeErrorLog(CodespaceType(space), CodeType(code), log), + } +} + //---------------------------------------- // REST error utilities diff --git a/types/errors_test.go b/types/errors_test.go index 52de6e14f6a5..0a6df2d3c2c2 100644 --- a/types/errors_test.go +++ b/types/errors_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/stretchr/testify/require" + + sdkerrors "github.com/cosmos/cosmos-sdk/types/errors" ) var codeTypes = []CodeType{ @@ -93,3 +95,43 @@ func TestAppendMsgToErr(t *testing.T) { fmt.Sprintf("Should have formatted the error message of ABCI Log. tc #%d", i)) } } + +func TestResultFromError(t *testing.T) { + cases := map[string]struct { + err error + expect Result + }{ + "sdk.Error": { + err: ErrUnauthorized("not owner"), + expect: Result{ + Codespace: CodespaceRoot, + Code: CodeUnauthorized, + Log: `{"codespace":"sdk","code":4,"message":"not owner"}`, + }, + }, + "types/errors": { + err: sdkerrors.Wrap(sdkerrors.ErrUnauthorized, "not owner"), + expect: Result{ + Codespace: CodespaceRoot, + Code: CodeUnauthorized, + Log: `{"codespace":"sdk","code":4,"message":"not owner: unauthorized"}`, + }, + }, + "stdlib errors": { + err: fmt.Errorf("not owner"), + expect: Result{ + Codespace: CodespaceType("undefined"), + Code: CodeInternal, + // note that we redact the internal errors in the new package to not leak eg. panics + Log: `{"codespace":"undefined","code":1,"message":"internal error"}`, + }, + }, + } + + for name, tc := range cases { + t.Run(name, func(t *testing.T) { + res := ResultFromError(tc.err) + require.Equal(t, tc.expect, res) + }) + } +}