< Summary

Information
Class: Elsa.Workflows.Activities.SwitchCase
Assembly: Elsa.Workflows.Core
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Workflows.Core/Activities/Switch.cs
Line coverage
66%
Covered lines: 12
Uncovered lines: 6
Coverable lines: 18
Total lines: 186
Line coverage: 66.6%
Branch coverage
N/A
Covered branches: 0
Total branches: 0
Branch coverage: N/A
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor()100%210%
.ctor(...)100%11100%
.ctor(...)100%210%
.ctor(...)100%210%
.ctor(...)100%11100%
.ctor(...)100%11100%
get_Label()100%11100%
get_Condition()100%11100%
get_Activity()100%11100%

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 />
 21    public Switch([CallerFilePath] string? source = null, [CallerLineNumber] int? line = null) : base(source, line)
 22    {
 23    }
 24
 25    /// <summary>
 26    /// The value to switch on, made available as output for capturing.
 27    /// </summary>
 28    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    )]
 37    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    )]
 46    public Input<SwitchMode> Mode { get; set; } = new(SwitchMode.MatchFirst);
 47
 48    /// <summary>
 49    /// The default activity to schedule when no case matches.
 50    /// </summary>
 51    [Port] public IActivity? Default { get; set; }
 52
 53    /// <inheritdoc />
 54    protected override async ValueTask ExecuteAsync(ActivityExecutionContext context)
 55    {
 56        var matchingCases = (await FindMatchingCasesAsync(context.ExpressionExecutionContext)).ToList();
 57        var hasAnyMatches = matchingCases.Any();
 58        var mode = context.Get(Mode);
 59        var results = mode == SwitchMode.MatchFirst
 60            ? hasAnyMatches
 61                ? new[] { matchingCases.First() }
 62                : Array.Empty<SwitchCase>()
 63            : matchingCases.ToArray();
 64
 65        var scheduledActivityIds = new HashSet<string>();
 66
 67        if (hasAnyMatches)
 68        {
 69            foreach (var result in results)
 70            {
 71                if (result.Activity == null)
 72                    continue;
 73
 74                await context.ScheduleActivityAsync(result.Activity, OnChildActivityCompletedAsync);
 75                scheduledActivityIds.Add(result.Activity.Id);
 76            }
 77        }
 78        else if (Default != null)
 79        {
 80            await context.ScheduleActivityAsync(Default, OnChildActivityCompletedAsync);
 81            scheduledActivityIds.Add(Default.Id);
 82        }
 83
 84        context.SetProperty("ScheduledActivityIds", scheduledActivityIds);
 85
 86        if (!scheduledActivityIds.Any())
 87        {
 88            await context.CompleteActivityAsync();
 89        }
 90    }
 91
 92    private async Task<IEnumerable<SwitchCase>> FindMatchingCasesAsync(ExpressionExecutionContext context)
 93    {
 94        var matchingCases = new List<SwitchCase>();
 95        var expressionEvaluator = context.GetRequiredService<IExpressionEvaluator>();
 96
 97        foreach (var switchCase in Cases)
 98        {
 99            var result = await expressionEvaluator.EvaluateAsync<bool?>(switchCase.Condition, context);
 100
 101            if (result == true)
 102            {
 103                matchingCases.Add(switchCase);
 104            }
 105        }
 106
 107        return matchingCases;
 108    }
 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    {
 115        var scheduledActivityIds = context.TargetContext.GetProperty<HashSet<string>>("ScheduledActivityIds");
 116
 117        if (scheduledActivityIds != null
 118            && scheduledActivityIds.Remove(context.ChildContext.Activity.Id)
 119            && scheduledActivityIds.Count == 0)
 120        {
 121            await context.TargetContext.CompleteActivityAsync();
 122        }
 123    }
 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]
 0135    public SwitchCase()
 136    {
 0137    }
 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>
 40145    public SwitchCase(string label, Expression condition, IActivity activity)
 146    {
 40147        Label = label;
 40148        Condition = condition;
 40149        Activity = activity;
 40150    }
 151
 152    /// <inheritdoc />
 0153    public SwitchCase(string label, Func<ExpressionExecutionContext, ValueTask<bool>> condition, IActivity activity) : t
 154    {
 0155    }
 156
 157    /// <inheritdoc />
 0158    public SwitchCase(string label, Func<ValueTask<bool>> condition, IActivity activity) : this(label, Expression.Delega
 159    {
 0160    }
 161
 162    /// <inheritdoc />
 3163    public SwitchCase(string label, Func<ExpressionExecutionContext, bool> condition, IActivity activity) : this(label, 
 164    {
 3165    }
 166
 167    /// <inheritdoc />
 22168    public SwitchCase(string label, Func<bool> condition, IActivity activity) : this(label, Expression.DelegateExpressio
 169    {
 22170    }
 171
 172    /// <summary>
 173    /// The label of the case.
 174    /// </summary>
 104175    public string Label { get; set; } = null!;
 176
 177    /// <summary>
 178    /// The condition to evaluate.
 179    /// </summary>
 144180    public Expression Condition { get; set; } = Expression.LiteralExpression(false);
 181
 182    /// <summary>
 183    /// The activity to schedule when the condition evaluates to true.
 184    /// </summary>
 188185    public IActivity? Activity { get; set; }
 186}