From cb25239fd1f6724bc2409d791f2d305ab31c5b26 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 26 Oct 2019 12:46:20 +0200 Subject: [PATCH] Add While activity (#110) * Implement While activity * Variable scope bug fixes * Merge designer attributes with activity definition * Update package references --- .../Activities/ReadLine.cs | 7 +- .../Activities/WriteLine.cs | 7 +- .../Elsa.Activities.Console.csproj | 4 - .../Elsa.Activities.Dropbox.csproj | 2 +- .../Activities/HttpRequestAction.cs | 7 +- .../Activities/HttpRequestEvent.cs | 7 +- .../Activities/HttpResponseAction.cs | 7 +- .../Elsa.Activities.Http.csproj | 4 +- .../Elsa.Activities.MassTransit.csproj | 6 +- .../Activities/CronEvent.cs | 21 ++-- .../Elsa.Activities.Timers.csproj | 2 +- .../Attributes/ActivityDefinitionAttribute.cs | 2 + .../ActivityDefinitionDesignerAttribute.cs | 11 -- .../Elsa.Abstractions.csproj | 2 +- .../Models/ActivityDefinition.cs | 2 + src/core/Elsa.Abstractions/OutcomeNames.cs | 1 + .../Services/IActivityBuilder.cs | 3 + .../Services/Models/Workflow.cs | 15 ++- .../Models/WorkflowExecutionContext.cs | 17 ++- .../Elsa.AutoMapper.Extensions.csproj | 6 +- .../Elsa.Core/Activities/ControlFlow/Fork.cs | 4 +- .../Activities/ControlFlow/IfElse.cs | 7 +- .../Elsa.Core/Activities/ControlFlow/Join.cs | 7 +- .../Activities/ControlFlow/Switch.cs | 10 +- .../Elsa.Core/Activities/ControlFlow/While.cs | 56 +++++++++ .../Activities/Primitives/SetVariable.cs | 9 +- src/core/Elsa.Core/Elsa.Core.csproj | 5 +- .../ElsaServiceCollectionExtensions.cs | 1 + .../Elsa.Core/Metadata}/ActivityDescriber.cs | 23 ++-- .../Elsa.Core/Metadata/ActivityDescriptor.cs} | 11 +- .../Metadata}/ActivityPropertyDescriptor.cs | 2 +- .../CommonScriptEngineConfigurator.cs | 2 +- .../Elsa.Core/Services/WorkflowFactory.cs | 3 +- .../WorkflowBuilders/ActivityBuilder.cs | 22 ++++ .../WorkflowBuilders/WorkflowBuilder.cs | 4 + .../ViewModels/WorkflowDefinitionEditModel.cs | 3 +- .../WorkflowInstanceDetailsModel.cs | 5 +- .../Areas/Elsa/Views/Shared/_Layout.cshtml | 4 +- .../Views/WorkflowInstance/Details.cshtml | 3 - .../ActivityDefinitionListExtensions.cs | 1 + .../Options/ActivityDefinitionList.cs | 11 +- .../Elsa.WorkflowDesigner.csproj | 4 +- .../Models/ActivityDesignerSettings.cs | 8 -- .../WorkflowDesignerViewComponent.cs | 5 +- .../Elsa.Persistence.DocumentDb.csproj | 2 +- ...lsa.Persistence.EntityFrameworkCore.csproj | 12 +- .../Elsa.Persistence.YesSql.csproj | 10 +- src/samples/Sample01/Sample01.csproj | 2 +- src/samples/Sample02/Sample02.csproj | 2 +- src/samples/Sample03/Sample03.csproj | 2 +- src/samples/Sample04/CalculatorWorkflow.cs | 108 ++++++++++-------- src/samples/Sample04/Program.cs | 5 +- src/samples/Sample05/Sample05.csproj | 2 +- src/samples/Sample08/Sample08.csproj | 2 +- src/samples/Sample14/Sample14.csproj | 6 +- 55 files changed, 280 insertions(+), 216 deletions(-) delete mode 100644 src/core/Elsa.Abstractions/Attributes/ActivityDefinitionDesignerAttribute.cs create mode 100644 src/core/Elsa.Core/Activities/ControlFlow/While.cs rename src/{dashboard/Elsa.WorkflowDesigner => core/Elsa.Core/Metadata}/ActivityDescriber.cs (80%) rename src/{dashboard/Elsa.WorkflowDesigner/Models/ActivityDefinitionModel.cs => core/Elsa.Core/Metadata/ActivityDescriptor.cs} (70%) rename src/{dashboard/Elsa.WorkflowDesigner/Models => core/Elsa.Core/Metadata}/ActivityPropertyDescriptor.cs (93%) delete mode 100644 src/dashboard/Elsa.WorkflowDesigner/Models/ActivityDesignerSettings.cs diff --git a/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs b/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs index 6bf1a7f43..e8bfaadf9 100644 --- a/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs +++ b/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs @@ -13,11 +13,8 @@ namespace Elsa.Activities.Console.Activities /// [ActivityDefinition( Category = "Console", - Description = "Read text from standard in." - )] - [ActivityDefinitionDesigner( - Description = - "a => !!a.state.variableName ? `Read text from standard in and store into ${ a.state.variableName }.` : 'Read text from standard in.'", + Description = "Read text from standard in.", + RuntimeDescription = "a => !!a.state.variableName ? `Read text from standard in and store into ${ a.state.variableName }.` : 'Read text from standard in.'", Outcomes = new[] { OutcomeNames.Done } )] public class ReadLine : Activity diff --git a/src/activities/Elsa.Activities.Console/Activities/WriteLine.cs b/src/activities/Elsa.Activities.Console/Activities/WriteLine.cs index 58b292fdf..ef1d050b6 100644 --- a/src/activities/Elsa.Activities.Console/Activities/WriteLine.cs +++ b/src/activities/Elsa.Activities.Console/Activities/WriteLine.cs @@ -15,11 +15,8 @@ namespace Elsa.Activities.Console.Activities /// [ActivityDefinition( Category = "Console", - Description = "Write text to standard out." - )] - [ActivityDefinitionDesigner( - Description = - "x => !!x.state.textExpression ? `Write ${ x.state.textExpression.expression } to standard out.` : x.definition.description", + Description = "Write text to standard out.", + RuntimeDescription = "x => !!x.state.textExpression ? `Write ${ x.state.textExpression.expression } to standard out.` : x.definition.description", Outcomes = new[] { OutcomeNames.Done } )] public class WriteLine : Activity diff --git a/src/activities/Elsa.Activities.Console/Elsa.Activities.Console.csproj b/src/activities/Elsa.Activities.Console/Elsa.Activities.Console.csproj index 125881071..481f8e48b 100644 --- a/src/activities/Elsa.Activities.Console/Elsa.Activities.Console.csproj +++ b/src/activities/Elsa.Activities.Console/Elsa.Activities.Console.csproj @@ -17,8 +17,4 @@ - - - - diff --git a/src/activities/Elsa.Activities.Dropbox/Elsa.Activities.Dropbox.csproj b/src/activities/Elsa.Activities.Dropbox/Elsa.Activities.Dropbox.csproj index 2369b77a7..008267d2e 100644 --- a/src/activities/Elsa.Activities.Dropbox/Elsa.Activities.Dropbox.csproj +++ b/src/activities/Elsa.Activities.Dropbox/Elsa.Activities.Dropbox.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/activities/Elsa.Activities.Http/Activities/HttpRequestAction.cs b/src/activities/Elsa.Activities.Http/Activities/HttpRequestAction.cs index 1390358db..965b40736 100644 --- a/src/activities/Elsa.Activities.Http/Activities/HttpRequestAction.cs +++ b/src/activities/Elsa.Activities.Http/Activities/HttpRequestAction.cs @@ -23,11 +23,8 @@ namespace Elsa.Activities.Http.Activities [ActivityDefinition( Category = "HTTP", DisplayName = "Send HTTP Request", - Description = "Send an HTTP request." - )] - [ActivityDefinitionDesigner( - Description = - "x => !!x.state.url ? `Send HTTP ${ x.state.method } ${ x.state.url.expression }.` : x.definition.description", + Description = "Send an HTTP request.", + RuntimeDescription = "x => !!x.state.url ? `Send HTTP ${ x.state.method } ${ x.state.url.expression }.` : x.definition.description", Outcomes = "x => !!x.state.supportedStatusCodes ? x.state.supportedStatusCodes : []" )] public class HttpRequestAction : Activity diff --git a/src/activities/Elsa.Activities.Http/Activities/HttpRequestEvent.cs b/src/activities/Elsa.Activities.Http/Activities/HttpRequestEvent.cs index 00a658424..6422f0f5a 100644 --- a/src/activities/Elsa.Activities.Http/Activities/HttpRequestEvent.cs +++ b/src/activities/Elsa.Activities.Http/Activities/HttpRequestEvent.cs @@ -20,11 +20,8 @@ namespace Elsa.Activities.Http.Activities [ActivityDefinition( Category = "HTTP", DisplayName = "Receive HTTP Request", - Description = "Receive an incoming HTTP request." - )] - [ActivityDefinitionDesigner( - Description = - "x => !!x.state.path ? `Handle ${ x.state.method } ${ x.state.path }.` : x.definition.description", + Description = "Receive an incoming HTTP request.", + RuntimeDescription = "x => !!x.state.path ? `Handle ${ x.state.method } ${ x.state.path }.` : x.definition.description", Outcomes = new[] { OutcomeNames.Done } )] public class HttpRequestEvent : Activity diff --git a/src/activities/Elsa.Activities.Http/Activities/HttpResponseAction.cs b/src/activities/Elsa.Activities.Http/Activities/HttpResponseAction.cs index 271791b9c..27344fedd 100644 --- a/src/activities/Elsa.Activities.Http/Activities/HttpResponseAction.cs +++ b/src/activities/Elsa.Activities.Http/Activities/HttpResponseAction.cs @@ -18,11 +18,8 @@ namespace Elsa.Activities.Http.Activities [ActivityDefinition( Category = "HTTP", DisplayName = "Send HTTP Response", - Description = "Send an HTTP response." - )] - [ActivityDefinitionDesigner( - Description = - "x => !!x.state.statusCode ? `Send an HTTP ${ x.state.statusCode } - ${ x.state.contentType } response` : x.definition.description", + Description = "Send an HTTP response.", + RuntimeDescription = "x => !!x.state.statusCode ? `Send an HTTP ${ x.state.statusCode } - ${ x.state.contentType } response` : x.definition.description", Outcomes = new[] { OutcomeNames.Done } )] public class HttpResponseAction : Activity diff --git a/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj b/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj index d2a5da3ab..ac69a60a6 100644 --- a/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj +++ b/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj @@ -19,11 +19,11 @@ - + - + diff --git a/src/activities/Elsa.Activities.MassTransit/Elsa.Activities.MassTransit.csproj b/src/activities/Elsa.Activities.MassTransit/Elsa.Activities.MassTransit.csproj index c405ee8f3..8665b7334 100644 --- a/src/activities/Elsa.Activities.MassTransit/Elsa.Activities.MassTransit.csproj +++ b/src/activities/Elsa.Activities.MassTransit/Elsa.Activities.MassTransit.csproj @@ -14,9 +14,9 @@ - - - + + + diff --git a/src/activities/Elsa.Activities.Timers/Activities/CronEvent.cs b/src/activities/Elsa.Activities.Timers/Activities/CronEvent.cs index 3ac49553f..8d10c8b81 100644 --- a/src/activities/Elsa.Activities.Timers/Activities/CronEvent.cs +++ b/src/activities/Elsa.Activities.Timers/Activities/CronEvent.cs @@ -13,11 +13,8 @@ namespace Elsa.Activities.Timers.Activities { [ActivityDefinition( Category = "Timers", - Description = "Triggers periodically based on a specified CRON expression." - )] - [ActivityDefinitionDesigner( - Description = - "x => !!x.state.cronExpression ? `${ x.state.cronExpression.expression }.` : x.definition.description", + Description = "Triggers periodically based on a specified CRON expression.", + RuntimeDescription = "x => !!x.state.cronExpression ? `${ x.state.cronExpression.expression }.` : x.definition.description", Outcomes = new[] { OutcomeNames.Done } )] public class CronEvent : Activity @@ -43,7 +40,7 @@ namespace Elsa.Activities.Timers.Activities get => GetState(); set => SetState(value); } - + protected override async Task OnCanExecuteAsync(WorkflowExecutionContext context, CancellationToken cancellationToken) { return StartTime == null || await IsExpiredAsync(context, cancellationToken); @@ -54,7 +51,8 @@ namespace Elsa.Activities.Timers.Activities return Halt(); } - protected override async Task OnResumeAsync(WorkflowExecutionContext context, + protected override async Task OnResumeAsync( + WorkflowExecutionContext context, CancellationToken cancellationToken) { if (await IsExpiredAsync(context, cancellationToken)) @@ -62,11 +60,12 @@ namespace Elsa.Activities.Timers.Activities StartTime = null; return Done(); } - + return Halt(); } - private async Task IsExpiredAsync(WorkflowExecutionContext workflowContext, + private async Task IsExpiredAsync( + WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) { var cronExpression = await expressionEvaluator.EvaluateAsync( @@ -76,10 +75,10 @@ namespace Elsa.Activities.Timers.Activities ); var schedule = CrontabSchedule.Parse(cronExpression); var now = clock.GetCurrentInstant(); - + if (StartTime == null) StartTime = now; - + var nextOccurrence = schedule.GetNextOccurrence(StartTime.Value.ToDateTimeUtc()); return now.ToDateTimeUtc() >= nextOccurrence; diff --git a/src/activities/Elsa.Activities.Timers/Elsa.Activities.Timers.csproj b/src/activities/Elsa.Activities.Timers/Elsa.Activities.Timers.csproj index 1f8353967..7d86b6658 100644 --- a/src/activities/Elsa.Activities.Timers/Elsa.Activities.Timers.csproj +++ b/src/activities/Elsa.Activities.Timers/Elsa.Activities.Timers.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/core/Elsa.Abstractions/Attributes/ActivityDefinitionAttribute.cs b/src/core/Elsa.Abstractions/Attributes/ActivityDefinitionAttribute.cs index 6ef6f1bdc..96ffae093 100644 --- a/src/core/Elsa.Abstractions/Attributes/ActivityDefinitionAttribute.cs +++ b/src/core/Elsa.Abstractions/Attributes/ActivityDefinitionAttribute.cs @@ -8,7 +8,9 @@ namespace Elsa.Attributes public string Type { get; set; } public string DisplayName { get; set; } public string Description { get; set; } + public string RuntimeDescription { get; set; } public string Category { get; set; } public string Icon { get; set; } + public object Outcomes { get; set; } } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Attributes/ActivityDefinitionDesignerAttribute.cs b/src/core/Elsa.Abstractions/Attributes/ActivityDefinitionDesignerAttribute.cs deleted file mode 100644 index e2074c0a1..000000000 --- a/src/core/Elsa.Abstractions/Attributes/ActivityDefinitionDesignerAttribute.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System; - -namespace Elsa.Attributes -{ - [AttributeUsage(AttributeTargets.Class)] - public class ActivityDefinitionDesignerAttribute : Attribute - { - public string Description { get; set; } - public object Outcomes { get; set; } - } -} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Elsa.Abstractions.csproj b/src/core/Elsa.Abstractions/Elsa.Abstractions.csproj index 48d647eed..9a157f5ed 100644 --- a/src/core/Elsa.Abstractions/Elsa.Abstractions.csproj +++ b/src/core/Elsa.Abstractions/Elsa.Abstractions.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/core/Elsa.Abstractions/Models/ActivityDefinition.cs b/src/core/Elsa.Abstractions/Models/ActivityDefinition.cs index dea04e554..60f42a72f 100644 --- a/src/core/Elsa.Abstractions/Models/ActivityDefinition.cs +++ b/src/core/Elsa.Abstractions/Models/ActivityDefinition.cs @@ -25,6 +25,8 @@ namespace Elsa.Models public string Id { get; set; } public string Type { get; set; } + public string DisplayName { get; set; } + public string Description { get; set; } public int Left { get; set; } public int Top { get; set; } public JObject State { get; set; } diff --git a/src/core/Elsa.Abstractions/OutcomeNames.cs b/src/core/Elsa.Abstractions/OutcomeNames.cs index 2d62709fd..772227618 100644 --- a/src/core/Elsa.Abstractions/OutcomeNames.cs +++ b/src/core/Elsa.Abstractions/OutcomeNames.cs @@ -7,5 +7,6 @@ namespace Elsa public const string True = "True"; public const string False = "False"; public const string Iterate = "Iterate"; + public const string Loop = "Loop"; } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs b/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs index f521ad1b7..4d714c92c 100644 --- a/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs +++ b/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs @@ -13,7 +13,10 @@ namespace Elsa.Services IOutcomeBuilder When(string outcome); IActivityBuilder Then(Action setup = null, Action branch = null, string id = null) where T : class, IActivity; IActivityBuilder WithId(string id); + IActivityBuilder WithDisplayName(string displayName); + IActivityBuilder WithDescription(string description); IWorkflowBuilder Then(string activityId); + IActivityBuilder Then(IActivityBuilder targetActivity); WorkflowDefinitionVersion Build(); ActivityDefinition BuildActivity(); } diff --git a/src/core/Elsa.Abstractions/Services/Models/Workflow.cs b/src/core/Elsa.Abstractions/Services/Models/Workflow.cs index 765221cff..61cadfb18 100644 --- a/src/core/Elsa.Abstractions/Services/Models/Workflow.cs +++ b/src/core/Elsa.Abstractions/Services/Models/Workflow.cs @@ -12,8 +12,7 @@ namespace Elsa.Services.Models { public Workflow( string id, - string definitionId, - int version, + WorkflowDefinitionVersion definition, Instant createdAt, IEnumerable activities, IEnumerable connections, @@ -21,8 +20,7 @@ namespace Elsa.Services.Models string correlationId = default) : this() { Id = id; - DefinitionId = definitionId; - Version = version; + Definition = definition; CreatedAt = createdAt; CorrelationId = correlationId; Activities = activities.ToList(); @@ -30,6 +28,8 @@ namespace Elsa.Services.Models Input = new Variables(input ?? Variables.Empty); } + + public Workflow() { Scopes = new Stack(new[] { new WorkflowExecutionScope() }); @@ -38,8 +38,7 @@ namespace Elsa.Services.Models } public string Id { get; set; } - public string DefinitionId { get; } - public int Version { get; } + public WorkflowDefinitionVersion Definition { get; } public string CorrelationId { get; set; } public WorkflowStatus Status { get; set; } public Instant CreatedAt { get; set; } @@ -63,8 +62,8 @@ namespace Elsa.Services.Models return new WorkflowInstance { Id = Id, - DefinitionId = DefinitionId, - Version = Version, + DefinitionId = Definition.Id, + Version = Definition.Version, CorrelationId = CorrelationId, Status = Status, CreatedAt = CreatedAt, diff --git a/src/core/Elsa.Abstractions/Services/Models/WorkflowExecutionContext.cs b/src/core/Elsa.Abstractions/Services/Models/WorkflowExecutionContext.cs index 99ea81d7b..27c21ffe3 100644 --- a/src/core/Elsa.Abstractions/Services/Models/WorkflowExecutionContext.cs +++ b/src/core/Elsa.Abstractions/Services/Models/WorkflowExecutionContext.cs @@ -53,8 +53,21 @@ namespace Elsa.Services.Models public IActivity PopScheduledActivity() => CurrentActivity = scheduledActivities.Pop(); public void ScheduleHaltingActivity(IActivity activity) => scheduledHaltingActivities.Push(activity); public IActivity PopScheduledHaltingActivity() => scheduledHaltingActivities.Pop(); - public void SetVariable(string name, object value) => CurrentScope.SetVariable(name, value); - public T GetVariable(string name) => CurrentScope.GetVariable(name); + + public void SetVariable(string name, object value) + { + // Get the first scope (starting from the oldest one) containing the variable (existing variable). Otherwise use the current scope (new variable declaration) + var scope = Workflow.Scopes.Reverse().FirstOrDefault(x => x.Variables.ContainsKey(name)) ?? CurrentScope; + scope.SetVariable(name, value); + } + + public T GetVariable(string name) + { + // Get the first scope (starting from the newest one) containing the variable. + var scope = Workflow.Scopes.FirstOrDefault(x => x.Variables.ContainsKey(name)) ?? CurrentScope; + return scope.GetVariable(name); + } + public void SetLastResult(object value) => CurrentScope.LastResult = value; public void Start() diff --git a/src/core/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj b/src/core/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj index ca0cf7ef2..6105fc850 100644 --- a/src/core/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj +++ b/src/core/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj @@ -14,9 +14,9 @@ - - - + + + diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Fork.cs b/src/core/Elsa.Core/Activities/ControlFlow/Fork.cs index 52d999228..0d9e7aaf5 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/Fork.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/Fork.cs @@ -9,8 +9,8 @@ namespace Elsa.Activities.ControlFlow [ActivityDefinition( Category = "Control Flow", Description = "Fork workflow execution into multiple branches.", - Icon = "fas fa-code-branch fa-rotate-180")] - [ActivityDefinitionDesigner(Outcomes = "x => x.state.branches")] + Icon = "fas fa-code-branch fa-rotate-180", + Outcomes = "x => x.state.branches")] public class Fork : Activity { [ActivityProperty( diff --git a/src/core/Elsa.Core/Activities/ControlFlow/IfElse.cs b/src/core/Elsa.Core/Activities/ControlFlow/IfElse.cs index 4bab9e82c..f58316cae 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/IfElse.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/IfElse.cs @@ -12,11 +12,8 @@ namespace Elsa.Activities.ControlFlow [ActivityDefinition( DisplayName = "If/Else", Category = "Control Flow", - Description = "Evaluate a Boolean expression and continue execution depending on the result." - )] - [ActivityDefinitionDesigner( - Description = - "x => !!x.state.expression ? `Evaluate ${ x.state.expression.expression } and continue execution depending on the result.` : x.definition.description", + Description = "Evaluate a Boolean expression and continue execution depending on the result.", + RuntimeDescription = "x => !!x.state.expression ? `Evaluate ${ x.state.expression.expression } and continue execution depending on the result.` : x.definition.description", Outcomes = new[] { OutcomeNames.True, OutcomeNames.False } )] public class IfElse : Activity diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Join.cs b/src/core/Elsa.Core/Activities/ControlFlow/Join.cs index 760fbcb99..dc8ed556a 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/Join.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/Join.cs @@ -14,11 +14,8 @@ namespace Elsa.Activities.ControlFlow [ActivityDefinition( Category = "Control Flow", Description = "Merge workflow execution back into a single branch.", - Icon = "fas fa-code-branch" - )] - [ActivityDefinitionDesigner( - Description = - "x => !!x.state.joinMode ? `Merge workflow execution back into a single branch using mode ${ x.state.joinMode }` : x.definition.description", + Icon = "fas fa-code-branch", + RuntimeDescription = "x => !!x.state.joinMode ? `Merge workflow execution back into a single branch using mode ${ x.state.joinMode }` : x.definition.description", Outcomes = new[] { OutcomeNames.Done } )] public class Join : Activity, IWorkflowEventHandler diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Switch.cs b/src/core/Elsa.Core/Activities/ControlFlow/Switch.cs index b7871d975..4fdf11895 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/Switch.cs +++ b/src/core/Elsa.Core/Activities/ControlFlow/Switch.cs @@ -13,11 +13,8 @@ namespace Elsa.Activities.ControlFlow [ActivityDefinition( Category = "Control Flow", Description = "Switch execution based on a given expression.", - Icon = "far fa-list-alt" - )] - [ActivityDefinitionDesigner( - Description = - "x => !!x.state.expression ? `Switch execution based on ${ x.state.expression.expression }.` : x.definition.description", + Icon = "far fa-list-alt", + RuntimeDescription = "x => !!x.state.expression ? `Switch execution based on ${ x.state.expression.expression }.` : x.definition.description", Outcomes = "x => x.state.cases.map(c => c.toString())" )] public class Switch : Activity @@ -44,7 +41,8 @@ namespace Elsa.Activities.ControlFlow set => SetState(value); } - protected override async Task OnExecuteAsync(WorkflowExecutionContext workflowContext, + protected override async Task OnExecuteAsync( + WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) { var result = await expressionEvaluator.EvaluateAsync(Expression, workflowContext, cancellationToken); diff --git a/src/core/Elsa.Core/Activities/ControlFlow/While.cs b/src/core/Elsa.Core/Activities/ControlFlow/While.cs new file mode 100644 index 000000000..0ae2d5060 --- /dev/null +++ b/src/core/Elsa.Core/Activities/ControlFlow/While.cs @@ -0,0 +1,56 @@ +using System.Threading; +using System.Threading.Tasks; +using Elsa.Attributes; +using Elsa.Expressions; +using Elsa.Extensions; +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Activities.ControlFlow +{ + [ActivityDefinition(Category = "Control Flow", Description = "Execute while a given condition is true.", Icon = "far fa-circle")] + public class While : Activity + { + private readonly IWorkflowExpressionEvaluator expressionEvaluator; + + public While(IWorkflowExpressionEvaluator expressionEvaluator) + { + this.expressionEvaluator = expressionEvaluator; + } + + [ActivityProperty(Hint = "Enter an expression that evaluates to a boolean value.")] + public WorkflowExpression ConditionExpression + { + get => GetState(() => new JavaScriptExpression("true")); + set => SetState(value); + } + + public bool HasStarted + { + get => GetState(() => false); + set => SetState(value); + } + + protected override async Task OnExecuteAsync( + WorkflowExecutionContext context, + CancellationToken cancellationToken) + { + var loop = await expressionEvaluator.EvaluateAsync(ConditionExpression, context, cancellationToken); + + if(HasStarted) + context.EndScope(); + + if (loop) + { + if (!HasStarted) + HasStarted = true; + + context.BeginScope(); + return Outcome(OutcomeNames.Loop); + } + + return Done(); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/Primitives/SetVariable.cs b/src/core/Elsa.Core/Activities/Primitives/SetVariable.cs index 39645a194..6706e9830 100644 --- a/src/core/Elsa.Core/Activities/Primitives/SetVariable.cs +++ b/src/core/Elsa.Core/Activities/Primitives/SetVariable.cs @@ -11,11 +11,8 @@ namespace Elsa.Activities.Primitives [ActivityDefinition( DisplayName = "Set Variable", Description = "Set variable on the workflow.", - Category = "Primitives" - )] - [ActivityDefinitionDesigner( - Description = - "x => !!x.state.variableName ? `${x.state.variableName} = ${x.state.valueExpression.expression}
${x.state.valueExpression.syntax}` : x.definition.description", + Category = "Primitives", + RuntimeDescription = "x => !!x.state.variableName ? `${x.state.variableName} = ${x.state.valueExpression.expression}
${x.state.valueExpression.syntax}` : x.definition.description", Outcomes = new[] { OutcomeNames.Done } )] public class SetVariable : Activity @@ -51,7 +48,7 @@ namespace Elsa.Activities.Primitives workflowContext, cancellationToken ); - workflowContext.CurrentScope.SetVariable(VariableName, value); + workflowContext.SetVariable(VariableName, value); return Done(); } } diff --git a/src/core/Elsa.Core/Elsa.Core.csproj b/src/core/Elsa.Core/Elsa.Core.csproj index 1e8b11eb6..b36a56d18 100644 --- a/src/core/Elsa.Core/Elsa.Core.csproj +++ b/src/core/Elsa.Core/Elsa.Core.csproj @@ -16,10 +16,11 @@ + - + @@ -29,7 +30,7 @@ - + diff --git a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs index 786595d67..0ff5d3a2b 100644 --- a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs @@ -175,6 +175,7 @@ namespace Microsoft.Extensions.DependencyInjection { return services .AddActivity() + .AddActivity() .AddActivity() .AddActivity() .AddSingleton(sp => sp.GetRequiredService()) diff --git a/src/dashboard/Elsa.WorkflowDesigner/ActivityDescriber.cs b/src/core/Elsa.Core/Metadata/ActivityDescriber.cs similarity index 80% rename from src/dashboard/Elsa.WorkflowDesigner/ActivityDescriber.cs rename to src/core/Elsa.Core/Metadata/ActivityDescriber.cs index 73e2a110c..cd385885a 100644 --- a/src/dashboard/Elsa.WorkflowDesigner/ActivityDescriber.cs +++ b/src/core/Elsa.Core/Metadata/ActivityDescriber.cs @@ -7,22 +7,20 @@ using Elsa.Attributes; using Elsa.Design; using Elsa.Expressions; using Elsa.Services.Models; -using Elsa.WorkflowDesigner.Models; using Humanizer; -namespace Elsa.WorkflowDesigner +namespace Elsa.Metadata { public static class ActivityDescriber { - public static ActivityDefinitionModel Describe() where T : IActivity + public static ActivityDescriptor Describe() where T : IActivity { return Describe(typeof(T)); } - - public static ActivityDefinitionModel Describe(Type activityType) + + public static ActivityDescriptor Describe(Type activityType) { var activityDefinitionAttribute = activityType.GetCustomAttribute(); - var activityDesignerAttribute = activityType.GetCustomAttribute(); var typeName = activityDefinitionAttribute?.Type ?? activityType.Name; var displayName = @@ -30,25 +28,22 @@ namespace Elsa.WorkflowDesigner activityType.Name.Humanize(LetterCasing.Title); var description = activityDefinitionAttribute?.Description; + var runtimeDescription = activityDefinitionAttribute?.RuntimeDescription; var category = activityDefinitionAttribute?.Category ?? "Miscellaneous"; var icon = activityDefinitionAttribute?.Icon; - var designerDescription = activityDesignerAttribute?.Description; - var designerOutcomes = activityDesignerAttribute?.Outcomes ?? new[] { OutcomeNames.Done }; + var outcomes = activityDefinitionAttribute?.Outcomes ?? new[] { OutcomeNames.Done }; var properties = DescribeProperties(activityType); - return new ActivityDefinitionModel + return new ActivityDescriptor { Type = typeName.Pascalize(), DisplayName = displayName, Description = description, + RuntimeDescription = runtimeDescription, Category = category, Icon = icon, Properties = properties.ToArray(), - Designer = new ActivityDesignerSettings - { - Description = designerDescription, - Outcomes = designerOutcomes - } + Outcomes = outcomes }; } diff --git a/src/dashboard/Elsa.WorkflowDesigner/Models/ActivityDefinitionModel.cs b/src/core/Elsa.Core/Metadata/ActivityDescriptor.cs similarity index 70% rename from src/dashboard/Elsa.WorkflowDesigner/Models/ActivityDefinitionModel.cs rename to src/core/Elsa.Core/Metadata/ActivityDescriptor.cs index e05641b47..0da3dfb49 100644 --- a/src/dashboard/Elsa.WorkflowDesigner/Models/ActivityDefinitionModel.cs +++ b/src/core/Elsa.Core/Metadata/ActivityDescriptor.cs @@ -1,23 +1,24 @@ -namespace Elsa.WorkflowDesigner.Models +namespace Elsa.Metadata { - public class ActivityDefinitionModel + public class ActivityDescriptor { - public ActivityDefinitionModel() + public ActivityDescriptor() { Type = "Activity"; Properties = new ActivityPropertyDescriptor[0]; Category = "Miscellaneous"; DisplayName = "Activity"; Properties = new ActivityPropertyDescriptor[0]; - Designer = new ActivityDesignerSettings(); + Outcomes = null; } public string Type { get; set; } public string DisplayName { get; set; } public string? Description { get; set; } + public string? RuntimeDescription { get; set; } public string Category { get; set; } public string? Icon { get; set; } + public object? Outcomes { get; set; } public ActivityPropertyDescriptor[] Properties { get; set; } - public ActivityDesignerSettings Designer { get; set; } } } \ No newline at end of file diff --git a/src/dashboard/Elsa.WorkflowDesigner/Models/ActivityPropertyDescriptor.cs b/src/core/Elsa.Core/Metadata/ActivityPropertyDescriptor.cs similarity index 93% rename from src/dashboard/Elsa.WorkflowDesigner/Models/ActivityPropertyDescriptor.cs rename to src/core/Elsa.Core/Metadata/ActivityPropertyDescriptor.cs index 6feaf6f26..58da3a6bc 100644 --- a/src/dashboard/Elsa.WorkflowDesigner/Models/ActivityPropertyDescriptor.cs +++ b/src/core/Elsa.Core/Metadata/ActivityPropertyDescriptor.cs @@ -1,4 +1,4 @@ -namespace Elsa.WorkflowDesigner.Models +namespace Elsa.Metadata { public class ActivityPropertyDescriptor { diff --git a/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs b/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs index df9aa755d..adcd6a908 100644 --- a/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs +++ b/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs @@ -23,7 +23,7 @@ namespace Elsa.Scripting engine.SetValue("correlationId", (Func) (() => context.Workflow.CorrelationId)); engine.SetValue("currentCulture", (Func) (() => CultureInfo.InvariantCulture)); - var variables = workflowExecutionContext.Workflow.Scopes + var variables = workflowExecutionContext.Workflow.Scopes.Reverse() .Select(x => x.Variables) .Aggregate(Variables.Empty, (x, y) => new Variables(x.Union(y))); diff --git a/src/core/Elsa.Core/Services/WorkflowFactory.cs b/src/core/Elsa.Core/Services/WorkflowFactory.cs index dbf3e8e50..4704af12a 100644 --- a/src/core/Elsa.Core/Services/WorkflowFactory.cs +++ b/src/core/Elsa.Core/Services/WorkflowFactory.cs @@ -52,8 +52,7 @@ namespace Elsa.Services var id = idGenerator.Generate(); var workflow = new Workflow( id, - definition.DefinitionId, - definition.Version, + definition, clock.GetCurrentInstant(), activities, connections, diff --git a/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs b/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs index bac4dc39e..3b8ff0c27 100644 --- a/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs +++ b/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs @@ -18,6 +18,8 @@ namespace Elsa.WorkflowBuilders public WorkflowBuilder WorkflowBuilder { get; } public ActivityDefinition Activity { get; } public string Id { get; set; } + public string Description { get; set; } + public string DisplayName { get; set; } public IActivityBuilder StartWith(Action setup = default, string id = default) where T : class, IActivity { @@ -45,6 +47,18 @@ namespace Elsa.WorkflowBuilders Id = id; return this; } + + public IActivityBuilder WithDisplayName(string displayName) + { + DisplayName = displayName; + return this; + } + + public IActivityBuilder WithDescription(string description) + { + Description = description; + return this; + } public IWorkflowBuilder Then(string activityId) { @@ -56,9 +70,17 @@ namespace Elsa.WorkflowBuilders return WorkflowBuilder; } + public IActivityBuilder Then(IActivityBuilder targetActivity) + { + WorkflowBuilder.Connect(this, targetActivity); + return this; + } + public ActivityDefinition BuildActivity() { Activity.Id = Id; + Activity.Description = Description; + Activity.DisplayName = DisplayName; return Activity; } diff --git a/src/core/Elsa.Core/WorkflowBuilders/WorkflowBuilder.cs b/src/core/Elsa.Core/WorkflowBuilders/WorkflowBuilder.cs index debe85b8c..fd0248d48 100644 --- a/src/core/Elsa.Core/WorkflowBuilders/WorkflowBuilder.cs +++ b/src/core/Elsa.Core/WorkflowBuilders/WorkflowBuilder.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Linq; +using Elsa.Metadata; using Elsa.Models; using Elsa.Services; using Elsa.Services.Models; @@ -79,10 +80,13 @@ namespace Elsa.WorkflowBuilders var activity = activityResolver.ResolveActivity(setupActivity); var activityBlueprint = ActivityDefinition.FromActivity(activity); var activityBuilder = new ActivityBuilder(this, activityBlueprint, id); + var activityDescriptor = ActivityDescriber.Describe(); if (id != null) activity.Id = id; + activityBuilder.DisplayName = activityDescriptor.DisplayName; + activityBuilder.Description = activityDescriptor.Description; activityBuilders.Add(activityBuilder); return activityBuilder; } diff --git a/src/dashboard/Elsa.Dashboard/Areas/Elsa/ViewModels/WorkflowDefinitionEditModel.cs b/src/dashboard/Elsa.Dashboard/Areas/Elsa/ViewModels/WorkflowDefinitionEditModel.cs index 5210446e2..9292d451b 100644 --- a/src/dashboard/Elsa.Dashboard/Areas/Elsa/ViewModels/WorkflowDefinitionEditModel.cs +++ b/src/dashboard/Elsa.Dashboard/Areas/Elsa/ViewModels/WorkflowDefinitionEditModel.cs @@ -1,3 +1,4 @@ +using Elsa.Metadata; using Elsa.WorkflowDesigner.Models; namespace Elsa.Dashboard.Areas.Elsa.ViewModels @@ -11,7 +12,7 @@ namespace Elsa.Dashboard.Areas.Elsa.ViewModels public string Description { get; set; } public bool IsSingleton { get; set; } public bool IsDisabled { get; set; } - public ActivityDefinitionModel[] ActivityDefinitions { get; set; } + public ActivityDescriptor[] ActivityDefinitions { get; set; } public WorkflowModel WorkflowModel { get; set; } } } \ No newline at end of file diff --git a/src/dashboard/Elsa.Dashboard/Areas/Elsa/ViewModels/WorkflowInstanceDetailsModel.cs b/src/dashboard/Elsa.Dashboard/Areas/Elsa/ViewModels/WorkflowInstanceDetailsModel.cs index c3f4c2e87..49cf99dcf 100644 --- a/src/dashboard/Elsa.Dashboard/Areas/Elsa/ViewModels/WorkflowInstanceDetailsModel.cs +++ b/src/dashboard/Elsa.Dashboard/Areas/Elsa/ViewModels/WorkflowInstanceDetailsModel.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Elsa.Metadata; using Elsa.Models; using Elsa.WorkflowDesigner.Models; @@ -11,7 +12,7 @@ namespace Elsa.Dashboard.Areas.Elsa.ViewModels WorkflowInstance workflowInstance, WorkflowDefinitionVersion workflowDefinition, WorkflowModel workflowModel, - IEnumerable activityDefinitions, + IEnumerable activityDefinitions, string returnUrl) { WorkflowInstance = workflowInstance; @@ -24,7 +25,7 @@ namespace Elsa.Dashboard.Areas.Elsa.ViewModels public WorkflowInstance WorkflowInstance { get; } public WorkflowDefinitionVersion WorkflowDefinition { get; } public WorkflowModel WorkflowModel { get; } - public ActivityDefinitionModel[] ActivityDefinitions { get; } + public ActivityDescriptor[] ActivityDefinitions { get; } public string ReturnUrl { get; } } } \ No newline at end of file diff --git a/src/dashboard/Elsa.Dashboard/Areas/Elsa/Views/Shared/_Layout.cshtml b/src/dashboard/Elsa.Dashboard/Areas/Elsa/Views/Shared/_Layout.cshtml index 020830b5c..d31eeeb6b 100644 --- a/src/dashboard/Elsa.Dashboard/Areas/Elsa/Views/Shared/_Layout.cshtml +++ b/src/dashboard/Elsa.Dashboard/Areas/Elsa/Views/Shared/_Layout.cshtml @@ -32,8 +32,8 @@ - - + + @await RenderSectionAsync("HeadScripts", false) diff --git a/src/dashboard/Elsa.Dashboard/Areas/Elsa/Views/WorkflowInstance/Details.cshtml b/src/dashboard/Elsa.Dashboard/Areas/Elsa/Views/WorkflowInstance/Details.cshtml index 7fa547a06..8d132901b 100644 --- a/src/dashboard/Elsa.Dashboard/Areas/Elsa/Views/WorkflowInstance/Details.cshtml +++ b/src/dashboard/Elsa.Dashboard/Areas/Elsa/Views/WorkflowInstance/Details.cshtml @@ -12,9 +12,6 @@ diff --git a/src/dashboard/Elsa.Dashboard/Extensions/ActivityDefinitionListExtensions.cs b/src/dashboard/Elsa.Dashboard/Extensions/ActivityDefinitionListExtensions.cs index 3ad6645c7..9669b422b 100644 --- a/src/dashboard/Elsa.Dashboard/Extensions/ActivityDefinitionListExtensions.cs +++ b/src/dashboard/Elsa.Dashboard/Extensions/ActivityDefinitionListExtensions.cs @@ -1,5 +1,6 @@ using System; using Elsa.Dashboard.Options; +using Elsa.Metadata; using Elsa.Services.Models; using Elsa.WorkflowDesigner; using Microsoft.Extensions.DependencyInjection; diff --git a/src/dashboard/Elsa.Dashboard/Options/ActivityDefinitionList.cs b/src/dashboard/Elsa.Dashboard/Options/ActivityDefinitionList.cs index fe4f96791..1f1f26f99 100644 --- a/src/dashboard/Elsa.Dashboard/Options/ActivityDefinitionList.cs +++ b/src/dashboard/Elsa.Dashboard/Options/ActivityDefinitionList.cs @@ -1,25 +1,26 @@ using System.Collections; using System.Collections.Generic; +using Elsa.Metadata; using Elsa.WorkflowDesigner.Models; namespace Elsa.Dashboard.Options { - public class ActivityDefinitionList : IEnumerable + public class ActivityDefinitionList : IEnumerable { public ActivityDefinitionList() { - Items = new Dictionary(); + Items = new Dictionary(); } - private IDictionary Items { get; } + private IDictionary Items { get; } - public ActivityDefinitionList Add(ActivityDefinitionModel item) + public ActivityDefinitionList Add(ActivityDescriptor item) { Items[item.Type] = item; return this; } - public IEnumerator GetEnumerator() => Items.Values.GetEnumerator(); + public IEnumerator GetEnumerator() => Items.Values.GetEnumerator(); IEnumerator IEnumerable.GetEnumerator() { diff --git a/src/dashboard/Elsa.WorkflowDesigner/Elsa.WorkflowDesigner.csproj b/src/dashboard/Elsa.WorkflowDesigner/Elsa.WorkflowDesigner.csproj index 54ee45818..128f8a015 100644 --- a/src/dashboard/Elsa.WorkflowDesigner/Elsa.WorkflowDesigner.csproj +++ b/src/dashboard/Elsa.WorkflowDesigner/Elsa.WorkflowDesigner.csproj @@ -15,12 +15,12 @@ - + - + diff --git a/src/dashboard/Elsa.WorkflowDesigner/Models/ActivityDesignerSettings.cs b/src/dashboard/Elsa.WorkflowDesigner/Models/ActivityDesignerSettings.cs deleted file mode 100644 index 02a76c395..000000000 --- a/src/dashboard/Elsa.WorkflowDesigner/Models/ActivityDesignerSettings.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace Elsa.WorkflowDesigner.Models -{ - public class ActivityDesignerSettings - { - public object? Description { get; set; } - public object? Outcomes { get; set; } - } -} \ No newline at end of file diff --git a/src/dashboard/Elsa.WorkflowDesigner/ViewComponents/WorkflowDesignerViewComponent.cs b/src/dashboard/Elsa.WorkflowDesigner/ViewComponents/WorkflowDesignerViewComponent.cs index 8b9f687a2..8fc993ec6 100644 --- a/src/dashboard/Elsa.WorkflowDesigner/ViewComponents/WorkflowDesignerViewComponent.cs +++ b/src/dashboard/Elsa.WorkflowDesigner/ViewComponents/WorkflowDesignerViewComponent.cs @@ -1,3 +1,4 @@ +using Elsa.Metadata; using Elsa.WorkflowDesigner.Models; using Elsa.WorkflowDesigner.ViewModels; using Microsoft.AspNetCore.Mvc; @@ -10,13 +11,13 @@ namespace Elsa.WorkflowDesigner.ViewComponents { public IViewComponentResult Invoke( string id, - ActivityDefinitionModel[]? activityDefinitions = null, + ActivityDescriptor[]? activityDefinitions = null, WorkflowModel? workflow = null, bool? isReadonly = null) { var model = new WorkflowDesignerViewComponentModel( id, - Serialize(activityDefinitions ?? new ActivityDefinitionModel[0]), + Serialize(activityDefinitions ?? new ActivityDescriptor[0]), Serialize(workflow ?? new WorkflowModel()), isReadonly.GetValueOrDefault() ); diff --git a/src/persistence/Elsa.Persistence.DocumentDb/Elsa.Persistence.DocumentDb.csproj b/src/persistence/Elsa.Persistence.DocumentDb/Elsa.Persistence.DocumentDb.csproj index f11e3b77a..876921f1f 100644 --- a/src/persistence/Elsa.Persistence.DocumentDb/Elsa.Persistence.DocumentDb.csproj +++ b/src/persistence/Elsa.Persistence.DocumentDb/Elsa.Persistence.DocumentDb.csproj @@ -8,7 +8,7 @@ - + diff --git a/src/persistence/Elsa.Persistence.EntityFrameworkCore/Elsa.Persistence.EntityFrameworkCore.csproj b/src/persistence/Elsa.Persistence.EntityFrameworkCore/Elsa.Persistence.EntityFrameworkCore.csproj index edf569c13..a5999c0dd 100644 --- a/src/persistence/Elsa.Persistence.EntityFrameworkCore/Elsa.Persistence.EntityFrameworkCore.csproj +++ b/src/persistence/Elsa.Persistence.EntityFrameworkCore/Elsa.Persistence.EntityFrameworkCore.csproj @@ -2,7 +2,7 @@ netstandard2.0 - netcoreapp2.2 + netcoreapp3.0 latest 1.0.0 Sipke Schoorstra @@ -15,11 +15,11 @@ - - - - - + + + + + diff --git a/src/persistence/Elsa.Persistence.YesSql/Elsa.Persistence.YesSql.csproj b/src/persistence/Elsa.Persistence.YesSql/Elsa.Persistence.YesSql.csproj index 08a0dd651..efffc9194 100644 --- a/src/persistence/Elsa.Persistence.YesSql/Elsa.Persistence.YesSql.csproj +++ b/src/persistence/Elsa.Persistence.YesSql/Elsa.Persistence.YesSql.csproj @@ -21,11 +21,11 @@ - - - - - + + + + + diff --git a/src/samples/Sample01/Sample01.csproj b/src/samples/Sample01/Sample01.csproj index 04a79f039..fe14b3066 100644 --- a/src/samples/Sample01/Sample01.csproj +++ b/src/samples/Sample01/Sample01.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.0 diff --git a/src/samples/Sample02/Sample02.csproj b/src/samples/Sample02/Sample02.csproj index 04a79f039..fe14b3066 100644 --- a/src/samples/Sample02/Sample02.csproj +++ b/src/samples/Sample02/Sample02.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.0 diff --git a/src/samples/Sample03/Sample03.csproj b/src/samples/Sample03/Sample03.csproj index 04a79f039..fe14b3066 100644 --- a/src/samples/Sample03/Sample03.csproj +++ b/src/samples/Sample03/Sample03.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.0 diff --git a/src/samples/Sample04/CalculatorWorkflow.cs b/src/samples/Sample04/CalculatorWorkflow.cs index 80a79cd0f..347eb15a5 100644 --- a/src/samples/Sample04/CalculatorWorkflow.cs +++ b/src/samples/Sample04/CalculatorWorkflow.cs @@ -1,6 +1,7 @@ using Elsa; using Elsa.Activities.Console.Activities; using Elsa.Activities.ControlFlow; +using Elsa.Activities.Primitives; using Elsa.Expressions; using Elsa.Services; using Elsa.Services.Models; @@ -13,57 +14,66 @@ namespace Sample04 public void Build(IWorkflowBuilder builder) { builder - .StartWith(x => x.TextExpression = new LiteralExpression("Welcome to Calculator Workflow!"), "welcome") - .Then(x => x.TextExpression = new LiteralExpression("Enter number 1:"), id: "enter-first-number-prompt") - .Then(x => x.VariableName = "number1", id: "read-first-number") - .Then(x => x.TextExpression = new LiteralExpression("Enter number 2:"), id: "enter-second-number-prompt") - .Then(x => x.VariableName = "number2", id: "read-second-number") - .Then(x => x.TextExpression = new LiteralExpression("Now enter the operation you wish to apply. Options are: add, subtract, multiply or divide:"), id: "enter-operation-prompt") - .Then(x => x.VariableName = "operation", id: "read-operation") - .Then(@switch => + .StartWith(x => x.TextExpression = new LiteralExpression("Welcome to Calculator Workflow!")).WithDisplayName("Welcome") + .Then( + x => { - @switch.Expression = new JavaScriptExpression("operation"); - @switch.Cases = new[] { "add", "subtract", "multiply", "divide" }; - }, - @switch => + x.VariableName = "Exit"; + x.ValueExpression = new JavaScriptExpression("false"); + }).WithDisplayName("Initialize Exit Variable") + .Then( + x => x.ConditionExpression = new JavaScriptExpression("!Exit"), + whileActivity => { - @switch - .When("add") - .Then(SetupOperation, id: "perform-sum") - .Then("show-result"); - - @switch - .When("subtract") - .Then(SetupOperation, id: "perform-subtract") - .Then("show-result"); - - @switch - .When("multiply") - .Then(SetupOperation, id: "perform-multiply") - .Then("show-result"); - - @switch - .When("divide") - .Then(SetupOperation, id: "perform-divide") - .Then("show-result"); - }, - "inspect-selected-operation" - ) - .Add(x => x.TextExpression = new JavaScriptExpression("`Result: ${result}`"), "show-result") - .Then(x => x.TextExpression = new LiteralExpression("Try again? (y/n)"), id: "try-again-prompt") - .Then(x => x.VariableName = "retry", id: "read-try-again") - .Then( - x => x.ConditionExpression = new JavaScriptExpression("retry.toLowerCase() === 'y'"), - ifElse => - { - ifElse - .When(OutcomeNames.True) - .Then("enter-first-number-prompt"); - - ifElse - .When(OutcomeNames.False) - .Then(x => x.TextExpression = new LiteralExpression("Bye!"), id: "say-good-bye"); - }, id: "inspect-retry"); + whileActivity + .When(OutcomeNames.Loop) + .Then(x => x.TextExpression = new LiteralExpression("Enter number 1:")) + .Then(x => x.VariableName = "number1") + .Then(x => x.TextExpression = new LiteralExpression("Enter number 2:")) + .Then(x => x.VariableName = "number2") + .Then(x => x.TextExpression = new LiteralExpression("Now enter the operation you wish to apply. Options are: add, subtract, multiply or divide:")) + .Then(x => x.VariableName = "operation") + .Then( + @switch => + { + @switch.Expression = new JavaScriptExpression("operation"); + @switch.Cases = new[] { "add", "subtract", "multiply", "divide" }; + }, + @switch => + { + @switch + .When("add") + .Then(SetupOperation, id: "perform-sum") + .Then("ShowResult"); + + @switch + .When("subtract") + .Then(SetupOperation, id: "perform-subtract") + .Then("ShowResult"); + + @switch + .When("multiply") + .Then(SetupOperation, id: "perform-multiply") + .Then("ShowResult"); + + @switch + .When("divide") + .Then(SetupOperation, id: "perform-divide") + .Then("ShowResult"); + }, + "inspect-selected-operation" + ) + .Add(x => x.TextExpression = new JavaScriptExpression("`Result: ${result}`")).WithId("ShowResult") + .Then(x => x.TextExpression = new LiteralExpression("Try again? (y/n)")) + .Then().WithId("TryAgain") + .Then(x => + { + x.VariableName = "Exit"; + x.ValueExpression = new JavaScriptExpression("TryAgain.Input !== 'y' && TryAgain.Input !== 'Y'"); + }) + .Then(whileActivity); + }).WithDisplayName("While") + .Then(x => x.TextExpression = new LiteralExpression("Bye!")); } private void SetupOperation(ArithmeticOperation operation) diff --git a/src/samples/Sample04/Program.cs b/src/samples/Sample04/Program.cs index b8e1cfb76..2dc98f0e2 100644 --- a/src/samples/Sample04/Program.cs +++ b/src/samples/Sample04/Program.cs @@ -1,4 +1,5 @@ using System; +using System.Linq; using System.Threading.Tasks; using Elsa.Activities.Console.Extensions; using Elsa.Services; @@ -16,6 +17,7 @@ namespace Sample04 { // Setup a service collection. var services = new ServiceCollection() + .AddSingleton(Console.In) .AddWorkflows() .AddConsoleActivities() .AddActivity() @@ -35,7 +37,8 @@ namespace Sample04 Console.WriteLine("Workflow has ended. Here are the activities that have executed:"); foreach (var logEntry in workflow.ExecutionLog) { - Console.WriteLine("{0}: {1}", logEntry.Timestamp, logEntry.ActivityId); + var activity = workflow.Definition.Activities.First(x => x.Id == logEntry.ActivityId); + Console.WriteLine("{0}: {1}", logEntry.Timestamp, activity.DisplayName); } Console.ReadLine(); } diff --git a/src/samples/Sample05/Sample05.csproj b/src/samples/Sample05/Sample05.csproj index 75fe70087..9de31932e 100644 --- a/src/samples/Sample05/Sample05.csproj +++ b/src/samples/Sample05/Sample05.csproj @@ -12,7 +12,7 @@ - + diff --git a/src/samples/Sample08/Sample08.csproj b/src/samples/Sample08/Sample08.csproj index 60899443c..491dc0a22 100644 --- a/src/samples/Sample08/Sample08.csproj +++ b/src/samples/Sample08/Sample08.csproj @@ -5,7 +5,7 @@ - + diff --git a/src/samples/Sample14/Sample14.csproj b/src/samples/Sample14/Sample14.csproj index f1cd1f4ba..6ff20127b 100644 --- a/src/samples/Sample14/Sample14.csproj +++ b/src/samples/Sample14/Sample14.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp2.2 + netcoreapp3.0 latest @@ -16,8 +16,8 @@ - - + +