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 }