diff --git a/pkg/client/channel/api.go b/pkg/client/channel/api.go index 9844449f72..c3eb59c80c 100644 --- a/pkg/client/channel/api.go +++ b/pkg/client/channel/api.go @@ -9,7 +9,6 @@ package channel import ( "time" - "github.com/hyperledger/fabric-sdk-go/pkg/context/api/core" "github.com/hyperledger/fabric-sdk-go/pkg/context/api/fab" "github.com/hyperledger/fabric-sdk-go/pkg/errors/retry" pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer" @@ -28,17 +27,17 @@ type CCEvent struct { type Registration interface { } -// Opts allows the user to specify more advanced options -type Opts struct { +// opts allows the user to specify more advanced options +type opts struct { ProposalProcessors []fab.ProposalProcessor // targets Timeout time.Duration Retry retry.Opts } //Option func for each Opts argument -type Option func(opts *Opts) error +type Option func(opts *opts) error -// Request contains the parameters to execute transaction +// Request contains the parameters to query and execute an invocation transaction type Request struct { ChaincodeID string Fcn string @@ -46,7 +45,7 @@ type Request struct { TransientMap map[string][]byte } -//Response contains response parameters for query and execute transaction +//Response contains response parameters for query and execute an invocation transaction type Response struct { Payload []byte TransactionID fab.TransactionID @@ -55,50 +54,26 @@ type Response struct { Responses []*fab.TransactionProposalResponse } -//Handler for chaining transaction executions -type Handler interface { - Handle(context *RequestContext, clientContext *ClientContext) -} - -//ClientContext contains context parameters for handler execution -type ClientContext struct { - CryptoSuite core.CryptoSuite - Discovery fab.DiscoveryService - Selection fab.SelectionService - Channel fab.Channel // TODO: this should be removed when we have MSP split out. - Transactor fab.Transactor - EventHub fab.EventHub -} - -//RequestContext contains request, opts, response parameters for handler execution -type RequestContext struct { - Request Request - Opts Opts - Response Response - Error error - RetryHandler retry.Handler -} - //WithTimeout encapsulates time.Duration to Option func WithTimeout(timeout time.Duration) Option { - return func(opts *Opts) error { - opts.Timeout = timeout + return func(o *opts) error { + o.Timeout = timeout return nil } } //WithProposalProcessor encapsulates ProposalProcessors to Option func WithProposalProcessor(proposalProcessors ...fab.ProposalProcessor) Option { - return func(opts *Opts) error { - opts.ProposalProcessors = proposalProcessors + return func(o *opts) error { + o.ProposalProcessors = proposalProcessors return nil } } // WithRetry option to configure retries -func WithRetry(opt retry.Opts) Option { - return func(opts *Opts) error { - opts.Retry = opt +func WithRetry(retryOpt retry.Opts) Option { + return func(o *opts) error { + o.Retry = retryOpt return nil } } diff --git a/pkg/client/channel/chclient.go b/pkg/client/channel/chclient.go index 5b50b81183..3c9b9af2c4 100644 --- a/pkg/client/channel/chclient.go +++ b/pkg/client/channel/chclient.go @@ -13,6 +13,7 @@ import ( "github.com/hyperledger/fabric-sdk-go/pkg/context/api/core" + "github.com/hyperledger/fabric-sdk-go/pkg/client/channel/invoke" "github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery" "github.com/hyperledger/fabric-sdk-go/pkg/client/common/discovery/greylist" "github.com/hyperledger/fabric-sdk-go/pkg/context" @@ -88,16 +89,16 @@ func New(c Context) (*Client, error) { // Query chaincode using request and optional options provided func (cc *Client) Query(request Request, options ...Option) (Response, error) { - return cc.InvokeHandler(NewQueryHandler(), request, cc.addDefaultTimeout(core.Query, options...)...) + return cc.InvokeHandler(invoke.NewQueryHandler(), request, cc.addDefaultTimeout(core.Query, options...)...) } // Execute prepares and executes transaction using request and optional options provided func (cc *Client) Execute(request Request, options ...Option) (Response, error) { - return cc.InvokeHandler(NewExecuteHandler(), request, cc.addDefaultTimeout(core.Execute, options...)...) + return cc.InvokeHandler(invoke.NewExecuteHandler(), request, cc.addDefaultTimeout(core.Execute, options...)...) } //InvokeHandler invokes handler using request and options provided -func (cc *Client) InvokeHandler(handler Handler, request Request, options ...Option) (Response, error) { +func (cc *Client) InvokeHandler(handler invoke.Handler, request Request, options ...Option) (Response, error) { //Read execute tx options txnOpts, err := cc.prepareOptsFromOptions(options...) if err != nil { @@ -123,14 +124,14 @@ func (cc *Client) InvokeHandler(handler Handler, request Request, options ...Opt }() select { case <-complete: - return requestContext.Response, requestContext.Error + return Response(requestContext.Response), requestContext.Error case <-time.After(requestContext.Opts.Timeout): return Response{}, status.New(status.ClientStatus, status.Timeout.ToInt32(), "request timed out", nil) } } -func (cc *Client) resolveRetry(ctx *RequestContext, opts Opts) bool { +func (cc *Client) resolveRetry(ctx *invoke.RequestContext, o opts) bool { errs, ok := ctx.Error.(multi.Errors) if !ok { errs = append(errs, ctx.Error) @@ -141,9 +142,9 @@ func (cc *Client) resolveRetry(ctx *RequestContext, opts Opts) bool { cc.greylist.Greylist(e) // Reset context parameters - ctx.Opts.ProposalProcessors = opts.ProposalProcessors + ctx.Opts.ProposalProcessors = o.ProposalProcessors ctx.Error = nil - ctx.Response = Response{} + ctx.Response = invoke.Response{} return true } @@ -152,13 +153,13 @@ func (cc *Client) resolveRetry(ctx *RequestContext, opts Opts) bool { } //prepareHandlerContexts prepares context objects for handlers -func (cc *Client) prepareHandlerContexts(request Request, options Opts) (*RequestContext, *ClientContext, error) { +func (cc *Client) prepareHandlerContexts(request Request, o opts) (*invoke.RequestContext, *invoke.ClientContext, error) { if request.ChaincodeID == "" || request.Fcn == "" { return nil, nil, errors.New("ChaincodeID and Fcn are required") } - clientContext := &ClientContext{ + clientContext := &invoke.ClientContext{ Selection: cc.selection, Discovery: cc.discovery, Channel: cc.channel, @@ -166,11 +167,11 @@ func (cc *Client) prepareHandlerContexts(request Request, options Opts) (*Reques EventHub: cc.eventHub, } - requestContext := &RequestContext{ - Request: request, - Opts: options, - Response: Response{}, - RetryHandler: retry.New(options.Retry), + requestContext := &invoke.RequestContext{ + Request: invoke.Request(request), + Opts: invoke.Opts(o), + Response: invoke.Response{}, + RetryHandler: retry.New(o.Retry), } if requestContext.Opts.Timeout == 0 { @@ -181,8 +182,8 @@ func (cc *Client) prepareHandlerContexts(request Request, options Opts) (*Reques } //prepareOptsFromOptions Reads apitxn.Opts from Option array -func (cc *Client) prepareOptsFromOptions(options ...Option) (Opts, error) { - txnOpts := Opts{} +func (cc *Client) prepareOptsFromOptions(options ...Option) (opts, error) { + txnOpts := opts{} for _, option := range options { err := option(&txnOpts) if err != nil { @@ -194,7 +195,7 @@ func (cc *Client) prepareOptsFromOptions(options ...Option) (Opts, error) { //addDefaultTimeout adds given default timeout if it is missing in options func (cc *Client) addDefaultTimeout(timeOutType core.TimeoutType, options ...Option) []Option { - txnOpts := Opts{} + txnOpts := opts{} for _, option := range options { option(&txnOpts) } diff --git a/pkg/client/channel/chclient_test.go b/pkg/client/channel/chclient_test.go index 644615cbb7..2660d1e08f 100644 --- a/pkg/client/channel/chclient_test.go +++ b/pkg/client/channel/chclient_test.go @@ -11,13 +11,14 @@ import ( "testing" "time" - "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/msp" - "github.com/hyperledger/fabric-sdk-go/pkg/context/api/core" - "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" + "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/msp" + "github.com/hyperledger/fabric-sdk-go/pkg/client/channel/invoke" txnmocks "github.com/hyperledger/fabric-sdk-go/pkg/client/common/mocks" "github.com/hyperledger/fabric-sdk-go/pkg/context" + "github.com/hyperledger/fabric-sdk-go/pkg/context/api/core" "github.com/hyperledger/fabric-sdk-go/pkg/context/api/fab" "github.com/hyperledger/fabric-sdk-go/pkg/errors/retry" "github.com/hyperledger/fabric-sdk-go/pkg/errors/status" @@ -25,8 +26,8 @@ import ( fcmocks "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" "github.com/hyperledger/fabric-sdk-go/pkg/fab/peer" "github.com/hyperledger/fabric-sdk-go/pkg/fab/txn" + "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" ) const ( @@ -225,7 +226,7 @@ type customHandler struct { expectedPayload []byte } -func (c *customHandler) Handle(requestContext *RequestContext, clientContext *ClientContext) { +func (c *customHandler) Handle(requestContext *invoke.RequestContext, clientContext *invoke.ClientContext) { requestContext.Response.Payload = c.expectedPayload } @@ -248,10 +249,10 @@ func TestInvokeHandler(t *testing.T) { // and instead sends the proposal to the given channel type customEndorsementHandler struct { transactor fab.Transactor - next Handler + next invoke.Handler } -func (h *customEndorsementHandler) Handle(requestContext *RequestContext, clientContext *ClientContext) { +func (h *customEndorsementHandler) Handle(requestContext *invoke.RequestContext, clientContext *invoke.ClientContext) { transactionProposalResponses, txnID, err := createAndSendTestTransactionProposal(h.transactor, &requestContext.Request, requestContext.Opts.ProposalProcessors) requestContext.Response.TransactionID = txnID @@ -285,10 +286,10 @@ func TestQueryWithCustomEndorser(t *testing.T) { } response, err := chClient.InvokeHandler( - NewProposalProcessorHandler( + invoke.NewProposalProcessorHandler( &customEndorsementHandler{ transactor: &transactor, - next: NewEndorsementValidationHandler(), + next: invoke.NewEndorsementValidationHandler(), }, ), Request{ChaincodeID: "testCC", Fcn: "invoke", Args: [][]byte{[]byte("query"), []byte("b")}}, @@ -634,7 +635,7 @@ func setupChannelClientWithNodes(peers []fab.Peer, return ch } -func createAndSendTestTransactionProposal(sender fab.ProposalSender, chrequest *Request, targets []fab.ProposalProcessor) ([]*fab.TransactionProposalResponse, fab.TransactionID, error) { +func createAndSendTestTransactionProposal(sender fab.ProposalSender, chrequest *invoke.Request, targets []fab.ProposalProcessor) ([]*fab.TransactionProposalResponse, fab.TransactionID, error) { request := fab.ChaincodeInvokeRequest{ ChaincodeID: chrequest.ChaincodeID, Fcn: chrequest.Fcn, diff --git a/pkg/client/channel/invoke/api.go b/pkg/client/channel/invoke/api.go new file mode 100644 index 0000000000..a740ef4c60 --- /dev/null +++ b/pkg/client/channel/invoke/api.go @@ -0,0 +1,65 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +// Package invoke provides the handlers for performing chaincode invocations. +package invoke + +import ( + "time" + + "github.com/hyperledger/fabric-sdk-go/pkg/context/api/core" + "github.com/hyperledger/fabric-sdk-go/pkg/context/api/fab" + "github.com/hyperledger/fabric-sdk-go/pkg/errors/retry" + pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer" +) + +// Opts allows the user to specify more advanced options +type Opts struct { + ProposalProcessors []fab.ProposalProcessor // targets + Timeout time.Duration + Retry retry.Opts +} + +// Request contains the parameters to execute transaction +type Request struct { + ChaincodeID string + Fcn string + Args [][]byte + TransientMap map[string][]byte +} + +//Response contains response parameters for query and execute transaction +type Response struct { + Payload []byte + TransactionID fab.TransactionID + TxValidationCode pb.TxValidationCode + Proposal *fab.TransactionProposal + Responses []*fab.TransactionProposalResponse +} + +//Handler for chaining transaction executions +type Handler interface { + Handle(context *RequestContext, clientContext *ClientContext) +} + +//ClientContext contains context parameters for handler execution +type ClientContext struct { + CryptoSuite core.CryptoSuite + Discovery fab.DiscoveryService + Selection fab.SelectionService + Channel fab.Channel // TODO: this should be removed when we have MSP split out. + Transactor fab.Transactor + EventHub fab.EventHub +} + +//RequestContext contains request, opts, response parameters for handler execution +type RequestContext struct { + Request Request + Opts Opts + Response Response + Error error + RetryHandler retry.Handler +} diff --git a/pkg/client/channel/signature.go b/pkg/client/channel/invoke/signature.go similarity index 99% rename from pkg/client/channel/signature.go rename to pkg/client/channel/invoke/signature.go index 28deb6fb4c..05097a5a7f 100644 --- a/pkg/client/channel/signature.go +++ b/pkg/client/channel/invoke/signature.go @@ -4,7 +4,7 @@ Copyright SecureKey Technologies Inc. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package channel +package invoke import ( "github.com/hyperledger/fabric-sdk-go/pkg/errors/status" diff --git a/pkg/client/channel/signature_test.go b/pkg/client/channel/invoke/signature_test.go similarity index 99% rename from pkg/client/channel/signature_test.go rename to pkg/client/channel/invoke/signature_test.go index f5f3012129..e4df1b56d4 100644 --- a/pkg/client/channel/signature_test.go +++ b/pkg/client/channel/invoke/signature_test.go @@ -4,19 +4,19 @@ Copyright SecureKey Technologies Inc. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package channel +package invoke import ( "errors" "strings" "testing" - "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/msp" + "github.com/stretchr/testify/assert" + "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/msp" txnmocks "github.com/hyperledger/fabric-sdk-go/pkg/client/common/mocks" "github.com/hyperledger/fabric-sdk-go/pkg/context/api/fab" fcmocks "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" - "github.com/stretchr/testify/assert" ) func TestSignatureValidationHandlerSuccess(t *testing.T) { diff --git a/pkg/client/channel/txnhandler.go b/pkg/client/channel/invoke/txnhandler.go similarity index 98% rename from pkg/client/channel/txnhandler.go rename to pkg/client/channel/invoke/txnhandler.go index 078ad584c9..e16304a23d 100644 --- a/pkg/client/channel/txnhandler.go +++ b/pkg/client/channel/invoke/txnhandler.go @@ -4,22 +4,24 @@ Copyright SecureKey Technologies Inc. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package channel +package invoke import ( + "bytes" "time" - "bytes" + "github.com/pkg/errors" "github.com/hyperledger/fabric-sdk-go/pkg/context/api/fab" "github.com/hyperledger/fabric-sdk-go/pkg/errors/status" "github.com/hyperledger/fabric-sdk-go/pkg/fab/peer" "github.com/hyperledger/fabric-sdk-go/pkg/fab/txn" - + "github.com/hyperledger/fabric-sdk-go/pkg/logging" "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/common" - "github.com/pkg/errors" ) +var logger = logging.NewLogger("fabric_sdk_go") + //EndorsementHandler for handling endorse transactions type EndorsementHandler struct { next Handler diff --git a/pkg/client/channel/txnhandler_test.go b/pkg/client/channel/invoke/txnhandler_test.go similarity index 92% rename from pkg/client/channel/txnhandler_test.go rename to pkg/client/channel/invoke/txnhandler_test.go index e1d6426a49..d2c52f48ea 100644 --- a/pkg/client/channel/txnhandler_test.go +++ b/pkg/client/channel/invoke/txnhandler_test.go @@ -4,22 +4,21 @@ Copyright SecureKey Technologies Inc. All Rights Reserved. SPDX-License-Identifier: Apache-2.0 */ -package channel +package invoke import ( + "strings" "testing" "time" - "github.com/hyperledger/fabric-sdk-go/pkg/context/api/fab" - "github.com/hyperledger/fabric-sdk-go/pkg/fab/channel" "github.com/pkg/errors" "github.com/stretchr/testify/assert" - "strings" - "github.com/hyperledger/fabric-sdk-go/internal/github.com/hyperledger/fabric/msp" - txnmocks "github.com/hyperledger/fabric-sdk-go/pkg/client/common/mocks" + "github.com/hyperledger/fabric-sdk-go/pkg/context" + "github.com/hyperledger/fabric-sdk-go/pkg/context/api/fab" + "github.com/hyperledger/fabric-sdk-go/pkg/fab/channel" fcmocks "github.com/hyperledger/fabric-sdk-go/pkg/fab/mocks" ) @@ -269,3 +268,29 @@ func setupChannelClientContext(discErr error, selectionErr error, peers []fab.Pe } } + +func setupTestContext() context.Context { + user := fcmocks.NewMockUser("test") + ctx := fcmocks.NewMockContext(user) + return ctx +} + +func setupTestDiscovery(discErr error, peers []fab.Peer) (fab.DiscoveryService, error) { + + mockDiscovery, err := txnmocks.NewMockDiscoveryProvider(discErr, peers) + if err != nil { + return nil, errors.WithMessage(err, "NewMockDiscoveryProvider failed") + } + + return mockDiscovery.NewDiscoveryService("mychannel") +} + +func setupTestSelection(discErr error, peers []fab.Peer) (*txnmocks.MockSelectionService, error) { + + mockSelection, err := txnmocks.NewMockSelectionProvider(discErr, peers) + if err != nil { + return nil, errors.WithMessage(err, "NewMockSelectinProvider failed") + } + + return mockSelection.NewSelectionService("mychannel") +} diff --git a/test/integration/sdk/channel_client_test.go b/test/integration/sdk/channel_client_test.go index 806396c6f9..0b3b101a31 100644 --- a/test/integration/sdk/channel_client_test.go +++ b/test/integration/sdk/channel_client_test.go @@ -14,6 +14,7 @@ import ( pb "github.com/hyperledger/fabric-sdk-go/third_party/github.com/hyperledger/fabric/protos/peer" "github.com/hyperledger/fabric-sdk-go/pkg/client/channel" + "github.com/hyperledger/fabric-sdk-go/pkg/client/channel/invoke" "github.com/hyperledger/fabric-sdk-go/pkg/context/api/fab" "github.com/hyperledger/fabric-sdk-go/pkg/core/config" "github.com/hyperledger/fabric-sdk-go/pkg/fabsdk" @@ -153,10 +154,10 @@ type testHandler struct { txID *string endorser *string txValidationCode *pb.TxValidationCode - next channel.Handler + next invoke.Handler } -func (h *testHandler) Handle(requestContext *channel.RequestContext, clientContext *channel.ClientContext) { +func (h *testHandler) Handle(requestContext *invoke.RequestContext, clientContext *invoke.ClientContext) { if h.txID != nil { *h.txID = string(requestContext.Response.TransactionID) h.t.Logf("Custom handler writing TxID [%s]", *h.txID) @@ -185,14 +186,14 @@ func testInvokeHandler(ccID string, chClient *channel.Client, t *testing.T) { txValidationCode := pb.TxValidationCode(-1) response, err := chClient.InvokeHandler( - channel.NewProposalProcessorHandler( - channel.NewEndorsementHandler( - channel.NewEndorsementValidationHandler( + invoke.NewProposalProcessorHandler( + invoke.NewEndorsementHandler( + invoke.NewEndorsementValidationHandler( &testHandler{ t: t, txID: &txID, endorser: &endorser, - next: channel.NewCommitHandler( + next: invoke.NewCommitHandler( &testHandler{ t: t, txValidationCode: &txValidationCode,