Refactored Switch Activity (#545)

* Add IfThen activity + sample

* Add match mode

* Rename IfThen to Switch
This commit is contained in:
Sipke Schoorstra 2020-12-29 16:40:21 +01:00 committed by GitHub
parent 460c75227d
commit 993e08ea89
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
34 changed files with 323 additions and 179 deletions

View file

@ -193,6 +193,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "server", "server", "{BBCFC6
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.Server.Host", "src\samples\server\Elsa.Samples.Server.Host\Elsa.Samples.Server.Host.csproj", "{63E52936-6C1D-4F45-8B56-86C23E9A853F}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.SwitchConsole", "src\samples\console\Elsa.Samples.SwitchConsole\Elsa.Samples.SwitchConsole.csproj", "{D6DD6E38-BB34-4B2F-9A42-4C17AF30C9CD}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@ -459,6 +461,10 @@ Global
{63E52936-6C1D-4F45-8B56-86C23E9A853F}.Debug|Any CPU.Build.0 = Debug|Any CPU
{63E52936-6C1D-4F45-8B56-86C23E9A853F}.Release|Any CPU.ActiveCfg = Release|Any CPU
{63E52936-6C1D-4F45-8B56-86C23E9A853F}.Release|Any CPU.Build.0 = Release|Any CPU
{D6DD6E38-BB34-4B2F-9A42-4C17AF30C9CD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D6DD6E38-BB34-4B2F-9A42-4C17AF30C9CD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D6DD6E38-BB34-4B2F-9A42-4C17AF30C9CD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D6DD6E38-BB34-4B2F-9A42-4C17AF30C9CD}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@ -549,6 +555,7 @@ Global
{3CF1D3EA-7D93-44B0-80A2-77983A5357BA} = {4673732F-2853-47BD-91B8-C95C229D2C89}
{BBCFC623-2113-464D-8A1F-3CCDABF1241C} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A}
{63E52936-6C1D-4F45-8B56-86C23E9A853F} = {BBCFC623-2113-464D-8A1F-3CCDABF1241C}
{D6DD6E38-BB34-4B2F-9A42-4C17AF30C9CD} = {FC9F520F-BA51-4AD2-BFEE-EF787798E734}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158}

View file

@ -59,7 +59,7 @@ namespace Elsa.Activities.Timers.HostedServices
var workflow = runAtWorkflow.workflow;
var activity = runAtWorkflow.activity;
var workflowWrapper = await _workflowBlueprintReflector.ReflectAsync(serviceScope, workflow, cancellationToken);
var timerWrapper = workflowWrapper.GetActivity<StartAt>(activity.Id);
var timerWrapper = workflowWrapper.GetActivity<StartAt>(activity.Id)!;
var startAt = await timerWrapper.GetPropertyValueAsync(x => x.Instant, cancellationToken);
await _workflowScheduler.ScheduleWorkflowAsync(workflow, activity.Id, startAt, cancellationToken);
@ -91,7 +91,7 @@ namespace Elsa.Activities.Timers.HostedServices
var workflow = timerWorkflow.workflow;
var activity = timerWorkflow.activity;
var workflowWrapper = await _workflowBlueprintReflector.ReflectAsync(serviceScope, workflow, cancellationToken);
var timerEventWrapper = workflowWrapper.GetActivity<Timer>(activity.Id);
var timerEventWrapper = workflowWrapper.GetActivity<Timer>(activity.Id)!;
var timeOut = await timerEventWrapper.GetPropertyValueAsync(x => x.Timeout, cancellationToken);
var startAt = now.Plus(timeOut);
@ -122,7 +122,7 @@ namespace Elsa.Activities.Timers.HostedServices
var workflow = cronWorkflow.workflow;
var activity = cronWorkflow.activity;
var workflowWrapper = await _workflowBlueprintReflector.ReflectAsync(serviceScope, workflow, cancellationToken);
var timerEventWrapper = workflowWrapper.GetActivity<Cron>(activity.Id);
var timerEventWrapper = workflowWrapper.GetActivity<Cron>(activity.Id)!;
var cronExpression = await timerEventWrapper.GetPropertyValueAsync(x => x.CronExpression, cancellationToken);
await _workflowScheduler.ScheduleWorkflowAsync(workflow, activity.Id, cronExpression!, cancellationToken);

View file

@ -32,7 +32,5 @@ namespace Elsa.Client.Models
/// </summary>
[DataMember(Order = 11)]
public Variables CustomAttributes { get; set;} = new();
[DataMember(Order = 12)] public string? DisplayName { get; set; }
}
}

View file

@ -16,14 +16,14 @@ namespace Elsa.ActivityResults
Outcomes = outcomeList;
}
public IEnumerable<string> Outcomes { get; }
protected override void Execute(ActivityExecutionContext activityExecutionContext)
{
var outcomes = activityExecutionContext.Outcomes = Outcomes.ToList();
var workflowExecutionContext = activityExecutionContext.WorkflowExecutionContext;
var nextActivities = GetNextActivities(
workflowExecutionContext,
activityExecutionContext.ActivityBlueprint.Id,
@ -37,14 +37,17 @@ namespace Elsa.ActivityResults
string sourceId,
IEnumerable<string> outcomes)
{
var orderedOutcomes = outcomes.Select((outcome, order) => (outcome, order));
var query =
from connection in workflowContext.WorkflowBlueprint.Connections
from outcome in outcomes
from outcome in orderedOutcomes
let connectionOutcome = connection.Source.Outcome ?? OutcomeNames.Done
let isConnectionOutcome = connectionOutcome.Equals(outcome, StringComparison.OrdinalIgnoreCase)
let isConnectionOutcome = connectionOutcome.Equals(outcome.outcome, StringComparison.OrdinalIgnoreCase)
where connection.Source.Activity.Id == sourceId && isConnectionOutcome
from activityBlueprint in workflowContext.WorkflowBlueprint.Activities
where activityBlueprint.Id == connection.Target.Activity.Id
orderby outcome.order
select activityBlueprint.Id;
return query.Distinct();

View file

@ -1,3 +1,3 @@
using System.Runtime.CompilerServices;
[assembly: InternalsVisibleTo("Elsa.Core")]
[assembly: InternalsVisibleTo("Elsa.Core")]

View file

@ -27,13 +27,13 @@ namespace Elsa.Builders
this ISetupActivity<T> builder,
Expression<Func<T, TProperty?>> propertyAccessor,
Func<ActivityExecutionContext, TProperty?> valueFactory) where T : IActivity =>
builder.Set(propertyAccessor, context => new ValueTask<TProperty>(valueFactory(context)));
builder.Set(propertyAccessor, context => new ValueTask<TProperty?>(valueFactory(context)));
public static ISetupActivity<T> Set<T, TProperty>(
this ISetupActivity<T> builder,
Expression<Func<T, TProperty?>> propertyAccessor,
Func<TProperty?> valueFactory) where T : IActivity =>
builder.Set(propertyAccessor, context => new ValueTask<TProperty>(valueFactory()));
builder.Set(propertyAccessor, context => new ValueTask<TProperty?>(valueFactory()));
public static ISetupActivity<T> Set<T, TProperty>(
this ISetupActivity<T> builder,

View file

@ -14,8 +14,6 @@ namespace Elsa.Builders
bool DeleteCompletedInstances { get; }
bool IsEnabled { get; }
IWorkflowBuilder WithWorkflowDefinitionId(string value);
IWorkflowBuilder WithDisplayName(string value);
IWorkflowBuilder WithDescription(string value);
IWorkflowBuilder WithVersion(int value);
IWorkflowBuilder AsSingleton();
IWorkflowBuilder AsTransient();

View file

@ -0,0 +1,27 @@
using System.ComponentModel;
using NodaTime;
using NodaTime.Text;
namespace Elsa
{
public static class ObjectConverter
{
public static T? ConvertTo<T>(this object? value)
{
if (value == null)
return default!;
if (value is T convertedValue)
return convertedValue;
if (value == default!)
return default!;
if (typeof(T) == typeof(Duration))
return (T?)((object?)DurationPattern.JsonRoundtrip.Parse(value!.ToString()).Value)!;
var converter = TypeDescriptor.GetConverter(typeof(T));
return converter.CanConvertFrom(value.GetType()) ? (T?) converter.ConvertFrom(value) : default;
}
}
}

View file

@ -0,0 +1,17 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System.ComponentModel;
// ReSharper disable once CheckNamespace
namespace System.Runtime.CompilerServices
{
/// <summary>
/// Reserved to be used by the compiler for tracking metadata.
/// This class should not be used by developers in source code.
/// </summary>
[EditorBrowsable(EditorBrowsableState.Never)]
internal static class IsExternalInit
{
}
}

View file

@ -21,19 +21,7 @@ namespace Elsa.Models
public IDictionary<string, object?> Data { get; set; }
public object? Get(string name) => Has(name) ? Data[name] : default;
public T Get<T>(string name)
{
if (!Has(name))
return default!;
var value = Get(name);
if (value == null)
return default!;
return (T)value!;
}
public T? Get<T>(string name) => !Has(name) ? default : Get(name).ConvertTo<T>();
public Variables Set(string name, object? value)
{

View file

@ -58,7 +58,7 @@ namespace Elsa.Services.Models
return await activityTypeService.ActivateActivityAsync(ActivityBlueprint, cancellationToken);
}
public T GetInput<T>() => (T) Input!;
public T? GetInput<T>() => Input.ConvertTo<T>();
public object? GetOutputFrom(string activityName) => WorkflowExecutionContext.GetOutputFrom(activityName);
public T GetOutputFrom<T>(string activityName) => (T) GetOutputFrom(activityName)!;
public void SetWorkflowContext(object? value) => WorkflowExecutionContext.SetWorkflowContext(value);

View file

@ -8,7 +8,6 @@ namespace Elsa.Services.Models
string? TenantId { get; }
bool IsSingleton { get; }
bool IsEnabled { get; }
string? Description { get; }
bool IsPublished { get; }
bool IsLatest { get; }

View file

@ -0,0 +1,34 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Services;
using Elsa.Services.Models;
// ReSharper disable once CheckNamespace
namespace Elsa.Activities.ControlFlow
{
[ActivityAttribute(
DisplayName = "Switch",
Category = "Control Flow",
Description = "Evaluate multiple conditions and continue execution depending on the results.",
Outcomes = new[] { OutcomeNames.Done }
)]
public class Switch : Activity
{
[ActivityProperty(Hint = "The conditions to evaluate.")]
public ICollection<SwitchCase> Cases { get; set; } = new List<SwitchCase>();
[ActivityProperty(Hint = "The conditions to evaluate.")]
public SwitchMode Mode { get; set; } = SwitchMode.MatchFirst;
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{
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 = new[] { OutcomeNames.Done }.Concat(results);
return Outcomes(outcomes);
}
}
}

View file

@ -0,0 +1,51 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Elsa.Builders;
using Elsa.Services.Models;
// ReSharper disable once CheckNamespace
namespace Elsa.Activities.ControlFlow
{
public static class SwitchBuilderExtensions
{
public static IActivityBuilder Switch(this IBuilder builder, Action<ISetupActivity<Switch>>? setup = default, Action<IActivityBuilder>? activity = default)
{
var activityBuilder = builder.Then(setup);
activity?.Invoke(activityBuilder);
return activityBuilder;
}
public static IActivityBuilder Switch(this IBuilder builder, Func<ActivityExecutionContext, ValueTask<ICollection<SwitchCase>>> cases, SwitchMode mode, Action<IActivityBuilder>? activity = default) => builder.Switch(x => x.WithCases(cases).WithMode(mode), activity);
public static IActivityBuilder Switch(this IBuilder builder, Func<ActivityExecutionContext, ValueTask<ICollection<SwitchCase>>> cases, Action<IActivityBuilder>? activity = default) => builder.Switch(x => x.WithCases(cases), activity);
public static IActivityBuilder Switch(this IBuilder builder, Func<ActivityExecutionContext, ICollection<SwitchCase>> cases, SwitchMode mode, Action<IActivityBuilder>? activity = default) => builder.Switch(x => x.WithCases(cases).WithMode(mode), activity);
public static IActivityBuilder Switch(this IBuilder builder, Func<ActivityExecutionContext, ICollection<SwitchCase>> cases, Action<IActivityBuilder>? activity = default) => builder.Switch(x => x.WithCases(cases), activity);
public static IActivityBuilder Switch(this IBuilder builder, Func<ICollection<SwitchCase>> cases, SwitchMode mode, Action<IActivityBuilder>? activity = default) => builder.Switch(x => x.WithCases(cases).WithMode(mode), activity);
public static IActivityBuilder Switch(this IBuilder builder, Func<ICollection<SwitchCase>> cases, Action<IActivityBuilder>? activity = default) => builder.Switch(x => x.WithCases(cases), activity);
public static IActivityBuilder Switch(this IBuilder builder, ICollection<SwitchCase> cases, SwitchMode mode, Action<IActivityBuilder>? activity = default) => builder.Switch(x => x.WithCases(cases).WithMode(mode), activity);
public static IActivityBuilder Switch(this IBuilder builder, ICollection<SwitchCase> cases, Action<IActivityBuilder>? activity = default) => builder.Switch(x => x.WithCases(cases), activity);
public static IActivityBuilder Switch(this IBuilder builder, Action<SwitchCaseBuilder> cases, Action<IActivityBuilder>? activity = default) => builder.Switch(cases, SwitchMode.MatchFirst, activity);
public static IActivityBuilder Switch(this IBuilder builder, Action<SwitchCaseBuilder> cases, SwitchMode mode, Action<IActivityBuilder>? activity = default)
{
return builder.Switch(x =>
{
cases(x);
return new ValueTask();
}, mode, activity);
}
public static IActivityBuilder Switch(this IBuilder builder, Func<SwitchCaseBuilder, ValueTask> cases, Action<IActivityBuilder>? activity = default) => builder.Switch(cases, SwitchMode.MatchFirst, activity);
public static IActivityBuilder Switch(this IBuilder builder, Func<SwitchCaseBuilder, ValueTask> cases, SwitchMode mode, Action<IActivityBuilder>? activity = default)
{
return builder.Switch(async context =>
{
var switchCaseBuilder = new SwitchCaseBuilder(context);
await cases(switchCaseBuilder);
return switchCaseBuilder.Cases;
}, mode, activity);
}
}
}

View file

@ -0,0 +1,4 @@
namespace Elsa.Activities.ControlFlow
{
public record SwitchCase(string Name, bool Condition);
}

View file

@ -0,0 +1,18 @@
using System.Collections.Generic;
using Elsa.Services.Models;
namespace Elsa.Activities.ControlFlow
{
public class SwitchCaseBuilder
{
public ActivityExecutionContext Context { get; }
public ICollection<SwitchCase> Cases { get; } = new List<SwitchCase>();
public SwitchCaseBuilder(ActivityExecutionContext context) => Context = context;
public SwitchCaseBuilder Add(string name, bool condition)
{
Cases.Add(new SwitchCase(name, condition));
return this;
}
}
}

View file

@ -0,0 +1,18 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Elsa.Builders;
using Elsa.Services.Models;
// ReSharper disable once CheckNamespace
namespace Elsa.Activities.ControlFlow
{
public static class SwitchExtensions
{
public static ISetupActivity<Switch> WithCases(this ISetupActivity<Switch> activity, Func<ActivityExecutionContext, ValueTask<ICollection<SwitchCase>>> value) => activity.Set(x => x.Cases, value!);
public static ISetupActivity<Switch> WithCases(this ISetupActivity<Switch> activity, Func<ActivityExecutionContext, ICollection<SwitchCase>> value) => activity.Set(x => x.Cases, value!);
public static ISetupActivity<Switch> WithCases(this ISetupActivity<Switch> activity, Func<ICollection<SwitchCase>> value) => activity.Set(x => x.Cases, value!);
public static ISetupActivity<Switch> WithCases(this ISetupActivity<Switch> activity, ICollection<SwitchCase> value) => activity.Set(x => x.Cases, value!);
public static ISetupActivity<Switch> WithMode(this ISetupActivity<Switch> activity, SwitchMode value) => activity.Set(x => x.Mode, value);
}
}

View file

@ -0,0 +1,15 @@
namespace Elsa.Activities.ControlFlow
{
public enum SwitchMode
{
/// <summary>
/// Yields the outcome of the first condition evaluating to true.
/// </summary>
MatchFirst,
/// <summary>
/// Yields the outcome of all conditions evaluating to true.
/// </summary>
MatchAny
}
}

View file

@ -1,48 +0,0 @@
using System;
using System.Collections.Generic;
using Elsa.ActivityResults;
using Elsa.Attributes;
using Elsa.Services;
using Elsa.Services.Models;
// ReSharper disable once CheckNamespace
namespace Elsa.Activities.ControlFlow
{
[Activity(
Category = "Control Flow",
Description = "Switch execution based on a given expression."
)]
public class Switch : Activity
{
public const string DefaultOutcome = "Default";
public Switch()
{
Cases = new HashSet<string>()
{
OutcomeNames.Default
};
}
[ActivityProperty(Hint = "The value to switch on.")]
public string Value { get; set; } = default!;
private HashSet<string> _cases = default!;
[ActivityProperty(Hint = "A comma-separated list of possible outcomes of the expression.")]
public HashSet<string> Cases
{
get => _cases;
set => _cases = new HashSet<string>(value, StringComparer.OrdinalIgnoreCase);
}
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
{
var result = Value;
var outcome = ContainsCase(result) ? result : DefaultOutcome;
return Outcome(outcome, result);
}
private bool ContainsCase(string @case) => Cases.Contains(@case);
}
}

View file

@ -1,20 +0,0 @@
using System;
using System.Collections.Generic;
using Elsa.Builders;
using Elsa.Services.Models;
// ReSharper disable once CheckNamespace
namespace Elsa.Activities.ControlFlow
{
public static class SwitchExtensions
{
public static ISetupActivity<Switch> WithCases(this ISetupActivity<Switch> activity, Func<ActivityExecutionContext, IEnumerable<string>> value) => activity.Set(x => x.Cases, value);
public static ISetupActivity<Switch> WithCases(this ISetupActivity<Switch> activity, Func<IEnumerable<string>> value) => activity.Set(x => x.Cases, value);
public static ISetupActivity<Switch> WithCases(this ISetupActivity<Switch> activity, IEnumerable<string> value) => activity.Set(x => x.Cases, value);
public static ISetupActivity<Switch> WithCases(this ISetupActivity<Switch> activity, params string[] value) => activity.Set(x => x.Cases, new HashSet<string>(value));
public static ISetupActivity<Switch> WithValue(this ISetupActivity<Switch> activity, Func<ActivityExecutionContext, string> value) => activity.Set(x => x.Value, value);
public static ISetupActivity<Switch> WithValue(this ISetupActivity<Switch> activity, Func<string> value) => activity.Set(x => x.Value, value);
public static ISetupActivity<Switch> WithValue(this ISetupActivity<Switch> activity, string value) => activity.Set(x => x.Value, value);
}
}

View file

@ -8,17 +8,17 @@ namespace Elsa.Activities.Signaling
{
public static class InterruptTriggerExtensions
{
public static ISetupActivity<InterruptTrigger> WithWorkflowInstanceId(this ISetupActivity<InterruptTrigger> activity, Func<ActivityExecutionContext, ValueTask<string>> value) => activity.Set(x => x.WorkflowInstanceId, value);
public static ISetupActivity<InterruptTrigger> WithWorkflowInstanceId(this ISetupActivity<InterruptTrigger> activity, Func<ActivityExecutionContext, string> value) => activity.Set(x => x.WorkflowInstanceId, value);
public static ISetupActivity<InterruptTrigger> WithWorkflowInstanceId(this ISetupActivity<InterruptTrigger> activity, Func<ValueTask<string>> value) => activity.Set(x => x.WorkflowInstanceId, value);
public static ISetupActivity<InterruptTrigger> WithWorkflowInstanceId(this ISetupActivity<InterruptTrigger> activity, Func<string> value) => activity.Set(x => x.WorkflowInstanceId, value);
public static ISetupActivity<InterruptTrigger> WithWorkflowInstanceId(this ISetupActivity<InterruptTrigger> activity, string value) => activity.Set(x => x.WorkflowInstanceId, value);
public static ISetupActivity<InterruptTrigger> WithWorkflowInstanceId(this ISetupActivity<InterruptTrigger> activity, Func<ActivityExecutionContext, ValueTask<string?>> value) => activity.Set(x => x.WorkflowInstanceId, value);
public static ISetupActivity<InterruptTrigger> WithWorkflowInstanceId(this ISetupActivity<InterruptTrigger> activity, Func<ActivityExecutionContext, string?> value) => activity.Set(x => x.WorkflowInstanceId, value);
public static ISetupActivity<InterruptTrigger> WithWorkflowInstanceId(this ISetupActivity<InterruptTrigger> activity, Func<ValueTask<string?>> value) => activity.Set(x => x.WorkflowInstanceId, value);
public static ISetupActivity<InterruptTrigger> WithWorkflowInstanceId(this ISetupActivity<InterruptTrigger> activity, Func<string?> value) => activity.Set(x => x.WorkflowInstanceId, value);
public static ISetupActivity<InterruptTrigger> WithWorkflowInstanceId(this ISetupActivity<InterruptTrigger> activity, string? value) => activity.Set(x => x.WorkflowInstanceId, value);
public static ISetupActivity<InterruptTrigger> WithBlockingActivityId(this ISetupActivity<InterruptTrigger> activity, Func<ActivityExecutionContext, ValueTask<string>> value) => activity.Set(x => x.BlockingActivityId, value);
public static ISetupActivity<InterruptTrigger> WithBlockingActivityId(this ISetupActivity<InterruptTrigger> activity, Func<ActivityExecutionContext, string> value) => activity.Set(x => x.BlockingActivityId, value);
public static ISetupActivity<InterruptTrigger> WithBlockingActivityId(this ISetupActivity<InterruptTrigger> activity, Func<ValueTask<string>> value) => activity.Set(x => x.BlockingActivityId, value);
public static ISetupActivity<InterruptTrigger> WithBlockingActivityId(this ISetupActivity<InterruptTrigger> activity, Func<string> value) => activity.Set(x => x.BlockingActivityId, value);
public static ISetupActivity<InterruptTrigger> WithBlockingActivityId(this ISetupActivity<InterruptTrigger> activity, string value) => activity.Set(x => x.BlockingActivityId, value);
public static ISetupActivity<InterruptTrigger> WithBlockingActivityId(this ISetupActivity<InterruptTrigger> activity, Func<ActivityExecutionContext, ValueTask<string?>> value) => activity.Set(x => x.BlockingActivityId, value);
public static ISetupActivity<InterruptTrigger> WithBlockingActivityId(this ISetupActivity<InterruptTrigger> activity, Func<ActivityExecutionContext, string?> value) => activity.Set(x => x.BlockingActivityId, value);
public static ISetupActivity<InterruptTrigger> WithBlockingActivityId(this ISetupActivity<InterruptTrigger> activity, Func<ValueTask<string?>> value) => activity.Set(x => x.BlockingActivityId, value);
public static ISetupActivity<InterruptTrigger> WithBlockingActivityId(this ISetupActivity<InterruptTrigger> activity, Func<string?> value) => activity.Set(x => x.BlockingActivityId, value);
public static ISetupActivity<InterruptTrigger> WithBlockingActivityId(this ISetupActivity<InterruptTrigger> activity, string? value) => activity.Set(x => x.BlockingActivityId, value);
public static ISetupActivity<InterruptTrigger> WithInput(this ISetupActivity<InterruptTrigger> activity, Func<ActivityExecutionContext, ValueTask<object?>> value) => activity.Set(x => x.Input, value);
public static ISetupActivity<InterruptTrigger> WithInput(this ISetupActivity<InterruptTrigger> activity, Func<ActivityExecutionContext, object?> value) => activity.Set(x => x.Input, value);

View file

@ -45,13 +45,13 @@ namespace Elsa.Builders
return this;
}
public IWorkflowBuilder WithDisplayName(string value)
public new IWorkflowBuilder WithDisplayName(string value)
{
DisplayName = value;
return this;
}
public IWorkflowBuilder WithDescription(string? value)
public new IWorkflowBuilder WithDescription(string? value)
{
Description = value;
return this;

View file

@ -1,5 +1,7 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Ccondition/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cfor/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Ccontrolflow_005Cifthen/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Cprimitives_005Csetname/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Csignaling_005Cactivities_005Creceivesignal/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/CodeInspection/NamespaceProvider/NamespaceFoldersToSkip/=activities_005Csignaling_005Creceivesignal/@EntryIndexedValue">True</s:Boolean>

View file

@ -138,6 +138,7 @@ namespace Microsoft.Extensions.DependencyInjection
.AddActivity<ParallelForEach>()
.AddActivity<Fork>()
.AddActivity<IfElse>()
.AddActivity<Switch>()
.AddActivity<Join>()
.AddActivity<Switch>()
.AddActivity<While>()

View file

@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework>
<Nullable>enable</Nullable>
<IsPackable>false</IsPackable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\core\Elsa\Elsa.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,47 @@
using System;
using Elsa.Activities.Console;
using Elsa.Activities.ControlFlow;
using Elsa.Builders;
namespace Elsa.Samples.SwitchConsole
{
public class GrayscaleWorkflow : IWorkflow
{
private readonly Random _random;
public GrayscaleWorkflow()
{
_random = new Random();
}
public void Build(IWorkflowBuilder workflow)
{
workflow
.WriteLine("--Grayscale Calculator--", "Start")
.WriteLine("Enter a number between 0 and 100.")
.ReadLine()
.Switch(cases =>
{
var input = cases.Context.GetInput<int>();
cases
.Add("Black", input >= 0 && input < 20)
.Add("Dark Gray", input >= 20 && input < 50)
.Add("Light Gray", input >= 50 && input < 70)
.Add("Gray", input >= 20 && input < 70)
.Add("White", input >= 70 && input <= 100)
.Add("Invalid", input < 0 || input > 100);
},
SwitchMode.MatchFirst,
ifThen =>
{
ifThen.When("Black").WriteLine("That number is black");
ifThen.When("Dark Gray").WriteLine("That number is dark gray");
ifThen.When("Light Gray").WriteLine("That number is light gray");
ifThen.When("Gray").WriteLine("That number is gray");
ifThen.When("White").WriteLine("That number is white");
ifThen.When("Invalid").WriteLine("That number is invalid");
})
.WriteLine("Goodbye");
}
}
}

View file

@ -0,0 +1,32 @@
using System.Threading.Tasks;
using Elsa.Services;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Samples.SwitchConsole
{
/// <summary>
/// Demonstrates a workflow with a While looping construct.
/// </summary>
static class Program
{
private static async Task Main()
{
// Create a service container with Elsa services.
var services = new ServiceCollection()
.AddElsa(options => options
.AddConsoleActivities()
.AddWorkflow<GrayscaleWorkflow>())
.BuildServiceProvider();
// Run startup actions (not needed when registering Elsa with a Host).
var startupRunner = services.GetRequiredService<IStartupRunner>();
await startupRunner.StartupAsync();
// Get a workflow runner.
var workflowRunner = services.GetRequiredService<IWorkflowRunner>();
// Execute the workflow.
await workflowRunner.RunWorkflowAsync<GrayscaleWorkflow>();
}
}
}

View file

@ -2,7 +2,7 @@
using Elsa.Activities.Http;
using Elsa.Builders;
namespace Elsa.Samples.Server.Host.Workflows
namespace ElsaDashboard.Samples.Monolith.Workflows
{
public class GoodbyeWorld : IWorkflow
{

View file

@ -2,7 +2,7 @@
using Elsa.Activities.Http;
using Elsa.Builders;
namespace Elsa.Samples.Server.Host.Workflows
namespace ElsaDashboard.Samples.Monolith.Workflows
{
public class HelloWorld : IWorkflow
{

View file

@ -2,7 +2,7 @@
using Elsa.Activities.Http;
using Elsa.Builders;
namespace Elsa.Samples.Server.Host.Workflows
namespace ElsaDashboard.Samples.Monolith.Workflows
{
public class HelloWorldV2 : IWorkflow
{

View file

@ -7,7 +7,7 @@ using Elsa.Activities.Primitives;
using Elsa.Builders;
using Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Http;
namespace Elsa.Samples.Server.Host.Workflows
namespace ElsaDashboard.Samples.Monolith.Workflows
{
public class NamingWorkflow : IWorkflow
{
@ -19,7 +19,7 @@ namespace Elsa.Samples.Server.Host.Workflows
.Correlate(() => Guid.NewGuid().ToString("N"))
.WriteHttpResponse(x => x.WithStatusCode(HttpStatusCode.OK).WithContent(context => $"Tell me your name please. Use correlation ID {context.WorkflowExecutionContext.CorrelationId}").WithContentType("text/plain"))
.HttpRequestReceived(x => x.WithPath("/signup").WithMethod(HttpMethod.Post.ToString()).WithReadContent())
.SetVariable("Name", context => (string) context.GetInput<HttpRequestModel>().Body)
.SetVariable("Name", context => (string?) context.GetInput<HttpRequestModel>().Body)
.SetName(context => context.GetVariable<string>("Name"))
.WriteHttpResponse(x => x

View file

@ -9,7 +9,6 @@ namespace Elsa.Server.Api.Endpoints.WorkflowRegistry
public string? TenantId { get; set; }
public bool IsSingleton { get; set; }
public bool IsEnabled { get; set; }
public string? Description { get; set; }
public bool IsPublished { get; set; }
public bool IsLatest { get; set; }
public Variables Variables { get; set; } = new();
@ -17,7 +16,6 @@ namespace Elsa.Server.Api.Endpoints.WorkflowRegistry
public WorkflowPersistenceBehavior PersistenceBehavior { get; set; }
public bool DeleteCompletedInstances { get; set; }
public Variables CustomAttributes { get; set; } = new();
public string? DisplayName { get; set; }
}
public class CompositeActivityBlueprintModel : ActivityBlueprintModel

View file

@ -1,35 +0,0 @@
using Elsa.Activities.Console;
using Elsa.Activities.ControlFlow;
using Elsa.Builders;
namespace Elsa.Core.IntegrationTests.Workflows
{
public class SwitchWorkflow : IWorkflow
{
public void Build(IWorkflowBuilder workflow)
{
workflow
.WriteLine("Start")
.Then<Switch>(
@switch => @switch.WithCases("Case 1", "Case 2", "Case 3").WithValue(context => (string)context.WorkflowExecutionContext.Input!),
@switch =>
{
@switch
.When("Case 1")
.WriteLine("Case 1 executed", id: "WriteLine1");
@switch
.When("Case 2")
.WriteLine("Case 2 executed", id: "WriteLine2");
@switch
.When("Case 3")
.WriteLine("Case 3 executed", id: "WriteLine3");
@switch
.When("Default")
.WriteLine("Default case executed", id: "WriteLineDefault");
});
}
}
}

View file

@ -1,28 +0,0 @@
// using System.Linq;
// using System.Threading.Tasks;
// using Elsa.Testing.Shared.Unit;
// using Xunit;
// using Xunit.Abstractions;
//
// namespace Elsa.Core.IntegrationTests.Workflows
// {
// public class SwitchWorkflowTests : WorkflowsUnitTestBase
// {
// public SwitchWorkflowTests(ITestOutputHelper testOutputHelper) : base(testOutputHelper)
// {
// }
//
// [Theory(DisplayName = "Runs Switch")]
// [InlineData("Case 1", "WriteLine1")]
// [InlineData("Case 2", "WriteLine2")]
// [InlineData("Case 3", "WriteLine3")]
// [InlineData("Different Case", "WriteLineDefault")]
// public async Task Test01(string input, string expectedActivityId)
// {
// var workflowInstance = await WorkflowRunner.RunWorkflowAsync<SwitchWorkflow>(input: input);
// var executedActivityIds = workflowInstance.ExecutionLog.Select(x => x.ActivityId).ToList();
//
// Assert.Contains(expectedActivityId, executedActivityIds);
// }
// }
// }