diff --git a/Elsa.sln b/Elsa.sln index 7cdbb8e6f..7a15d64d0 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -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} diff --git a/src/activities/Elsa.Activities.Timers/HostedServices/StartJobs.cs b/src/activities/Elsa.Activities.Timers/HostedServices/StartJobs.cs index cdbeb1116..fdd405228 100644 --- a/src/activities/Elsa.Activities.Timers/HostedServices/StartJobs.cs +++ b/src/activities/Elsa.Activities.Timers/HostedServices/StartJobs.cs @@ -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(activity.Id); + var timerWrapper = workflowWrapper.GetActivity(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(activity.Id); + var timerEventWrapper = workflowWrapper.GetActivity(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(activity.Id); + var timerEventWrapper = workflowWrapper.GetActivity(activity.Id)!; var cronExpression = await timerEventWrapper.GetPropertyValueAsync(x => x.CronExpression, cancellationToken); await _workflowScheduler.ScheduleWorkflowAsync(workflow, activity.Id, cronExpression!, cancellationToken); diff --git a/src/clients/Elsa.Client/Models/WorkflowBlueprint.cs b/src/clients/Elsa.Client/Models/WorkflowBlueprint.cs index c62f5dca7..4e1415327 100644 --- a/src/clients/Elsa.Client/Models/WorkflowBlueprint.cs +++ b/src/clients/Elsa.Client/Models/WorkflowBlueprint.cs @@ -32,7 +32,5 @@ namespace Elsa.Client.Models /// [DataMember(Order = 11)] public Variables CustomAttributes { get; set;} = new(); - - [DataMember(Order = 12)] public string? DisplayName { get; set; } } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/ActivityResults/OutcomeResult.cs b/src/core/Elsa.Abstractions/ActivityResults/OutcomeResult.cs index 85235ebfc..5ed308fc7 100644 --- a/src/core/Elsa.Abstractions/ActivityResults/OutcomeResult.cs +++ b/src/core/Elsa.Abstractions/ActivityResults/OutcomeResult.cs @@ -16,14 +16,14 @@ namespace Elsa.ActivityResults Outcomes = outcomeList; } - + public IEnumerable 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 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(); diff --git a/src/core/Elsa.Abstractions/AssemblyInfo.cs b/src/core/Elsa.Abstractions/AssemblyInfo.cs index a4a0442ee..191cb0f82 100644 --- a/src/core/Elsa.Abstractions/AssemblyInfo.cs +++ b/src/core/Elsa.Abstractions/AssemblyInfo.cs @@ -1,3 +1,3 @@ using System.Runtime.CompilerServices; -[assembly: InternalsVisibleTo("Elsa.Core")] \ No newline at end of file +[assembly: InternalsVisibleTo("Elsa.Core")] diff --git a/src/core/Elsa.Abstractions/Builders/ISetupActivity.cs b/src/core/Elsa.Abstractions/Builders/ISetupActivity.cs index c1116e1c2..3c780f2b5 100644 --- a/src/core/Elsa.Abstractions/Builders/ISetupActivity.cs +++ b/src/core/Elsa.Abstractions/Builders/ISetupActivity.cs @@ -27,13 +27,13 @@ namespace Elsa.Builders this ISetupActivity builder, Expression> propertyAccessor, Func valueFactory) where T : IActivity => - builder.Set(propertyAccessor, context => new ValueTask(valueFactory(context))); + builder.Set(propertyAccessor, context => new ValueTask(valueFactory(context))); public static ISetupActivity Set( this ISetupActivity builder, Expression> propertyAccessor, Func valueFactory) where T : IActivity => - builder.Set(propertyAccessor, context => new ValueTask(valueFactory())); + builder.Set(propertyAccessor, context => new ValueTask(valueFactory())); public static ISetupActivity Set( this ISetupActivity builder, diff --git a/src/core/Elsa.Abstractions/Builders/IWorkflowBuilder.cs b/src/core/Elsa.Abstractions/Builders/IWorkflowBuilder.cs index bfdb01180..4aef63d37 100644 --- a/src/core/Elsa.Abstractions/Builders/IWorkflowBuilder.cs +++ b/src/core/Elsa.Abstractions/Builders/IWorkflowBuilder.cs @@ -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(); diff --git a/src/core/Elsa.Abstractions/Extensions/ObjectConverter.cs b/src/core/Elsa.Abstractions/Extensions/ObjectConverter.cs new file mode 100644 index 000000000..235238475 --- /dev/null +++ b/src/core/Elsa.Abstractions/Extensions/ObjectConverter.cs @@ -0,0 +1,27 @@ +using System.ComponentModel; +using NodaTime; +using NodaTime.Text; + +namespace Elsa +{ + public static class ObjectConverter + { + public static T? ConvertTo(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; + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/IsExternalInit.cs b/src/core/Elsa.Abstractions/IsExternalInit.cs new file mode 100644 index 000000000..475dd51b6 --- /dev/null +++ b/src/core/Elsa.Abstractions/IsExternalInit.cs @@ -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 +{ + /// + /// Reserved to be used by the compiler for tracking metadata. + /// This class should not be used by developers in source code. + /// + [EditorBrowsable(EditorBrowsableState.Never)] + internal static class IsExternalInit + { + } +} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Models/Variables.cs b/src/core/Elsa.Abstractions/Models/Variables.cs index 72fc38ddb..95b25b600 100644 --- a/src/core/Elsa.Abstractions/Models/Variables.cs +++ b/src/core/Elsa.Abstractions/Models/Variables.cs @@ -21,19 +21,7 @@ namespace Elsa.Models public IDictionary Data { get; set; } public object? Get(string name) => Has(name) ? Data[name] : default; - - public T Get(string name) - { - if (!Has(name)) - return default!; - - var value = Get(name); - - if (value == null) - return default!; - - return (T)value!; - } + public T? Get(string name) => !Has(name) ? default : Get(name).ConvertTo(); public Variables Set(string name, object? value) { diff --git a/src/core/Elsa.Abstractions/Services/Models/ActivityExecutionContext.cs b/src/core/Elsa.Abstractions/Services/Models/ActivityExecutionContext.cs index 9608fbeaa..791766648 100644 --- a/src/core/Elsa.Abstractions/Services/Models/ActivityExecutionContext.cs +++ b/src/core/Elsa.Abstractions/Services/Models/ActivityExecutionContext.cs @@ -58,7 +58,7 @@ namespace Elsa.Services.Models return await activityTypeService.ActivateActivityAsync(ActivityBlueprint, cancellationToken); } - public T GetInput() => (T) Input!; + public T? GetInput() => Input.ConvertTo(); public object? GetOutputFrom(string activityName) => WorkflowExecutionContext.GetOutputFrom(activityName); public T GetOutputFrom(string activityName) => (T) GetOutputFrom(activityName)!; public void SetWorkflowContext(object? value) => WorkflowExecutionContext.SetWorkflowContext(value); diff --git a/src/core/Elsa.Abstractions/Services/Models/IWorkflowBlueprint.cs b/src/core/Elsa.Abstractions/Services/Models/IWorkflowBlueprint.cs index c13542446..6afa22af7 100644 --- a/src/core/Elsa.Abstractions/Services/Models/IWorkflowBlueprint.cs +++ b/src/core/Elsa.Abstractions/Services/Models/IWorkflowBlueprint.cs @@ -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; } diff --git a/src/core/Elsa.Core/Activities/ControlFlow/IfThen/Switch.cs b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/Switch.cs new file mode 100644 index 000000000..0a3c5a85e --- /dev/null +++ b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/Switch.cs @@ -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 Cases { get; set; } = new List(); + + [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); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchBuilderExtensions.cs b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchBuilderExtensions.cs new file mode 100644 index 000000000..8aefce1f6 --- /dev/null +++ b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchBuilderExtensions.cs @@ -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>? setup = default, Action? activity = default) + { + var activityBuilder = builder.Then(setup); + activity?.Invoke(activityBuilder); + return activityBuilder; + } + + public static IActivityBuilder Switch(this IBuilder builder, Func>> cases, SwitchMode mode, Action? activity = default) => builder.Switch(x => x.WithCases(cases).WithMode(mode), activity); + public static IActivityBuilder Switch(this IBuilder builder, Func>> cases, Action? activity = default) => builder.Switch(x => x.WithCases(cases), activity); + public static IActivityBuilder Switch(this IBuilder builder, Func> cases, SwitchMode mode, Action? activity = default) => builder.Switch(x => x.WithCases(cases).WithMode(mode), activity); + public static IActivityBuilder Switch(this IBuilder builder, Func> cases, Action? activity = default) => builder.Switch(x => x.WithCases(cases), activity); + public static IActivityBuilder Switch(this IBuilder builder, Func> cases, SwitchMode mode, Action? activity = default) => builder.Switch(x => x.WithCases(cases).WithMode(mode), activity); + public static IActivityBuilder Switch(this IBuilder builder, Func> cases, Action? activity = default) => builder.Switch(x => x.WithCases(cases), activity); + public static IActivityBuilder Switch(this IBuilder builder, ICollection cases, SwitchMode mode, Action? activity = default) => builder.Switch(x => x.WithCases(cases).WithMode(mode), activity); + public static IActivityBuilder Switch(this IBuilder builder, ICollection cases, Action? activity = default) => builder.Switch(x => x.WithCases(cases), activity); + + public static IActivityBuilder Switch(this IBuilder builder, Action cases, Action? activity = default) => builder.Switch(cases, SwitchMode.MatchFirst, activity); + + public static IActivityBuilder Switch(this IBuilder builder, Action cases, SwitchMode mode, Action? activity = default) + { + return builder.Switch(x => + { + cases(x); + return new ValueTask(); + }, mode, activity); + } + + public static IActivityBuilder Switch(this IBuilder builder, Func cases, Action? activity = default) => builder.Switch(cases, SwitchMode.MatchFirst, activity); + + public static IActivityBuilder Switch(this IBuilder builder, Func cases, SwitchMode mode, Action? activity = default) + { + return builder.Switch(async context => + { + var switchCaseBuilder = new SwitchCaseBuilder(context); + await cases(switchCaseBuilder); + return switchCaseBuilder.Cases; + }, mode, activity); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchCase.cs b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchCase.cs new file mode 100644 index 000000000..875d9448b --- /dev/null +++ b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchCase.cs @@ -0,0 +1,4 @@ +namespace Elsa.Activities.ControlFlow +{ + public record SwitchCase(string Name, bool Condition); +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchCaseBuilder.cs b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchCaseBuilder.cs new file mode 100644 index 000000000..5177321c0 --- /dev/null +++ b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchCaseBuilder.cs @@ -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 Cases { get; } = new List(); + public SwitchCaseBuilder(ActivityExecutionContext context) => Context = context; + + public SwitchCaseBuilder Add(string name, bool condition) + { + Cases.Add(new SwitchCase(name, condition)); + return this; + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchExtensions.cs b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchExtensions.cs new file mode 100644 index 000000000..06c0eae72 --- /dev/null +++ b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchExtensions.cs @@ -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 WithCases(this ISetupActivity activity, Func>> value) => activity.Set(x => x.Cases, value!); + public static ISetupActivity WithCases(this ISetupActivity activity, Func> value) => activity.Set(x => x.Cases, value!); + public static ISetupActivity WithCases(this ISetupActivity activity, Func> value) => activity.Set(x => x.Cases, value!); + public static ISetupActivity WithCases(this ISetupActivity activity, ICollection value) => activity.Set(x => x.Cases, value!); + public static ISetupActivity WithMode(this ISetupActivity activity, SwitchMode value) => activity.Set(x => x.Mode, value); + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchMode.cs b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchMode.cs new file mode 100644 index 000000000..0be121250 --- /dev/null +++ b/src/core/Elsa.Core/Activities/ControlFlow/IfThen/SwitchMode.cs @@ -0,0 +1,15 @@ +namespace Elsa.Activities.ControlFlow +{ + public enum SwitchMode + { + /// + /// Yields the outcome of the first condition evaluating to true. + /// + MatchFirst, + + /// + /// Yields the outcome of all conditions evaluating to true. + /// + MatchAny + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Switch/Switch.cs b/src/core/Elsa.Core/Activities/ControlFlow/Switch/Switch.cs deleted file mode 100644 index ea4194fd7..000000000 --- a/src/core/Elsa.Core/Activities/ControlFlow/Switch/Switch.cs +++ /dev/null @@ -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() - { - OutcomeNames.Default - }; - } - - [ActivityProperty(Hint = "The value to switch on.")] - public string Value { get; set; } = default!; - - private HashSet _cases = default!; - - [ActivityProperty(Hint = "A comma-separated list of possible outcomes of the expression.")] - public HashSet Cases - { - get => _cases; - set => _cases = new HashSet(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); - } -} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Switch/SwitchExtensions.cs b/src/core/Elsa.Core/Activities/ControlFlow/Switch/SwitchExtensions.cs deleted file mode 100644 index 554895a9c..000000000 --- a/src/core/Elsa.Core/Activities/ControlFlow/Switch/SwitchExtensions.cs +++ /dev/null @@ -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 WithCases(this ISetupActivity activity, Func> value) => activity.Set(x => x.Cases, value); - public static ISetupActivity WithCases(this ISetupActivity activity, Func> value) => activity.Set(x => x.Cases, value); - public static ISetupActivity WithCases(this ISetupActivity activity, IEnumerable value) => activity.Set(x => x.Cases, value); - public static ISetupActivity WithCases(this ISetupActivity activity, params string[] value) => activity.Set(x => x.Cases, new HashSet(value)); - - public static ISetupActivity WithValue(this ISetupActivity activity, Func value) => activity.Set(x => x.Value, value); - public static ISetupActivity WithValue(this ISetupActivity activity, Func value) => activity.Set(x => x.Value, value); - public static ISetupActivity WithValue(this ISetupActivity activity, string value) => activity.Set(x => x.Value, value); - } -} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/Signaling/Activities/InterruptTrigger/InterruptTriggerExtensions.cs b/src/core/Elsa.Core/Activities/Signaling/Activities/InterruptTrigger/InterruptTriggerExtensions.cs index 367a4d344..af99863cd 100644 --- a/src/core/Elsa.Core/Activities/Signaling/Activities/InterruptTrigger/InterruptTriggerExtensions.cs +++ b/src/core/Elsa.Core/Activities/Signaling/Activities/InterruptTrigger/InterruptTriggerExtensions.cs @@ -8,17 +8,17 @@ namespace Elsa.Activities.Signaling { public static class InterruptTriggerExtensions { - public static ISetupActivity WithWorkflowInstanceId(this ISetupActivity activity, Func> value) => activity.Set(x => x.WorkflowInstanceId, value); - public static ISetupActivity WithWorkflowInstanceId(this ISetupActivity activity, Func value) => activity.Set(x => x.WorkflowInstanceId, value); - public static ISetupActivity WithWorkflowInstanceId(this ISetupActivity activity, Func> value) => activity.Set(x => x.WorkflowInstanceId, value); - public static ISetupActivity WithWorkflowInstanceId(this ISetupActivity activity, Func value) => activity.Set(x => x.WorkflowInstanceId, value); - public static ISetupActivity WithWorkflowInstanceId(this ISetupActivity activity, string value) => activity.Set(x => x.WorkflowInstanceId, value); + public static ISetupActivity WithWorkflowInstanceId(this ISetupActivity activity, Func> value) => activity.Set(x => x.WorkflowInstanceId, value); + public static ISetupActivity WithWorkflowInstanceId(this ISetupActivity activity, Func value) => activity.Set(x => x.WorkflowInstanceId, value); + public static ISetupActivity WithWorkflowInstanceId(this ISetupActivity activity, Func> value) => activity.Set(x => x.WorkflowInstanceId, value); + public static ISetupActivity WithWorkflowInstanceId(this ISetupActivity activity, Func value) => activity.Set(x => x.WorkflowInstanceId, value); + public static ISetupActivity WithWorkflowInstanceId(this ISetupActivity activity, string? value) => activity.Set(x => x.WorkflowInstanceId, value); - public static ISetupActivity WithBlockingActivityId(this ISetupActivity activity, Func> value) => activity.Set(x => x.BlockingActivityId, value); - public static ISetupActivity WithBlockingActivityId(this ISetupActivity activity, Func value) => activity.Set(x => x.BlockingActivityId, value); - public static ISetupActivity WithBlockingActivityId(this ISetupActivity activity, Func> value) => activity.Set(x => x.BlockingActivityId, value); - public static ISetupActivity WithBlockingActivityId(this ISetupActivity activity, Func value) => activity.Set(x => x.BlockingActivityId, value); - public static ISetupActivity WithBlockingActivityId(this ISetupActivity activity, string value) => activity.Set(x => x.BlockingActivityId, value); + public static ISetupActivity WithBlockingActivityId(this ISetupActivity activity, Func> value) => activity.Set(x => x.BlockingActivityId, value); + public static ISetupActivity WithBlockingActivityId(this ISetupActivity activity, Func value) => activity.Set(x => x.BlockingActivityId, value); + public static ISetupActivity WithBlockingActivityId(this ISetupActivity activity, Func> value) => activity.Set(x => x.BlockingActivityId, value); + public static ISetupActivity WithBlockingActivityId(this ISetupActivity activity, Func value) => activity.Set(x => x.BlockingActivityId, value); + public static ISetupActivity WithBlockingActivityId(this ISetupActivity activity, string? value) => activity.Set(x => x.BlockingActivityId, value); public static ISetupActivity WithInput(this ISetupActivity activity, Func> value) => activity.Set(x => x.Input, value); public static ISetupActivity WithInput(this ISetupActivity activity, Func value) => activity.Set(x => x.Input, value); diff --git a/src/core/Elsa.Core/Builders/WorkflowBuilder.cs b/src/core/Elsa.Core/Builders/WorkflowBuilder.cs index 15fa192c1..88d97115b 100644 --- a/src/core/Elsa.Core/Builders/WorkflowBuilder.cs +++ b/src/core/Elsa.Core/Builders/WorkflowBuilder.cs @@ -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; diff --git a/src/core/Elsa.Core/Elsa.Core.csproj.DotSettings b/src/core/Elsa.Core/Elsa.Core.csproj.DotSettings index 9ade3e63e..2117cfad5 100644 --- a/src/core/Elsa.Core/Elsa.Core.csproj.DotSettings +++ b/src/core/Elsa.Core/Elsa.Core.csproj.DotSettings @@ -1,5 +1,7 @@  + True True + True True True True diff --git a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs index 40404dc49..b61044d63 100644 --- a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs @@ -138,6 +138,7 @@ namespace Microsoft.Extensions.DependencyInjection .AddActivity() .AddActivity() .AddActivity() + .AddActivity() .AddActivity() .AddActivity() .AddActivity() diff --git a/src/samples/console/Elsa.Samples.SwitchConsole/Elsa.Samples.SwitchConsole.csproj b/src/samples/console/Elsa.Samples.SwitchConsole/Elsa.Samples.SwitchConsole.csproj new file mode 100644 index 000000000..7a4b359fc --- /dev/null +++ b/src/samples/console/Elsa.Samples.SwitchConsole/Elsa.Samples.SwitchConsole.csproj @@ -0,0 +1,18 @@ + + + + Exe + net5.0 + enable + false + + + + + + + + + + + diff --git a/src/samples/console/Elsa.Samples.SwitchConsole/GrayscaleWorkflow.cs b/src/samples/console/Elsa.Samples.SwitchConsole/GrayscaleWorkflow.cs new file mode 100644 index 000000000..d91d69e1d --- /dev/null +++ b/src/samples/console/Elsa.Samples.SwitchConsole/GrayscaleWorkflow.cs @@ -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(); + 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"); + } + } +} \ No newline at end of file diff --git a/src/samples/console/Elsa.Samples.SwitchConsole/Program.cs b/src/samples/console/Elsa.Samples.SwitchConsole/Program.cs new file mode 100644 index 000000000..a1ff37144 --- /dev/null +++ b/src/samples/console/Elsa.Samples.SwitchConsole/Program.cs @@ -0,0 +1,32 @@ +using System.Threading.Tasks; +using Elsa.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Samples.SwitchConsole +{ + /// + /// Demonstrates a workflow with a While looping construct. + /// + static class Program + { + private static async Task Main() + { + // Create a service container with Elsa services. + var services = new ServiceCollection() + .AddElsa(options => options + .AddConsoleActivities() + .AddWorkflow()) + .BuildServiceProvider(); + + // Run startup actions (not needed when registering Elsa with a Host). + var startupRunner = services.GetRequiredService(); + await startupRunner.StartupAsync(); + + // Get a workflow runner. + var workflowRunner = services.GetRequiredService(); + + // Execute the workflow. + await workflowRunner.RunWorkflowAsync(); + } + } +} \ No newline at end of file diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/GoodbyeWorld.cs b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/GoodbyeWorld.cs index d79507fb3..53c9711ff 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/GoodbyeWorld.cs +++ b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/GoodbyeWorld.cs @@ -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 { diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/HelloWorld.cs b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/HelloWorld.cs index f27308e7e..7c6cc6ef2 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/HelloWorld.cs +++ b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/HelloWorld.cs @@ -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 { diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/HelloWorld.v2.cs b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/HelloWorld.v2.cs index dd3f108e9..a52213ec4 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/HelloWorld.v2.cs +++ b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/HelloWorld.v2.cs @@ -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 { diff --git a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/NamingWorkflow.cs b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/NamingWorkflow.cs index ed009504d..96e05d93f 100644 --- a/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/NamingWorkflow.cs +++ b/src/samples/dashboard/ElsaDashboard.Samples.Monolith/Workflows/NamingWorkflow.cs @@ -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().Body) + .SetVariable("Name", context => (string?) context.GetInput().Body) .SetName(context => context.GetVariable("Name")) .WriteHttpResponse(x => x diff --git a/src/server/Elsa.Server.Api/Endpoints/WorkflowRegistry/Models.cs b/src/server/Elsa.Server.Api/Endpoints/WorkflowRegistry/Models.cs index a0015835f..e921aa07d 100644 --- a/src/server/Elsa.Server.Api/Endpoints/WorkflowRegistry/Models.cs +++ b/src/server/Elsa.Server.Api/Endpoints/WorkflowRegistry/Models.cs @@ -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 diff --git a/test/integration/Elsa.Core.IntegrationTests/Workflows/SwitchWorkflow.cs b/test/integration/Elsa.Core.IntegrationTests/Workflows/SwitchWorkflow.cs deleted file mode 100644 index dc7e35c38..000000000 --- a/test/integration/Elsa.Core.IntegrationTests/Workflows/SwitchWorkflow.cs +++ /dev/null @@ -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.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"); - }); - } - } -} \ No newline at end of file diff --git a/test/integration/Elsa.Core.IntegrationTests/Workflows/SwitchWorkflowTests.cs b/test/integration/Elsa.Core.IntegrationTests/Workflows/SwitchWorkflowTests.cs deleted file mode 100644 index e963543e8..000000000 --- a/test/integration/Elsa.Core.IntegrationTests/Workflows/SwitchWorkflowTests.cs +++ /dev/null @@ -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(input: input); -// var executedActivityIds = workflowInstance.ExecutionLog.Select(x => x.ActivityId).ToList(); -// -// Assert.Contains(expectedActivityId, executedActivityIds); -// } -// } -// } \ No newline at end of file