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());
        }
예제 #3
0
        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;
                }
            }
        }