Пример #1
0
        public async Task InvalidTokenReferenceTest()
        {
            string a2input1 = "$(Actions. badly formed";
            string a2input2 = "$(Actions.a15.MissingAction)";
            string a2input3 = "$(Actions.a1.MissingResult)";

            LogRecord          record   = new LogRecord();
            PassThroughOptions settings = null;
            await TestHostHelper.CreateCollectionRulesHost(_outputHelper, rootOptions =>
            {
                CollectionRuleOptions options = rootOptions.CreateCollectionRule(DefaultRuleName)
                                                .AddPassThroughAction("a1", "a1input1", "a1input2", "a1input3")
                                                .AddPassThroughAction("a2", a2input1, a2input2, a2input3)
                                                .SetStartupTrigger();

                settings = (PassThroughOptions)options.Actions.Last().Settings;
            }, host =>
            {
                using CancellationTokenSource cancellationTokenSource = new CancellationTokenSource(TimeoutMs);

                CollectionRuleOptions ruleOptions      = host.Services.GetRequiredService <IOptionsMonitor <CollectionRuleOptions> >().Get(DefaultRuleName);
                ILogger <CollectionRuleService> logger = host.Services.GetRequiredService <ILogger <CollectionRuleService> >();
                ISystemClock clock = host.Services.GetRequiredService <ISystemClock>();

                CollectionRuleContext context = new(DefaultRuleName, ruleOptions, null, logger, clock);

                ActionOptionsDependencyAnalyzer analyzer = ActionOptionsDependencyAnalyzer.Create(context);
                analyzer.GetActionDependencies(1);
                analyzer.SubstituteOptionValues(new Dictionary <string, CollectionRuleActionResult>(), 1, settings);

                Assert.Equal(3, record.Events.Count);
                Assert.Equal(LoggingEventIds.InvalidActionReferenceToken.Id(), record.Events[0].EventId.Id);
                Assert.Equal(LoggingEventIds.InvalidActionReference.Id(), record.Events[1].EventId.Id);
                Assert.Equal(LoggingEventIds.InvalidActionResultReference.Id(), record.Events[2].EventId.Id);
            }, serviceCollection =>
        //CONSIDER Only named rules currently return results since only named results can be referenced
        public async Task <IDictionary <string, CollectionRuleActionResult> > ExecuteActions(
            CollectionRuleContext context,
            Action startCallback,
            CancellationToken cancellationToken)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            bool   started = false;
            Action wrappedStartCallback = () =>
            {
                if (!started)
                {
                    started = true;
                    startCallback?.Invoke();
                }
            };

            int actionIndex = 0;
            List <ActionCompletionEntry> deferredCompletions = new(context.Options.Actions.Count);

            var actionResults      = new Dictionary <string, CollectionRuleActionResult>(StringComparer.Ordinal);
            var dependencyAnalyzer = ActionOptionsDependencyAnalyzer.Create(context);

            try
            {
                // Start and optionally wait for each action to complete
                foreach (CollectionRuleActionOptions actionOption in context.Options.Actions)
                {
                    KeyValueLogScope actionScope = new();
                    actionScope.AddCollectionRuleAction(actionOption.Type, actionIndex);
                    using IDisposable actionScopeRegistration = _logger.BeginScope(actionScope);

                    _logger.CollectionRuleActionStarted(context.Name, actionOption.Type);

                    try
                    {
                        IList <CollectionRuleActionOptions> actionDependencies = dependencyAnalyzer.GetActionDependencies(actionIndex);
                        foreach (CollectionRuleActionOptions actionDependency in actionDependencies)
                        {
                            for (int i = 0; i < deferredCompletions.Count; i++)
                            {
                                ActionCompletionEntry deferredCompletion = deferredCompletions[i];
                                if (string.Equals(deferredCompletion.Options.Name, actionDependency.Name, StringComparison.Ordinal))
                                {
                                    deferredCompletions.RemoveAt(i);
                                    i--;
                                    await WaitForCompletion(context, wrappedStartCallback, actionResults, deferredCompletion, cancellationToken);

                                    break;
                                }
                            }
                        }

                        ICollectionRuleActionFactoryProxy factory;

                        if (!_actionOperations.TryCreateFactory(actionOption.Type, out factory))
                        {
                            throw new InvalidOperationException(Strings.ErrorMessage_CouldNotMapToAction);
                        }

                        object newSettings           = dependencyAnalyzer.SubstituteOptionValues(actionResults, actionIndex, actionOption.Settings);
                        ICollectionRuleAction action = factory.Create(context.EndpointInfo, newSettings);

                        try
                        {
                            await action.StartAsync(cancellationToken);

                            // Check if the action completion should be awaited synchronously (in respect to
                            // starting the next action). If not, add a deferred entry so that it can be completed
                            // after starting each action in the list.
                            if (actionOption.WaitForCompletion.GetValueOrDefault(CollectionRuleActionOptionsDefaults.WaitForCompletion))
                            {
                                await WaitForCompletion(context, wrappedStartCallback, actionResults, action, actionOption, cancellationToken);
                            }
                            else
                            {
                                deferredCompletions.Add(new(action, actionOption, actionIndex));

                                // Set to null to skip disposal
                                action = null;
                            }
                        }
                        finally
                        {
                            await DisposableHelper.DisposeAsync(action);
                        }
                    }
                    catch (Exception ex) when(ShouldHandleException(ex, context.Name, actionOption.Type))
                    {
                        throw new CollectionRuleActionExecutionException(ex, actionOption.Type, actionIndex);
                    }

                    ++actionIndex;
                }

                // Notify that all actions have started
                wrappedStartCallback?.Invoke();

                // Wait for any actions whose completion has been deferred.
                while (deferredCompletions.Count > 0)
                {
                    ActionCompletionEntry deferredCompletion = deferredCompletions[0];
                    deferredCompletions.RemoveAt(0);
                    await WaitForCompletion(context, wrappedStartCallback, actionResults, deferredCompletion, cancellationToken);
                }

                return(actionResults);
            }
            finally
            {
                // Always dispose any deferred action completions so that those actions
                // are stopped before leaving the action list executor.
                foreach (ActionCompletionEntry deferredCompletion in deferredCompletions)
                {
                    await DisposableHelper.DisposeAsync(deferredCompletion.Action);
                }
            }
        }