github.com/MontFerret/ferret@v0.18.0/pkg/runtime/expressions/waitfor_event.go (about)

     1  package expressions
     2  
     3  import (
     4  	"context"
     5  	"time"
     6  
     7  	"github.com/pkg/errors"
     8  
     9  	"github.com/MontFerret/ferret/pkg/runtime/collections"
    10  	"github.com/MontFerret/ferret/pkg/runtime/core"
    11  	"github.com/MontFerret/ferret/pkg/runtime/events"
    12  	"github.com/MontFerret/ferret/pkg/runtime/expressions/clauses"
    13  	"github.com/MontFerret/ferret/pkg/runtime/expressions/literals"
    14  	"github.com/MontFerret/ferret/pkg/runtime/values"
    15  	"github.com/MontFerret/ferret/pkg/runtime/values/types"
    16  )
    17  
    18  const DefaultWaitTimeout = 5000
    19  
    20  type WaitForEventExpression struct {
    21  	src            core.SourceMap
    22  	eventName      core.Expression
    23  	eventSource    core.Expression
    24  	options        core.Expression
    25  	timeout        core.Expression
    26  	filterSrc      core.SourceMap
    27  	filter         core.Expression
    28  	filterVariable string
    29  }
    30  
    31  func NewWaitForEventExpression(
    32  	src core.SourceMap,
    33  	eventName core.Expression,
    34  	eventSource core.Expression,
    35  ) (*WaitForEventExpression, error) {
    36  	if eventName == nil {
    37  		return nil, core.Error(core.ErrInvalidArgument, "event name")
    38  	}
    39  
    40  	if eventSource == nil {
    41  		return nil, core.Error(core.ErrMissedArgument, "event source")
    42  	}
    43  
    44  	return &WaitForEventExpression{
    45  		src:         src,
    46  		eventName:   eventName,
    47  		eventSource: eventSource,
    48  		timeout:     literals.NewIntLiteral(DefaultWaitTimeout),
    49  	}, nil
    50  }
    51  
    52  func (e *WaitForEventExpression) SetOptions(options core.Expression) error {
    53  	if options == nil {
    54  		return core.ErrInvalidArgument
    55  	}
    56  
    57  	e.options = options
    58  
    59  	return nil
    60  }
    61  
    62  func (e *WaitForEventExpression) SetTimeout(timeout core.Expression) error {
    63  	if timeout == nil {
    64  		return core.ErrInvalidArgument
    65  	}
    66  
    67  	e.timeout = timeout
    68  
    69  	return nil
    70  }
    71  
    72  func (e *WaitForEventExpression) SetFilter(src core.SourceMap, variable string, exp core.Expression) error {
    73  	if variable == "" {
    74  		return core.ErrInvalidArgument
    75  	}
    76  
    77  	if exp == nil {
    78  		return core.ErrInvalidArgument
    79  	}
    80  
    81  	e.filterSrc = src
    82  	e.filterVariable = variable
    83  	e.filter = exp
    84  
    85  	return nil
    86  }
    87  
    88  func (e *WaitForEventExpression) Exec(ctx context.Context, scope *core.Scope) (core.Value, error) {
    89  	eventName, err := e.getEventName(ctx, scope)
    90  
    91  	if err != nil {
    92  		return values.None, core.SourceError(e.src, errors.Wrap(err, "unable to calculate event name"))
    93  	}
    94  
    95  	eventSource, err := e.eventSource.Exec(ctx, scope)
    96  
    97  	if err != nil {
    98  		return values.None, core.SourceError(e.src, err)
    99  	}
   100  
   101  	observable, ok := eventSource.(events.Observable)
   102  
   103  	if !ok {
   104  		return values.None, core.TypeError(eventSource.Type(), core.NewType("Observable"))
   105  	}
   106  
   107  	opts, err := e.getOptions(ctx, scope)
   108  
   109  	if err != nil {
   110  		return values.None, core.SourceError(e.src, err)
   111  	}
   112  
   113  	timeout, err := e.getTimeout(ctx, scope)
   114  
   115  	if err != nil {
   116  		return values.None, core.SourceError(e.src, errors.Wrap(err, "failed to calculate timeout value"))
   117  	}
   118  
   119  	subscription := events.Subscription{
   120  		EventName: eventName,
   121  		Options:   opts,
   122  	}
   123  
   124  	ctx, cancel := context.WithTimeout(ctx, timeout)
   125  	defer cancel()
   126  
   127  	var val core.Value
   128  
   129  	if e.filter == nil {
   130  		val, err = e.consumeFirst(ctx, observable, subscription)
   131  	} else {
   132  		val, err = e.consumeFiltered(ctx, scope, observable, subscription)
   133  	}
   134  
   135  	if err != nil {
   136  		return nil, core.SourceError(e.src, err)
   137  	}
   138  
   139  	return val, nil
   140  }
   141  
   142  func (e *WaitForEventExpression) consumeFirst(ctx context.Context, observable events.Observable, subscription events.Subscription) (core.Value, error) {
   143  	stream, err := observable.Subscribe(ctx, subscription)
   144  
   145  	if err != nil {
   146  		return values.None, err
   147  	}
   148  
   149  	defer stream.Close(ctx)
   150  
   151  	select {
   152  	case evt := <-stream.Read(ctx):
   153  		if err := evt.Err(); err != nil {
   154  			return values.None, err
   155  		}
   156  
   157  		return evt.Value(), nil
   158  	case <-ctx.Done():
   159  		return values.None, ctx.Err()
   160  	}
   161  }
   162  
   163  func (e *WaitForEventExpression) consumeFiltered(ctx context.Context, scope *core.Scope, observable events.Observable, subscription events.Subscription) (core.Value, error) {
   164  	stream, err := observable.Subscribe(ctx, subscription)
   165  
   166  	if err != nil {
   167  		return values.None, err
   168  	}
   169  
   170  	defer stream.Close(ctx)
   171  
   172  	iterable, err := clauses.NewFilterClause(
   173  		e.filterSrc,
   174  		collections.AsIterable(func(c context.Context, scope *core.Scope) (collections.Iterator, error) {
   175  			return collections.FromCoreIterator(e.filterVariable, "", events.NewIterator(stream.Read(c)))
   176  		}),
   177  		e.filter,
   178  	)
   179  
   180  	if err != nil {
   181  		return values.None, err
   182  	}
   183  
   184  	iter, err := iterable.Iterate(ctx, scope)
   185  
   186  	if err != nil {
   187  		return values.None, err
   188  	}
   189  
   190  	out, err := iter.Next(ctx, scope)
   191  
   192  	if err != nil {
   193  		return values.None, err
   194  	}
   195  
   196  	return out.GetVariable(e.filterVariable)
   197  }
   198  
   199  func (e *WaitForEventExpression) getEventName(ctx context.Context, scope *core.Scope) (string, error) {
   200  	eventName, err := e.eventName.Exec(ctx, scope)
   201  
   202  	if err != nil {
   203  		return "", err
   204  	}
   205  
   206  	return eventName.String(), nil
   207  }
   208  
   209  func (e *WaitForEventExpression) getOptions(ctx context.Context, scope *core.Scope) (*values.Object, error) {
   210  	if e.options == nil {
   211  		return nil, nil
   212  	}
   213  
   214  	options, err := e.options.Exec(ctx, scope)
   215  
   216  	if err != nil {
   217  		return nil, err
   218  	}
   219  
   220  	if err := core.ValidateType(options, types.Object); err != nil {
   221  		return nil, err
   222  	}
   223  
   224  	return options.(*values.Object), nil
   225  }
   226  
   227  func (e *WaitForEventExpression) getTimeout(ctx context.Context, scope *core.Scope) (time.Duration, error) {
   228  	timeoutValue, err := e.timeout.Exec(ctx, scope)
   229  
   230  	if err != nil {
   231  		return 0, err
   232  	}
   233  
   234  	timeoutInt := values.ToIntDefault(timeoutValue, DefaultWaitTimeout)
   235  
   236  	return time.Duration(timeoutInt) * time.Millisecond, nil
   237  }