github.com/argoproj/argo-events@v1.9.1/eventbus/kafka/sensor/trigger_handler.go (about)

     1  package kafka
     2  
     3  import (
     4  	"time"
     5  
     6  	"github.com/Knetic/govaluate"
     7  	"github.com/argoproj/argo-events/eventbus/common"
     8  	"github.com/argoproj/argo-events/eventbus/kafka/base"
     9  	cloudevents "github.com/cloudevents/sdk-go/v2"
    10  	"go.uber.org/zap"
    11  )
    12  
    13  type KafkaTriggerHandler interface {
    14  	common.TriggerConnection
    15  	Name() string
    16  	Ready() bool
    17  	Reset()
    18  	OneAndDone() bool
    19  	DependsOn(*cloudevents.Event) (string, bool)
    20  	Transform(string, *cloudevents.Event) (*cloudevents.Event, error)
    21  	Filter(string, *cloudevents.Event) bool
    22  	Update(event *cloudevents.Event, partition int32, offset int64, timestamp time.Time) ([]*cloudevents.Event, error)
    23  	Offset(int32, int64) int64
    24  	Action([]*cloudevents.Event) func()
    25  }
    26  
    27  func (c *KafkaTriggerConnection) Name() string {
    28  	return c.triggerName
    29  }
    30  
    31  func (c *KafkaTriggerConnection) Ready() bool {
    32  	// cannot process events until the subscribe function has been
    33  	// called, which is when these functions are set
    34  	return c.transform != nil && c.filter != nil && c.action != nil
    35  }
    36  
    37  func (c *KafkaTriggerConnection) DependsOn(event *cloudevents.Event) (string, bool) {
    38  	if dep, ok := c.dependencies[base.EventKey(event.Source(), event.Subject())]; ok {
    39  		return dep.Name, true
    40  	}
    41  
    42  	return "", false
    43  }
    44  
    45  func (c *KafkaTriggerConnection) OneAndDone() bool {
    46  	for _, token := range c.depExpression.Tokens() {
    47  		if token.Kind == govaluate.LOGICALOP && token.Value == "&&" {
    48  			return false
    49  		}
    50  	}
    51  
    52  	return true
    53  }
    54  
    55  func (c *KafkaTriggerConnection) Transform(depName string, event *cloudevents.Event) (*cloudevents.Event, error) {
    56  	return c.transform(depName, *event)
    57  }
    58  
    59  func (c *KafkaTriggerConnection) Filter(depName string, event *cloudevents.Event) bool {
    60  	return c.filter(depName, *event)
    61  }
    62  
    63  func (c *KafkaTriggerConnection) Update(event *cloudevents.Event, partition int32, offset int64, timestamp time.Time) ([]*cloudevents.Event, error) {
    64  	eventWithMetadata := &eventWithMetadata{
    65  		Event:     event,
    66  		partition: partition,
    67  		offset:    offset,
    68  		timestamp: timestamp,
    69  	}
    70  
    71  	// remove previous events with same source and subject and remove
    72  	// all events older than last condition reset time
    73  	i := 0
    74  	for _, event := range c.events {
    75  		if !event.Same(eventWithMetadata) && event.After(c.lastResetTime) {
    76  			c.events[i] = event
    77  			i++
    78  		}
    79  	}
    80  	for j := i; j < len(c.events); j++ {
    81  		c.events[j] = nil // avoid memory leak
    82  	}
    83  	c.events = append(c.events[:i], eventWithMetadata)
    84  
    85  	satisfied, err := c.satisfied()
    86  	if err != nil {
    87  		return nil, err
    88  	}
    89  
    90  	// if satisfied, publish a message to the action topic containing
    91  	// all events and reset the trigger
    92  	var events []*cloudevents.Event
    93  	if satisfied == true {
    94  		defer c.Reset()
    95  		for _, event := range c.events {
    96  			events = append(events, event.Event)
    97  		}
    98  	}
    99  
   100  	return events, nil
   101  }
   102  
   103  func (c *KafkaTriggerConnection) Offset(partition int32, offset int64) int64 {
   104  	for _, event := range c.events {
   105  		if partition == event.partition && offset > event.offset {
   106  			offset = event.offset
   107  		}
   108  	}
   109  
   110  	return offset
   111  }
   112  
   113  func (c *KafkaTriggerConnection) Action(events []*cloudevents.Event) func() {
   114  	eventMap := map[string]cloudevents.Event{}
   115  	for _, event := range events {
   116  		if depName, ok := c.DependsOn(event); ok {
   117  			eventMap[depName] = *event
   118  		}
   119  	}
   120  
   121  	// If at least once is specified, we must call the action
   122  	// function before committing a transaction, otherwise the
   123  	// function must be called after. To call after we return a
   124  	// function.
   125  	var f func()
   126  	if c.atLeastOnce {
   127  		c.action(eventMap)
   128  	} else {
   129  		f = func() { c.action(eventMap) }
   130  	}
   131  
   132  	return f
   133  }
   134  
   135  func (c *KafkaTriggerConnection) satisfied() (interface{}, error) {
   136  	parameters := Parameters{}
   137  	for _, event := range c.events {
   138  		if depName, ok := c.DependsOn(event.Event); ok {
   139  			parameters[depName] = true
   140  		}
   141  	}
   142  
   143  	c.Logger.Infow("Evaluating", zap.String("expr", c.depExpression.String()), zap.Any("parameters", parameters))
   144  
   145  	return c.depExpression.Eval(parameters)
   146  }
   147  
   148  func (c *KafkaTriggerConnection) Reset() {
   149  	c.events = nil
   150  }
   151  
   152  type Parameters map[string]bool
   153  
   154  func (p Parameters) Get(name string) (interface{}, error) {
   155  	return p[name], nil
   156  }