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 }