Skip to content

Commit

Permalink
[FAB-8261] Introduce Multi Errors type
Browse files Browse the repository at this point in the history
This type is used to represent multiple errors,
typically from multiple server targets invoked
by the SDK in the same operation.

Change-Id: I718478751c93e5b430b929dfd82f1d263bf864f7
Signed-off-by: Divyank Katira <Divyank.Katira@securekey.com>
  • Loading branch information
d1vyank committed Feb 14, 2018
1 parent 07808ad commit 39a4250
Show file tree
Hide file tree
Showing 29 changed files with 374 additions and 365 deletions.
4 changes: 2 additions & 2 deletions api/apifabclient/mocks/mockfabclient.gen.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

12 changes: 2 additions & 10 deletions api/apifabclient/proposer.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import (

// ProposalProcessor simulates transaction proposal, so that a client can submit the result for ordering.
type ProposalProcessor interface {
ProcessTransactionProposal(proposal TransactionProposal) (TransactionProposalResult, error)
ProcessTransactionProposal(proposal TransactionProposal) (TransactionProposalResponse, error)
}

// ProposalSender provides the ability for a transaction proposal to be created and sent.
Expand Down Expand Up @@ -43,19 +43,11 @@ type TransactionProposal struct {
Proposal *pb.Proposal
}

// TransactionProposalResponse encapsulates both the result of transaction proposal processing and errors.
// TransactionProposalResponse respresents the result of transaction proposal processing.
type TransactionProposalResponse struct {
TransactionProposalResult
Err error // TODO: consider refactoring
}

// TransactionProposalResult respresents the result of transaction proposal processing.
type TransactionProposalResult struct {
Endorser string
Status int32

Proposal TransactionProposal
ProposalResponse *pb.ProposalResponse
}

// TODO: TransactionProposalResponse and TransactionProposalResult may need better names.
1 change: 0 additions & 1 deletion api/apitxn/resmgmtclient/resmgmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,6 @@ type InstallCCResponse struct {
Target string
Status int32
Info string
Err error
}

// InstantiateCCRequest contains instantiate chaincode request parameters
Expand Down
72 changes: 72 additions & 0 deletions pkg/errors/multi/multi.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package multi

import (
"fmt"
"strings"
)

// Errors is used to represent multiple errors
type Errors []error

// New Errors object with the given errors. Only non-nil errors are added.
func New(errs ...error) error {
errors := Errors{}
for _, err := range errs {
if err != nil {
errors = append(errors, err)
}
}

if len(errors) == 0 {
return nil
}

if len(errors) == 1 {
return errors[0]
}

return errors
}

// Append error to Errors. If the first arg is not an Errors object, one will be created
func Append(errs error, err error) error {
m, ok := errs.(Errors)
if !ok {
return New(errs, err)
}
return append(m, err)
}

// ToError converts Errors to the error interface
// returns nil if no errors are present, a single error object if only one is present
func (errs Errors) ToError() error {
if len(errs) == 0 {
return nil
}
if len(errs) == 1 {
return errs[0]
}
return errs
}

// Error implements the error interface to return a string representation of Errors
func (errs Errors) Error() string {
if len(errs) == 0 {
return ""
}
if len(errs) == 1 {
return errs[0].Error()
}

errors := []string{fmt.Sprint("Multiple errors occurred: ")}
for _, err := range errs {
errors = append(errors, err.Error())
}
return strings.Join(errors, "\n")
}
63 changes: 63 additions & 0 deletions pkg/errors/multi/multi_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
/*
Copyright SecureKey Technologies Inc. All Rights Reserved.
SPDX-License-Identifier: Apache-2.0
*/

package multi

import (
"fmt"
"testing"

"github.com/stretchr/testify/assert"
)

func TestErrorString(t *testing.T) {
testErr := fmt.Errorf("test")
var errs Errors

assert.Equal(t, "", errs.Error())

errs = append(errs, testErr)
assert.Equal(t, testErr.Error(), errs.Error())

errs = append(errs, testErr)
assert.Equal(t, "Multiple errors occurred: \ntest\ntest", errs.Error())
}

func TestAppend(t *testing.T) {
testErr := fmt.Errorf("test")
testErr2 := fmt.Errorf("test2")

m := Append(nil, nil)
assert.Nil(t, m)

m = Append(nil, testErr)
assert.Equal(t, testErr, m)

m = Append(testErr, testErr2)
m1, ok := m.(Errors)
assert.True(t, ok)
assert.Equal(t, testErr, m1[0])
assert.Equal(t, testErr2, m1[1])

m = Append(Errors{testErr}, testErr2)
m1, ok = m.(Errors)
assert.True(t, ok)
assert.Equal(t, testErr, m1[0])
assert.Equal(t, testErr2, m1[1])
}

func TestToError(t *testing.T) {
testErr := fmt.Errorf("test")
var errs Errors

assert.Equal(t, nil, errs.ToError())

errs = append(errs, testErr)
assert.Equal(t, testErr, errs.ToError())

errs = append(errs, testErr)
assert.Equal(t, errs, errs.ToError())
}
4 changes: 4 additions & 0 deletions pkg/errors/status/codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ const (

// NoPeersFound No peers were discovered/configured
NoPeersFound Code = 6

// MultipleErrors multiple errors occurred
MultipleErrors Code = 7
)

// CodeName maps the codes in this packages to human-readable strings
Expand All @@ -49,6 +52,7 @@ var CodeName = map[int32]string{
4: "EMPTY_CERT",
5: "TIMEOUT",
6: "NO_PEERS_FOUND",
7: "MULTIPLE_ERRORS",
}

// ToInt32 cast to int32
Expand Down
8 changes: 6 additions & 2 deletions pkg/errors/status/status.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (

"github.com/pkg/errors"

"github.com/hyperledger/fabric-sdk-go/pkg/errors/multi"
pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer"
grpcstatus "google.golang.org/grpc/status"
)
Expand Down Expand Up @@ -94,8 +95,8 @@ func (g Group) String() string {
return UnknownStatus.String()
}

// FromError returns a Status representing err if it was produced from this
// package, otherwise it returns nil, false.
// FromError returns a Status representing err if available,
// otherwise it returns nil, false.
func FromError(err error) (s *Status, ok bool) {
if err == nil {
return &Status{Code: int32(OK)}, true
Expand All @@ -107,6 +108,9 @@ func FromError(err error) (s *Status, ok bool) {
if s, ok := unwrappedErr.(*Status); ok {
return s, true
}
if m, ok := unwrappedErr.(multi.Errors); ok {
return New(ClientStatus, MultipleErrors.ToInt32(), m.Error(), nil), true
}

return nil, false
}
Expand Down
9 changes: 9 additions & 0 deletions pkg/errors/status/status_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"fmt"
"testing"

"github.com/hyperledger/fabric-sdk-go/pkg/errors/multi"
"github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common"
pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer"
"github.com/pkg/errors"
Expand Down Expand Up @@ -65,6 +66,14 @@ func TestFromError(t *testing.T) {

s, ok = FromError(fmt.Errorf("Test"))
assert.False(t, ok)

errs := multi.Errors{}
errs = append(errs, fmt.Errorf("Test"))
s, ok = FromError(errs)
assert.True(t, ok)
assert.Equal(t, ClientStatus, s.Group)
assert.EqualValues(t, MultipleErrors.ToInt32(), s.Code)
assert.Equal(t, errs.Error(), s.Message)
}

func TestStatusToError(t *testing.T) {
Expand Down
4 changes: 2 additions & 2 deletions pkg/fabric-client/channel/channel.go
Original file line number Diff line number Diff line change
Expand Up @@ -431,7 +431,7 @@ func (c *Channel) QueryByChaincode(request fab.ChaincodeInvokeRequest) ([][]byte
return nil, err
}
resps, err := queryChaincode(c.clientContext, c.name, request, targets)
return filterProposalResponses(resps, err)
return collectProposalResponses(resps), err
}

// QueryBySystemChaincode invokes a chaincode that isn't part of a channel.
Expand All @@ -443,5 +443,5 @@ func (c *Channel) QueryBySystemChaincode(request fab.ChaincodeInvokeRequest) ([]
return nil, err
}
resps, err := queryChaincode(c.clientContext, systemChannel, request, targets)
return filterProposalResponses(resps, err)
return collectProposalResponses(resps), err
}
Loading

0 comments on commit 39a4250

Please sign in to comment.