github.com/s7techlab/cckit@v0.10.5/gateway/chaincode_instance_event_service.go (about)

     1  package gateway
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/golang/protobuf/ptypes/timestamp"
     8  	"github.com/hyperledger/fabric-protos-go/peer"
     9  	"go.uber.org/zap"
    10  
    11  	"github.com/s7techlab/cckit/router"
    12  	"github.com/s7techlab/cckit/sdk"
    13  )
    14  
    15  type (
    16  	ChaincodeInstanceEventService struct {
    17  		EventDelivery sdk.EventDelivery
    18  		Locator       *ChaincodeLocator
    19  		Opts          *Opts
    20  		Logger        *zap.Logger
    21  	}
    22  
    23  	ChaincodeInstanceEvents interface {
    24  		ChaincodeInstanceEventsServiceServer
    25  
    26  		EventsChan(ctx context.Context, rr ...*ChaincodeInstanceEventsStreamRequest) (
    27  			_ chan *ChaincodeEvent, closer func() error, _ error)
    28  	}
    29  )
    30  
    31  var _ ChaincodeInstanceEventsServiceServer = &ChaincodeInstanceEventService{}
    32  
    33  func NewChaincodeInstanceEventService(delivery sdk.EventDelivery, channel, chaincode string, opts ...Opt) *ChaincodeInstanceEventService {
    34  	cis := &ChaincodeInstanceEventService{
    35  		EventDelivery: delivery,
    36  		Locator: &ChaincodeLocator{
    37  			Channel:   channel,
    38  			Chaincode: chaincode,
    39  		},
    40  		Opts:   &Opts{},
    41  		Logger: zap.NewNop(),
    42  	}
    43  
    44  	for _, o := range opts {
    45  		o(cis.Opts)
    46  	}
    47  
    48  	return cis
    49  }
    50  
    51  func (ces *ChaincodeInstanceEventService) ServiceDef() ServiceDef {
    52  	return ServiceDef{
    53  		Desc:                        &_ChaincodeInstanceEventsService_serviceDesc,
    54  		Service:                     ces,
    55  		HandlerFromEndpointRegister: RegisterChaincodeInstanceEventsServiceHandlerFromEndpoint,
    56  	}
    57  }
    58  
    59  func (ces *ChaincodeInstanceEventService) EventsStream(
    60  	req *ChaincodeInstanceEventsStreamRequest, stream ChaincodeInstanceEventsService_EventsStreamServer) error {
    61  	if err := router.ValidateRequest(req); err != nil {
    62  		return err
    63  	}
    64  
    65  	ctx := stream.Context()
    66  	for _, c := range ces.Opts.Context {
    67  		ctx = c(ctx)
    68  	}
    69  
    70  	signer, _ := SignerFromContext(stream.Context())
    71  	events, closer, err := ces.EventDelivery.Events(
    72  		stream.Context(),
    73  		ces.Locator.Channel,
    74  		ces.Locator.Chaincode,
    75  		signer,
    76  		BlockRange(req.FromBlock, req.ToBlock)...,
    77  	)
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	for {
    83  		select {
    84  
    85  		case <-stream.Context().Done():
    86  			return closer()
    87  
    88  		case e, ok := <-events:
    89  			if !ok {
    90  				return nil
    91  			}
    92  
    93  			processedEvent, err := ProcessEvent(e, ces.Opts.Event, req.EventName)
    94  			if err != nil {
    95  				ces.Logger.Warn(`event processing`, zap.Error(err))
    96  			}
    97  
    98  			if processedEvent != nil {
    99  				if err = stream.Send(processedEvent); err != nil {
   100  					return err
   101  				}
   102  			}
   103  		}
   104  	}
   105  }
   106  
   107  func (ces *ChaincodeInstanceEventService) Events(ctx context.Context, req *ChaincodeInstanceEventsRequest) (*ChaincodeEvents, error) {
   108  	if err := router.ValidateRequest(req); err != nil {
   109  		return nil, err
   110  	}
   111  
   112  	// for event _list_ default block range is up to current channel height
   113  	if req.ToBlock == nil {
   114  		req.ToBlock = &BlockLimit{Num: 0}
   115  	}
   116  
   117  	for _, c := range ces.Opts.Context {
   118  		ctx = c(ctx)
   119  	}
   120  	signer, _ := SignerFromContext(ctx)
   121  	eventStream, closer, err := ces.EventDelivery.Events(
   122  		ctx,
   123  		ces.Locator.Channel,
   124  		ces.Locator.Chaincode,
   125  		signer,
   126  		BlockRange(req.FromBlock, req.ToBlock)...,
   127  	)
   128  
   129  	if err != nil {
   130  		return nil, err
   131  	}
   132  
   133  	events := &ChaincodeEvents{
   134  		Locator:   ces.Locator,
   135  		FromBlock: req.FromBlock,
   136  		ToBlock:   req.ToBlock,
   137  		Items:     []*ChaincodeEvent{},
   138  	}
   139  
   140  	// timeout for receiving events
   141  	ticker := time.NewTicker(EventListStreamTimeout)
   142  	defer ticker.Stop()
   143  
   144  	for {
   145  		select {
   146  
   147  		case <-ctx.Done():
   148  			_ = closer()
   149  			return events, nil
   150  
   151  		case <-ticker.C:
   152  			_ = closer()
   153  			// return values if events are not streamed
   154  			return events, nil
   155  
   156  		case e, hasMore := <-eventStream:
   157  			if !hasMore {
   158  				_ = closer()
   159  				return events, nil
   160  			}
   161  
   162  			processedEvent, err := ProcessEvent(e, ces.Opts.Event, req.EventName)
   163  			if err != nil {
   164  				ces.Logger.Warn(`event processing`, zap.Error(err))
   165  			}
   166  
   167  			if processedEvent != nil {
   168  				events.Items = append(events.Items, processedEvent)
   169  			}
   170  
   171  			ticker.Reset(EventListStreamTimeout)
   172  		}
   173  	}
   174  }
   175  
   176  // EventsChan is not part of ChaincodeInstanceEventService interface, for calls as component
   177  func (ces *ChaincodeInstanceEventService) EventsChan(ctx context.Context, rr ...*ChaincodeInstanceEventsStreamRequest) (_ chan *ChaincodeEvent, closer func() error, _ error) {
   178  	req := &ChaincodeInstanceEventsStreamRequest{}
   179  	if len(rr) == 1 {
   180  		req = rr[0]
   181  		if err := router.ValidateRequest(req); err != nil {
   182  			return nil, nil, err
   183  		}
   184  	}
   185  
   186  	for _, c := range ces.Opts.Context {
   187  		ctx = c(ctx)
   188  	}
   189  	signer, _ := SignerFromContext(ctx)
   190  	events, deliveryCloser, err := ces.EventDelivery.Events(
   191  		ctx,
   192  		ces.Locator.Channel,
   193  		ces.Locator.Chaincode,
   194  		signer,
   195  		BlockRange(req.FromBlock, req.ToBlock)...,
   196  	)
   197  
   198  	if err != nil {
   199  		return nil, nil, err
   200  	}
   201  
   202  	eventsProcessed := make(chan *ChaincodeEvent)
   203  
   204  	closer = func() error {
   205  		return deliveryCloser()
   206  	}
   207  
   208  	go func() {
   209  		for e := range events {
   210  			eventProcessed, err := ProcessEvent(e, ces.Opts.Event, req.EventName)
   211  
   212  			if err != nil {
   213  				ces.Logger.Warn(`event processing`, zap.Error(err))
   214  			}
   215  			if eventProcessed != nil {
   216  				eventsProcessed <- eventProcessed
   217  			}
   218  		}
   219  		close(eventsProcessed)
   220  	}()
   221  
   222  	return eventsProcessed, closer, nil
   223  }
   224  
   225  func ProcessEvent(event interface {
   226  	Event() *peer.ChaincodeEvent
   227  	Block() uint64
   228  	TxTimestamp() *timestamp.Timestamp
   229  }, opts []EventOpt, matchName []string) (*ChaincodeEvent, error) {
   230  
   231  	processedEvent := &ChaincodeEvent{
   232  		Event:       event.Event(),
   233  		Block:       event.Block(),
   234  		TxTimestamp: event.TxTimestamp(),
   235  	}
   236  	for _, o := range opts {
   237  		if err := o(processedEvent); err != nil {
   238  			return processedEvent, err
   239  		}
   240  	}
   241  
   242  	if !MatchEventName(event.Event().EventName, matchName) {
   243  		return nil, nil
   244  	}
   245  
   246  	return processedEvent, nil
   247  }
   248  
   249  func BlockRange(from, to *BlockLimit) []int64 {
   250  	var blockRange []int64
   251  
   252  	switch {
   253  	case from != nil && to != nil:
   254  		blockRange = []int64{from.Num, to.Num}
   255  
   256  	case from != nil:
   257  		blockRange = []int64{from.Num}
   258  
   259  	case to != nil:
   260  		blockRange = []int64{0, to.Num}
   261  	}
   262  
   263  	return blockRange
   264  }
   265  
   266  func MatchEventName(eventName string, expectedNames []string) bool {
   267  	if len(expectedNames) == 0 {
   268  		return true
   269  	}
   270  
   271  	for _, expectedName := range expectedNames {
   272  		if eventName == expectedName {
   273  			return true
   274  		}
   275  	}
   276  
   277  	return false
   278  }