public static async Task <JToken> ExecuteAsync(this InvokeFunctionAction action, StateMachineContext context, JToken input) { action.CheckArgNull(nameof(action)); context.CheckArgNull(nameof(context)); input.CheckArgNull(nameof(input)); Func <CancellationToken, Task <JToken> > invokeTask = token => ExecuteFunctionAsync(action, context, input, token); 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); }
private static async Task <JToken> ExecuteFunctionAsync(InvokeFunctionAction action, StateMachineContext context, JToken input, CancellationToken cancelToken) { Debug.Assert(action != null); Debug.Assert(context != null); Debug.Assert(input != null); var function = context.Workflow.Functions?.SingleOrDefault(f => f.Name.IsEqualTo(action.FunctionName)); if (function == null) { throw new InvalidOperationException("Unable to resolve function reference: " + action.FunctionName); } Debug.Assert(!string.IsNullOrWhiteSpace(function.Operation)); JToken?result = null; switch (function.Type) { case FunctionsType.Rest: result = await context.Host.InvokeAsync(function.Operation, action.Arguments.ResolveExpressionArguments(input, context), cancelToken, action.WaitForCompletion); break; case FunctionsType.Expression: result = function.Operation.EvalExpr(input, context); break; default: Debug.Fail("Unexpected FunctionsType: " + function.Type); break; } return(result ?? JValue.CreateNull()); }
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; } } }