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  }