< Summary

Information
Class: Elsa.Workflows.Activities.Switch
Assembly: Elsa.Workflows.Core
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Activities/Switch.cs
Line coverage
100%
Covered lines: 43
Uncovered lines: 0
Coverable lines: 43
Total lines: 186
Line coverage: 100%
Branch coverage
100%
Covered branches: 24
Total branches: 24
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
get_Output()100%11100%
get_Cases()100%11100%
get_Mode()100%11100%
get_Default()100%11100%
ExecuteAsync()100%1414100%
FindMatchingCasesAsync()100%44100%
OnChildActivityCompletedAsync()100%66100%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Activities/Switch.cs

#LineLine coverage
 1using System.Runtime.CompilerServices;
 2using System.Text.Json.Serialization;
 3using Elsa.Expressions.Contracts;
 4using Elsa.Expressions.Models;
 5using Elsa.Workflows.Attributes;
 6using Elsa.Workflows.Models;
 7using Elsa.Workflows.UIHints;
 8using JetBrains.Annotations;
 9
 10namespace Elsa.Workflows.Activities;
 11
 12/// <summary>
 13/// The Switch activity is an approximation of the `switch` construct in C#.
 14/// When a case evaluates to true, the associated activity is then scheduled for execution.
 15/// </summary>
 16[Activity("Elsa", "Branching", "Evaluate a set of case conditions and schedule the activity for a matching case.")]
 17[PublicAPI]
 18public class Switch : Activity
 19{
 20    /// <inheritdoc />
 2521    public Switch([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line)
 22    {
 2523    }
 24
 25    /// <summary>
 26    /// The value to switch on, made available as output for capturing.
 27    /// </summary>
 3328    public Output<object>? Output { get; set; }
 29
 30    /// <summary>
 31    /// The cases to evaluate.
 32    /// </summary>
 33    [Input(
 34        Description = "The cases to evaluate.",
 35        UIHint = "switch-editor"
 36    )]
 13637    public ICollection<SwitchCase> Cases { get; set; } = new List<SwitchCase>();
 38
 39    /// <summary>
 40    /// The switch mode determines whether the first match should be scheduled, or all matches.
 41    /// </summary>
 42    [Input(
 43        Description = "The switch mode determines whether the first match should be scheduled, or all matches.",
 44        UIHint = InputUIHints.DropDown
 45    )]
 11246    public Input<SwitchMode> Mode { get; set; } = new(SwitchMode.MatchFirst);
 47
 48    /// <summary>
 49    /// The default activity to schedule when no case matches.
 50    /// </summary>
 7451    [Port] public IActivity? Default { get; set; }
 52
 53    /// <inheritdoc />
 54    protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
 55    {
 2356        var matchingCases = (await FindMatchingCasesAsync(context.ExpressionExecutionContext)).ToList();
 2357        var hasAnyMatches = matchingCases.Any();
 2358        var mode = context.Get(Mode);
 2359        var results = mode == SwitchMode.MatchFirst
 2360            ? hasAnyMatches
 2361                ? new[] { matchingCases.First() }
 2362                : Array.Empty<SwitchCase>()
 2363            : matchingCases.ToArray();
 64
 2365        var scheduledActivityIds = new HashSet<string>();
 66
 2367        if (hasAnyMatches)
 68        {
 4869            foreach (var result in results)
 70            {
 1571                if (result.Activity == null)
 72                    continue;
 73
 1574                await context.ScheduleActivityAsync(result.Activity, OnChildActivityCompletedAsync);
 1575                scheduledActivityIds.Add(result.Activity.Id);
 1576            }
 77        }
 1478        else if (Default != null)
 79        {
 880            await context.ScheduleActivityAsync(Default, OnChildActivityCompletedAsync);
 881            scheduledActivityIds.Add(Default.Id);
 82        }
 83
 2384        context.SetProperty("ScheduledActivityIds", scheduledActivityIds);
 85
 2386        if (!scheduledActivityIds.Any())
 87        {
 688            await context.CompleteActivityAsync();
 89        }
 2390    }
 91
 92    private async Task<IEnumerable<SwitchCase>> FindMatchingCasesAsync(ExpressionExecutionContext context)
 93    {
 2394        var matchingCases = new List<SwitchCase>();
 2395        var expressionEvaluator = context.GetRequiredService<IExpressionEvaluator>();
 96
 12497        foreach (var switchCase in Cases)
 98        {
 3999            var result = await expressionEvaluator.EvaluateAsync<bool?>(switchCase.Condition, context);
 100
 39101            if (result == true)
 102            {
 18103                matchingCases.Add(switchCase);
 104            }
 39105        }
 106
 23107        return matchingCases;
 23108    }
 109
 110    /// <summary>
 111    /// Tracks the completion of scheduled child activities and completes the Switch activity only after all scheduled c
 112    /// </summary>
 113    private async ValueTask OnChildActivityCompletedAsync(ActivityCompletedContext context)
 114    {
 13115        var scheduledActivityIds = context.TargetContext.GetProperty<HashSet<string>>("ScheduledActivityIds");
 116
 13117        if (scheduledActivityIds != null
 13118            && scheduledActivityIds.Remove(context.ChildContext.Activity.Id)
 13119            && scheduledActivityIds.Count == 0)
 120        {
 8121            await context.TargetContext.CompleteActivityAsync();
 122        }
 13123    }
 124}
 125
 126/// <summary>
 127/// Represents an individual case of the <see cref="Switch"/> activity.
 128/// </summary>
 129public class SwitchCase
 130{
 131    /// <summary>
 132    /// Initializes a new instance of the <see cref="SwitchCase"/> class.
 133    /// </summary>
 134    [JsonConstructor]
 135    public SwitchCase()
 136    {
 137    }
 138
 139    /// <summary>
 140    /// Initializes a new instance of the <see cref="SwitchCase"/> class.
 141    /// </summary>
 142    /// <param name="label">The label of the case.</param>
 143    /// <param name="condition">The condition to evaluate.</param>
 144    /// <param name="activity">The activity to schedule when the condition evaluates to true.</param>
 145    public SwitchCase(string label, Expression condition, IActivity activity)
 146    {
 147        Label = label;
 148        Condition = condition;
 149        Activity = activity;
 150    }
 151
 152    /// <inheritdoc />
 153    public SwitchCase(string label, Func<ExpressionExecutionContext, ValueTask<bool>> condition, IActivity activity) : t
 154    {
 155    }
 156
 157    /// <inheritdoc />
 158    public SwitchCase(string label, Func<ValueTask<bool>> condition, IActivity activity) : this(label, Expression.Delega
 159    {
 160    }
 161
 162    /// <inheritdoc />
 163    public SwitchCase(string label, Func<ExpressionExecutionContext, bool> condition, IActivity activity) : this(label, 
 164    {
 165    }
 166
 167    /// <inheritdoc />
 168    public SwitchCase(string label, Func<bool> condition, IActivity activity) : this(label, Expression.DelegateExpressio
 169    {
 170    }
 171
 172    /// <summary>
 173    /// The label of the case.
 174    /// </summary>
 175    public string Label { get; set; } = null!;
 176
 177    /// <summary>
 178    /// The condition to evaluate.
 179    /// </summary>
 180    public Expression Condition { get; set; } = Expression.LiteralExpression(false);
 181
 182    /// <summary>
 183    /// The activity to schedule when the condition evaluates to true.
 184    /// </summary>
 185    public IActivity? Activity { get; set; }
 186}