github.com/cs3org/reva/v2@v2.27.7/pkg/events/events.go (about) 1 // Copyright 2018-2021 CERN 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 // 15 // In applying this license, CERN does not waive the privileges and immunities 16 // granted to it by virtue of its status as an Intergovernmental Organization 17 // or submit itself to any jurisdiction. 18 19 package events 20 21 import ( 22 "context" 23 "log" 24 "reflect" 25 26 ctxpkg "github.com/cs3org/reva/v2/pkg/ctx" 27 "github.com/google/uuid" 28 "go-micro.dev/v4/events" 29 "go.opentelemetry.io/otel/propagation" 30 ) 31 32 var ( 33 // MainQueueName is the name of the main queue 34 // All events will go through here as they are forwarded to the consumer via the 35 // group name 36 // TODO: "fan-out" so not all events go through the same queue? requires investigation 37 MainQueueName = "main-queue" 38 39 // MetadatakeyEventType is the key used for the eventtype in the metadata map of the event 40 MetadatakeyEventType = "eventtype" 41 42 // MetadatakeyEventID is the key used for the eventID in the metadata map of the event 43 MetadatakeyEventID = "eventid" 44 45 // MetadatakeyTraceParent is the key used for the traceparent in the metadata map of the event 46 MetadatakeyTraceParent = "traceparent" 47 48 // MetadatakeyInitiatorID is the key used for the initiator id in the metadata map of the event 49 MetadatakeyInitiatorID = "initiatorid" 50 ) 51 52 type ( 53 // Unmarshaller is the interface events need to fulfill 54 Unmarshaller interface { 55 Unmarshal([]byte) (interface{}, error) 56 } 57 58 // Publisher is the interface publishers need to fulfill 59 Publisher interface { 60 Publish(string, interface{}, ...events.PublishOption) error 61 } 62 63 // Consumer is the interface consumer need to fulfill 64 Consumer interface { 65 Consume(string, ...events.ConsumeOption) (<-chan events.Event, error) 66 } 67 68 // Stream is the interface common to Publisher and Consumer 69 Stream interface { 70 Publish(string, interface{}, ...events.PublishOption) error 71 Consume(string, ...events.ConsumeOption) (<-chan events.Event, error) 72 } 73 74 // Event is the envelope for events 75 Event struct { 76 Type string 77 ID string 78 TraceParent string 79 InitiatorID string 80 Event interface{} 81 } 82 ) 83 84 // Consume returns a channel that will get all events that match the given evs 85 // group defines the service type: One group will get exactly one copy of a event that is emitted 86 // NOTE: uses reflect on initialization 87 func Consume(s Consumer, group string, evs ...Unmarshaller) (<-chan Event, error) { 88 c, err := s.Consume(MainQueueName, events.WithGroup(group)) 89 if err != nil { 90 return nil, err 91 } 92 93 registeredEvents := map[string]Unmarshaller{} 94 for _, e := range evs { 95 typ := reflect.TypeOf(e) 96 registeredEvents[typ.String()] = e 97 } 98 99 outchan := make(chan Event) 100 go func() { 101 for { 102 e := <-c 103 et := e.Metadata[MetadatakeyEventType] 104 ev, ok := registeredEvents[et] 105 if !ok { 106 continue 107 } 108 109 event, err := ev.Unmarshal(e.Payload) 110 if err != nil { 111 log.Printf("can't unmarshal event %v", err) 112 continue 113 } 114 115 outchan <- Event{ 116 Type: et, 117 ID: e.Metadata[MetadatakeyEventID], 118 TraceParent: e.Metadata[MetadatakeyTraceParent], 119 InitiatorID: e.Metadata[MetadatakeyInitiatorID], 120 Event: event, 121 } 122 } 123 }() 124 return outchan, nil 125 } 126 127 // ConsumeAll allows consuming all events. Note that unmarshalling must be done manually in this case, therefore Event.Event will always be of type []byte 128 func ConsumeAll(s Consumer, group string) (<-chan Event, error) { 129 c, err := s.Consume(MainQueueName, events.WithGroup(group)) 130 if err != nil { 131 return nil, err 132 } 133 134 outchan := make(chan Event) 135 go func() { 136 for { 137 e := <-c 138 outchan <- Event{ 139 Type: e.Metadata[MetadatakeyEventType], 140 ID: e.Metadata[MetadatakeyEventID], 141 TraceParent: e.Metadata[MetadatakeyTraceParent], 142 InitiatorID: e.Metadata[MetadatakeyInitiatorID], 143 Event: e.Payload, 144 } 145 } 146 }() 147 return outchan, nil 148 } 149 150 // Publish publishes the ev to the MainQueue from where it is distributed to all subscribers 151 // NOTE: needs to use reflect on runtime 152 func Publish(ctx context.Context, s Publisher, ev interface{}) error { 153 evName := reflect.TypeOf(ev).String() 154 traceParent := getTraceParentFromCtx(ctx) 155 iid, _ := ctxpkg.ContextGetInitiator(ctx) 156 return s.Publish(MainQueueName, ev, events.WithMetadata(map[string]string{ 157 MetadatakeyEventType: evName, 158 MetadatakeyEventID: uuid.New().String(), 159 MetadatakeyTraceParent: traceParent, 160 MetadatakeyInitiatorID: iid, 161 })) 162 } 163 164 // GetTraceContext extracts the trace context from the event and injects it into the given 165 // context. 166 func (e *Event) GetTraceContext(ctx context.Context) context.Context { 167 return propagation.TraceContext{}.Extract(ctx, propagation.MapCarrier{ 168 "traceparent": e.TraceParent, 169 }) 170 } 171 172 // getTraceParentFromCtx will return a traceparent from the context if it exists. 173 // it will be a string as specificied here: https://www.w3.org/TR/trace-context/ 174 // If no trace info in the context, the return will be an empty string 175 func getTraceParentFromCtx(ctx context.Context) string { 176 mc := propagation.MapCarrier{} 177 tc := propagation.TraceContext{} 178 tc.Inject(ctx, &mc) 179 return mc["traceparent"] 180 }