diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Switch/Switch.cs b/src/core/Elsa.Core/Activities/ControlFlow/Switch/Switch.cs index 3ab46962e..937c50ac8 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/Switch/Switch.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/Switch/Switch.cs @@ -18,7 +18,7 @@ namespace Elsa.Activities.ControlFlow DisplayName = "Switch", Category = "Control Flow", Description = "Evaluate multiple conditions and continue execution depending on the results.", - Outcomes = new[] { OutcomeNames.Done } + Outcomes = new[] { OutcomeNames.Default } )] public class Switch : Activity, INotificationHandler { @@ -55,8 +55,9 @@ namespace Elsa.Activities.ControlFlow } var matches = Cases.Where(x => x.Condition).Select(x => x.Name).ToList(); - var results = Mode == SwitchMode.MatchFirst ? matches.Any() ? new[] { matches.First() } : new string[0] : matches.ToArray(); - var outcomes = results; + var hasAnyMatches = matches.Any(); + var results = Mode == SwitchMode.MatchFirst ? hasAnyMatches ? new[] { matches.First() } : new string[0] : matches.ToArray(); + var outcomes = hasAnyMatches ? results : new[] { OutcomeNames.Default }; return Outcomes(outcomes); } diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Switch/SwitchHandler.cs b/src/core/Elsa.Core/Activities/ControlFlow/Switch/SwitchHandler.cs new file mode 100644 index 000000000..ec9c9abfe --- /dev/null +++ b/src/core/Elsa.Core/Activities/ControlFlow/Switch/SwitchHandler.cs @@ -0,0 +1,59 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Expressions; +using Elsa.Serialization; +using Elsa.Services.Models; + +namespace Elsa.Activities.ControlFlow +{ + public class SwitchHandler : IExpressionHandler + { + private readonly IContentSerializer _contentSerializer; + public string Syntax => "Switch"; + + public SwitchHandler(IContentSerializer contentSerializer) + { + _contentSerializer = contentSerializer; + } + + public async Task EvaluateAsync(string expression, Type returnType, ActivityExecutionContext context, CancellationToken cancellationToken) + { + var caseModels = TryDeserializeExpression(expression); + var evaluatedCases = await EvaluateCasesAsync(caseModels, context, cancellationToken).ToListAsync(cancellationToken); + return evaluatedCases; + } + + private async IAsyncEnumerable EvaluateCasesAsync(IEnumerable caseModels, ActivityExecutionContext context, [EnumeratorCancellation] CancellationToken cancellationToken) + { + var validCaseModels = caseModels.Where(x => x.Expressions != null && !string.IsNullOrWhiteSpace(x.Syntax) && x.Expressions.ContainsKey(x.Syntax)).ToList(); + var evaluator = context.GetService(); + + foreach (var caseModel in validCaseModels) + { + var syntax = caseModel.Syntax!; + var expression = caseModel.Expressions![syntax]; + var result = await evaluator.EvaluateAsync(expression, syntax, context, cancellationToken); + + yield return new SwitchCase(caseModel.Name, result); + } + } + + private IList TryDeserializeExpression(string expression) + { + try + { + return _contentSerializer.Deserialize>(expression); + } + catch + { + return new List(); + } + } + + public record SwitchCaseModel(string Name, IDictionary? Expressions, string? Syntax); + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs index d576656a6..ba7ab5d59 100644 --- a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs @@ -1,5 +1,6 @@ using System; using Elsa; +using Elsa.Activities.ControlFlow; using Elsa.Activities.Signaling; using Elsa.Activities.Signaling.Services; using Elsa.Activities.Workflows; @@ -147,6 +148,7 @@ namespace Microsoft.Extensions.DependencyInjection .TryAddProvider(ServiceLifetime.Singleton) .TryAddProvider(ServiceLifetime.Singleton) .TryAddProvider(ServiceLifetime.Singleton) + .TryAddProvider(ServiceLifetime.Singleton) .AddScoped(); // Workflow providers. diff --git a/src/designer/elsa-workflows-studio/src/plugins/switch-plugin.tsx b/src/designer/elsa-workflows-studio/src/plugins/switch-plugin.tsx new file mode 100644 index 000000000..4c6667d73 --- /dev/null +++ b/src/designer/elsa-workflows-studio/src/plugins/switch-plugin.tsx @@ -0,0 +1,26 @@ +import {ElsaPlugin} from "../services/elsa-plugin"; +import {eventBus} from '../services/event-bus'; +import {ActivityDesignDisplayContext, EventTypes, SyntaxNames} from "../models"; +import {h} from "@stencil/core"; +import {parseJson} from "../utils/utils"; +import {SwitchCase} from "../components/editors/properties/elsa-switch-cases-property/models"; + +export class SwitchPlugin implements ElsaPlugin { + constructor() { + eventBus.on(EventTypes.ActivityDesignDisplaying, this.onActivityDesignDisplaying); + } + + onActivityDesignDisplaying(context: ActivityDesignDisplayContext) { + const activityModel = context.activityModel; + + if (activityModel.type !== 'Switch') + return; + + const props = activityModel.properties || []; + const syntax = 'Switch'; + const casesProp = props.find(x => x.name == 'Cases') || { expressions: {'Switch': ''}, syntax: syntax }; + const expression = casesProp.expressions[syntax]; + const cases: Array = parseJson(expression) || []; + context.outcomes = [...cases.map(x => x.name), 'Default']; + } +} diff --git a/src/designer/elsa-workflows-studio/src/services/plugin-manager.ts b/src/designer/elsa-workflows-studio/src/services/plugin-manager.ts index 13d0fa63d..c4605dce9 100644 --- a/src/designer/elsa-workflows-studio/src/services/plugin-manager.ts +++ b/src/designer/elsa-workflows-studio/src/services/plugin-manager.ts @@ -8,6 +8,7 @@ import {DefaultDriversPlugin} from "../plugins/default-drivers-plugin"; import {ForkPlugin} from "../plugins/fork-plugin"; import {RunJavascriptPlugin} from "../plugins/run-javascript-plugin"; import {ActivityIconProviderPlugin} from "../plugins/activity-icon-provider-plugin"; +import {SwitchPlugin} from "../plugins/switch-plugin"; export class PluginManager { @@ -19,6 +20,7 @@ export class PluginManager { new ActivityIconProviderPlugin(), new IfPlugin(), new ForkPlugin(), + new SwitchPlugin(), new HttpEndpointPlugin(), new TimerPlugin(), new WriteLinePlugin(), diff --git a/src/scripting/Elsa.Scripting.JavaScript/Extensions/JavaScriptServiceCollectionExtensions.cs b/src/scripting/Elsa.Scripting.JavaScript/Extensions/JavaScriptServiceCollectionExtensions.cs index 883f7cc80..87fd4454a 100644 --- a/src/scripting/Elsa.Scripting.JavaScript/Extensions/JavaScriptServiceCollectionExtensions.cs +++ b/src/scripting/Elsa.Scripting.JavaScript/Extensions/JavaScriptServiceCollectionExtensions.cs @@ -19,7 +19,7 @@ namespace Microsoft.Extensions.DependencyInjection .AddScoped() .AddScoped() .AddScoped() - .AddTransient(s => new JintEvaluationResultConverterFactory(s).GetConverter()) + .AddTransient(s => new JintEvaluationResultConverterFactory(s).GetConverter()) .AddTransient(s => new EnumerableResultConverter(default)) .TryAddProvider(ServiceLifetime.Scoped) .AddNotificationHandlers(typeof(JavaScriptServiceCollectionExtensions));