public static async Task <bool> ShouldRetryAsync(this RetryPolicy retryPolicy, StateMachineContext context, int attempts, TimeSpan elapsedDelay) { context.CheckArgNull(nameof(context)); if (retryPolicy == null) { return(false); } else if (attempts >= retryPolicy.MaxAttempts) { return(false); } else if (retryPolicy.Delay != null) { var delay = attempts == 1 ? retryPolicy.Delay.Value : elapsedDelay; if (retryPolicy.Increment != null) { delay += retryPolicy.Increment.Value; } else if (retryPolicy.Multiplier != null) { delay *= retryPolicy.Multiplier.Value; }
public static async Task <JToken> ExecuteAsync(this SendEventAction action, StateMachineContext context, JToken input) { action.CheckArgNull(nameof(action)); context.CheckArgNull(nameof(context)); input.CheckArgNull(nameof(input)); var eventDefinition = context.Workflow.Events.SingleOrDefault(ev => ev.Name.IsEqualTo(action.Event)); if (eventDefinition == null) { throw new InvalidOperationException("Unable to resolve event definition: " + action.Event); } JToken payload = action.Expression?.EvalExpr(input, context) ?? new JObject(); var evt = context.Host.CreateEventInstance(eventDefinition.Name, eventDefinition.Type, eventDefinition.Source, payload, action.ContextAttributes); Debug.Assert(evt != null); await context.Host.SendEventsAsync(new[] { evt }, context.CancelToken); return(JValue.CreateNull()); }
public static async Task <JToken> ExecuteAsync(this SequenceAction action, StateMachineContext context, JToken input) { action.CheckArgNull(nameof(action)); context.CheckArgNull(nameof(context)); input.CheckArgNull(nameof(input)); var output = new JObject(); for (var idx = 0; idx < action.Actions.Count; idx++) { var childAction = action.Actions.ElementAt(idx); Debug.Assert(childAction != null); var id = string.IsNullOrWhiteSpace(childAction.Name) ? idx.ToString() : childAction.Name; Debug.Assert(!string.IsNullOrWhiteSpace(id)); output[id] = await childAction.ExecuteAsync(context, input); } return(output); }
public static async Task <JToken> ExecuteAsync(this ParallelAction action, StateMachineContext context, JToken input) { action.CheckArgNull(nameof(action)); context.CheckArgNull(nameof(context)); input.CheckArgNull(nameof(input)); var output = new JObject(); var tasks = action.Actions.Select(async(a, idx) => { var item = await a.ExecuteAsync(context, input.DeepClone()); Debug.Assert(item != null); var id = string.IsNullOrWhiteSpace(a.Name) ? idx.ToString() : a.Name; return(id, item); }).ToList(); if (action.CompletionType == ParallelCompletionType.And) { var results = await Task.WhenAll(tasks); Array.ForEach(results, tuple => output[tuple.id] = tuple.item); } else if (action.CompletionType == ParallelCompletionType.Xor) { var resultTask = await Task.WhenAny(tasks); var tuple = await resultTask; output[tuple.id] = tuple.item; } else { Debug.Assert(action.CompletionType == ParallelCompletionType.N_of_M); Debug.Assert(action.N > 0); var resultCount = 0; while (resultCount < action.N && resultCount < tasks.Count) { var resultTask = await Task.WhenAny(tasks); tasks.Remove(resultTask); var tuple = await resultTask; output[tuple.id] = tuple.item; resultCount++; } } return(output); }
public static Task <JToken> ExecuteAsync(this InjectDataAction action, StateMachineContext context, JToken input) { action.CheckArgNull(nameof(action)); context.CheckArgNull(nameof(context)); input.CheckArgNull(nameof(input)); return(Task.FromResult(action.Expression.EvalExpr(input, context) ?? JValue.CreateNull())); }
public static async Task <JToken> ExecuteAsync(this DelayAction action, StateMachineContext context, JToken input) { action.CheckArgNull(nameof(action)); context.CheckArgNull(nameof(context)); input.CheckArgNull(nameof(input)); await context.Host.DelayAsync(action.Timeout, context.CancelToken); return(JValue.CreateNull()); }
public static async Task <JToken> ExecuteAsync(this InvokeSubflowAction action, StateMachineContext context, JToken input) { action.CheckArgNull(nameof(action)); context.CheckArgNull(nameof(context)); input.CheckArgNull(nameof(input)); Func <CancellationToken, Task <JToken> > invokeTask = async token => { var jobj = await context.Host.ExecuteSubflowAsync(action.SubflowName, input, token, action.WaitForCompletion); Debug.Assert(jobj != null); return(jobj); }; JToken output; if (action.Timeout != null) { using var localTimeoutCancelTokenSource = new CancellationTokenSource(); using var combined = CancellationTokenSource.CreateLinkedTokenSource( localTimeoutCancelTokenSource.Token, context.CancelToken); Task <JToken> timeoutTask = context.Host.DelayAsync(action.Timeout.Value, combined.Token) .ContinueWith(_ => { return((JToken)JValue.CreateNull()); }); Debug.Assert(timeoutTask != null); output = await Task.WhenAny(timeoutTask, invokeTask(combined.Token)).Unwrap(); if (!timeoutTask.IsCompleted) { localTimeoutCancelTokenSource.Cancel(); } } else { output = await invokeTask(context.CancelToken); } Debug.Assert(output != null); return(output); }
public static JToken?EvalExpr(this string?expr, JToken json, StateMachineContext context) { json.CheckArgNull(nameof(json)); context.CheckArgNull(nameof(context)); if (string.IsNullOrWhiteSpace(expr)) { return(json); } else if (!expr.IsJQExpression()) { throw new InvalidOperationException("Invalid JQ expression: " + expr); } expr = expr.TrimJQExpression(); if (expr.StartsWith("fn:")) { var functionName = expr[3..];
public static async Task <JToken> ExecuteAsync(this ForEachAction action, StateMachineContext context, JToken input) { action.CheckArgNull(nameof(action)); context.CheckArgNull(nameof(context)); input.CheckArgNull(nameof(input)); Debug.Assert(!string.IsNullOrWhiteSpace(action.Input)); var token = action.Input.EvalExpr(input, context); if (token == null || token.Type != JTokenType.Array) { throw new InvalidOperationException("Unable to resolve input collection for ForEach action."); } var inputCollection = (JArray)token; var max = action.MaxParallel ?? inputCollection.Count; Debug.Assert(action.Action != null); var outputs = new JArray(); for (var i = 0; i < inputCollection.Count; i += max) { if (context.CancelToken.IsCancellationRequested) { break; } var subsetTasks = inputCollection.Skip(i) .Take(max) .Select(json => action.Action.ExecuteAsync(context, json)); var results = await Task.WhenAll(subsetTasks); Array.ForEach(results, outputs.Add); } return(outputs); }
public static async Task <State?> ExecuteAsync(this State state, StateMachineContext context) { state.CheckArgNull(nameof(state)); context.CheckArgNull(nameof(context)); JToken data = await EnterState(state, context); State?nextState = null; while (true) { var transition = await state.ResolveTransitionAsync(context, data); if (transition == null) { break; } if (transition.Action != null) { var result = await transition.Action.ExecuteAsync(context, data); Debug.Assert(result != null); result.Merge(context.Data, transition.ResultHandler, context); } nextState = context.Workflow.ResolveStateByName(transition.NextState); if (nextState != null) { break; } } await ExitState(state, context, data); return(nextState); }
public static async Task <JToken> ExecuteAsync(this ModelAction action, StateMachineContext context, JToken input) { action.CheckArgNull(nameof(action)); context.CheckArgNull(nameof(context)); input.CheckArgNull(nameof(input)); await context.RecordObservableActionAsync(ObservableAction.BeforeAction, () => new Dictionary <string, object> { { "actionName", action.Name }, { "actionType", action.GetType().FullName } }); Func <StateMachineContext, JToken, Task <JToken> > executeFunc = action switch { InjectDataAction inject => inject.ExecuteAsync, ParallelAction parallel => parallel.ExecuteAsync, SequenceAction sequence => sequence.ExecuteAsync, SendEventAction send => send.ExecuteAsync, DelayAction delay => delay.ExecuteAsync, InvokeSubflowAction subflow => subflow.ExecuteAsync, InvokeFunctionAction function => function.ExecuteAsync, ForEachAction forEach => forEach.ExecuteAsync, _ => throw new NotImplementedException("Action behavior not implemented: " + action.GetType().FullName) }; Debug.Assert(executeFunc != null); var attempts = 0; DateTimeOffset?start = null; while (true) { try { var result = await executeFunc(context, input); await context.RecordObservableActionAsync(ObservableAction.AfterAction, () => new Dictionary <string, object> { { "actionName", action.Name }, { "actionType", action.GetType().FullName } }); return(result); } catch (Exception ex) { if (action.TryHandleError(JObject.FromObject(ex), context, out RetryPolicy? retryPolicy)) { if (retryPolicy == null) { return(JValue.CreateNull()); } else { TimeSpan elapsedDelay; if (start == null) { start = DateTimeOffset.UtcNow; elapsedDelay = TimeSpan.Zero; } else { elapsedDelay = DateTimeOffset.UtcNow.Subtract(start.Value); } var retry = await retryPolicy.ShouldRetryAsync(context, ++attempts, elapsedDelay); if (retry) { continue; } } } throw; } } }