From dc0a68c37d33ec74e8ebff06d2bd2889a57a99cc Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 24 Oct 2019 17:56:36 +0200 Subject: [PATCH 01/13] Add activity output example --- Samples.sln | 7 ++++ .../Sample17/Activities/CreateDocument.cs | 38 +++++++++++++++++++ .../Sample17/CreateDocumentWorkflow.cs | 20 ++++++++++ src/samples/Sample17/Models/Document.cs | 7 ++++ src/samples/Sample17/Program.cs | 30 +++++++++++++++ src/samples/Sample17/Sample17.csproj | 15 ++++++++ 6 files changed, 117 insertions(+) create mode 100644 src/samples/Sample17/Activities/CreateDocument.cs create mode 100644 src/samples/Sample17/CreateDocumentWorkflow.cs create mode 100644 src/samples/Sample17/Models/Document.cs create mode 100644 src/samples/Sample17/Program.cs create mode 100644 src/samples/Sample17/Sample17.csproj diff --git a/Samples.sln b/Samples.sln index ae50bb915..5dab74820 100644 --- a/Samples.sln +++ b/Samples.sln @@ -87,6 +87,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Persistence.DocumentDb EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample16", "src\samples\Sample16\Sample16.csproj", "{DFE72CF4-C77C-4932-A00F-A8D325E603CE}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample17", "src\samples\Sample17\Sample17.csproj", "{8854DF17-D566-49C9-8210-68AB8F5659FA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -221,6 +223,10 @@ Global {DFE72CF4-C77C-4932-A00F-A8D325E603CE}.Debug|Any CPU.Build.0 = Debug|Any CPU {DFE72CF4-C77C-4932-A00F-A8D325E603CE}.Release|Any CPU.ActiveCfg = Release|Any CPU {DFE72CF4-C77C-4932-A00F-A8D325E603CE}.Release|Any CPU.Build.0 = Release|Any CPU + {8854DF17-D566-49C9-8210-68AB8F5659FA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8854DF17-D566-49C9-8210-68AB8F5659FA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8854DF17-D566-49C9-8210-68AB8F5659FA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8854DF17-D566-49C9-8210-68AB8F5659FA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -263,6 +269,7 @@ Global {79E85459-EF54-44D6-9508-1E68B8238D00} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} {8C51A528-F9B5-464E-AAD1-1ADC44697085} = {600C4AE0-0585-4181-9CEC-2BB43430C713} {DFE72CF4-C77C-4932-A00F-A8D325E603CE} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} + {8854DF17-D566-49C9-8210-68AB8F5659FA} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158} diff --git a/src/samples/Sample17/Activities/CreateDocument.cs b/src/samples/Sample17/Activities/CreateDocument.cs new file mode 100644 index 000000000..7a2ed8bf5 --- /dev/null +++ b/src/samples/Sample17/Activities/CreateDocument.cs @@ -0,0 +1,38 @@ +using System.Threading; +using System.Threading.Tasks; +using Elsa.Expressions; +using Elsa.Extensions; +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Models; +using Sample17.Models; + +namespace Sample17.Activities +{ + public class CreateDocument : Activity + { + private readonly IWorkflowExpressionEvaluator expressionEvaluator; + + public CreateDocument(IWorkflowExpressionEvaluator expressionEvaluator) + { + this.expressionEvaluator = expressionEvaluator; + } + + public WorkflowExpression TitleExpression + { + get => GetState>(); + set => SetState(value); + } + + protected override async Task OnExecuteAsync( + WorkflowExecutionContext context, + CancellationToken cancellationToken) + { + var title = await expressionEvaluator.EvaluateAsync(TitleExpression, context, cancellationToken); + var document = new Document { Title = title}; + + Output["Document"] = document; + return Done(); + } + } +} \ No newline at end of file diff --git a/src/samples/Sample17/CreateDocumentWorkflow.cs b/src/samples/Sample17/CreateDocumentWorkflow.cs new file mode 100644 index 000000000..7a9e5bb42 --- /dev/null +++ b/src/samples/Sample17/CreateDocumentWorkflow.cs @@ -0,0 +1,20 @@ +using Elsa.Activities.Console.Activities; +using Elsa.Expressions; +using Elsa.Services; +using Elsa.Services.Models; +using Sample17.Activities; + +namespace Sample17 +{ + public class CreateDocumentWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(x => x.TextExpression = new LiteralExpression("Enter a title")) + .Then(id: "DocumentTitleInput") + .Then(x => x.TitleExpression = new JavaScriptExpression("DocumentTitleInput.Input"), id: "CreateDocument") + .Then(x => x.TextExpression = new JavaScriptExpression("`A new document was created with title \"${CreateDocument.Document.Title}\"`")); + } + } +} \ No newline at end of file diff --git a/src/samples/Sample17/Models/Document.cs b/src/samples/Sample17/Models/Document.cs new file mode 100644 index 000000000..31b3dcff0 --- /dev/null +++ b/src/samples/Sample17/Models/Document.cs @@ -0,0 +1,7 @@ +namespace Sample17.Models +{ + public class Document + { + public string Title { get; set; } + } +} \ No newline at end of file diff --git a/src/samples/Sample17/Program.cs b/src/samples/Sample17/Program.cs new file mode 100644 index 000000000..eb7d72bad --- /dev/null +++ b/src/samples/Sample17/Program.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading.Tasks; +using Elsa.Activities.Console.Extensions; +using Elsa.Services; +using Microsoft.Extensions.DependencyInjection; +using Sample17.Activities; + +namespace Sample17 +{ + internal static class Program + { + private static async Task Main(string[] args) + { + var services = BuildServices(); + var workflowInvoker = services.GetRequiredService(); + + await workflowInvoker.StartAsync(); + } + + private static IServiceProvider BuildServices() + { + return new ServiceCollection() + .AddWorkflows() + .AddWorkflow() + .AddConsoleActivities() + .AddActivity() + .BuildServiceProvider(); + } + } +} \ No newline at end of file diff --git a/src/samples/Sample17/Sample17.csproj b/src/samples/Sample17/Sample17.csproj new file mode 100644 index 000000000..32f47e3b5 --- /dev/null +++ b/src/samples/Sample17/Sample17.csproj @@ -0,0 +1,15 @@ + + + + Exe + netcoreapp3.0 + latest + + + + + + + + + From 75e89b6593ea9481979f1b04fb2820edf4a37e2a Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 24 Oct 2019 20:47:40 +0200 Subject: [PATCH 02/13] Update Sample17 --- src/samples/Sample17/CreateDocumentWorkflow.cs | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/samples/Sample17/CreateDocumentWorkflow.cs b/src/samples/Sample17/CreateDocumentWorkflow.cs index 7a9e5bb42..7c7886078 100644 --- a/src/samples/Sample17/CreateDocumentWorkflow.cs +++ b/src/samples/Sample17/CreateDocumentWorkflow.cs @@ -1,8 +1,10 @@ using Elsa.Activities.Console.Activities; +using Elsa.Activities.Primitives; using Elsa.Expressions; using Elsa.Services; using Elsa.Services.Models; using Sample17.Activities; +using Sample17.Models; namespace Sample17 { @@ -14,7 +16,12 @@ namespace Sample17 .StartWith(x => x.TextExpression = new LiteralExpression("Enter a title")) .Then(id: "DocumentTitleInput") .Then(x => x.TitleExpression = new JavaScriptExpression("DocumentTitleInput.Input"), id: "CreateDocument") - .Then(x => x.TextExpression = new JavaScriptExpression("`A new document was created with title \"${CreateDocument.Document.Title}\"`")); + .Then(x => + { + x.VariableName = "Document"; + x.ValueExpression = new JavaScriptExpression("CreateDocument.Document"); + }) + .Then(x => x.TextExpression = new JavaScriptExpression("`A new document was created with title \"${Document.Title}\"`")); } } } \ No newline at end of file From 0ea4cd36718f965ee3bee6254ea17cbc15b926a4 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 24 Oct 2019 20:51:41 +0200 Subject: [PATCH 03/13] Update Sample17 --- src/samples/Sample17/Models/Document.cs | 2 ++ src/samples/Sample17/Program.cs | 7 +++++-- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/src/samples/Sample17/Models/Document.cs b/src/samples/Sample17/Models/Document.cs index 31b3dcff0..4044442db 100644 --- a/src/samples/Sample17/Models/Document.cs +++ b/src/samples/Sample17/Models/Document.cs @@ -3,5 +3,7 @@ public class Document { public string Title { get; set; } + + public override string ToString() => Title; } } \ No newline at end of file diff --git a/src/samples/Sample17/Program.cs b/src/samples/Sample17/Program.cs index eb7d72bad..5a2d3a156 100644 --- a/src/samples/Sample17/Program.cs +++ b/src/samples/Sample17/Program.cs @@ -4,6 +4,7 @@ using Elsa.Activities.Console.Extensions; using Elsa.Services; using Microsoft.Extensions.DependencyInjection; using Sample17.Activities; +using Sample17.Models; namespace Sample17 { @@ -13,8 +14,10 @@ namespace Sample17 { var services = BuildServices(); var workflowInvoker = services.GetRequiredService(); - - await workflowInvoker.StartAsync(); + var workflow = await workflowInvoker.StartAsync(); + var document = workflow.CurrentScope.GetVariable("Document"); + + Console.WriteLine("Created document: {0}", document); } private static IServiceProvider BuildServices() From 82ce170f8da098a944932451fa17794d77e10a84 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 24 Oct 2019 21:45:19 +0200 Subject: [PATCH 04/13] Update Sample17 --- .../Activities/ReadLine.cs | 4 +- .../Services/IActivityBuilder.cs | 2 + .../WorkflowBuilders/ActivityBuilder.cs | 7 ++++ .../{CreateDocument.cs => CreatePerson.cs} | 19 ++++++--- .../Sample17/CreateDocumentWorkflow.cs | 27 ------------ src/samples/Sample17/CreatePersonWorkflow.cs | 41 +++++++++++++++++++ src/samples/Sample17/Models/Document.cs | 9 ---- src/samples/Sample17/Models/Person.cs | 10 +++++ src/samples/Sample17/Program.cs | 9 ++-- 9 files changed, 80 insertions(+), 48 deletions(-) rename src/samples/Sample17/Activities/{CreateDocument.cs => CreatePerson.cs} (56%) delete mode 100644 src/samples/Sample17/CreateDocumentWorkflow.cs create mode 100644 src/samples/Sample17/CreatePersonWorkflow.cs delete mode 100644 src/samples/Sample17/Models/Document.cs create mode 100644 src/samples/Sample17/Models/Person.cs diff --git a/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs b/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs index 9df718795..6bf1a7f43 100644 --- a/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs +++ b/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs @@ -24,7 +24,7 @@ namespace Elsa.Activities.Console.Activities { private readonly TextReader input; - public ReadLine() : this(System.Console.In) + public ReadLine() { } @@ -53,7 +53,7 @@ namespace Elsa.Activities.Console.Activities protected override ActivityExecutionResult OnResume(WorkflowExecutionContext context) { - var receivedInput = (string) context.Workflow.Input[VariableName]; + var receivedInput = (string) context.Workflow.Input["ReadLineInput"]; return Execute(context, receivedInput); } diff --git a/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs b/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs index f97f0c703..d579fc7b5 100644 --- a/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs +++ b/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs @@ -1,4 +1,5 @@ using System; +using System.Linq.Expressions; using Elsa.Models; using Elsa.Services.Models; @@ -12,6 +13,7 @@ namespace Elsa.Services IActivityBuilder Add(Action setup = default, string id = null) where T : class, IActivity; IOutcomeBuilder When(string outcome); IActivityBuilder Then(Action setup = null, Action branch = null, string id = null) where T : class, IActivity; + IActivityBuilder WithId(string id); IWorkflowBuilder Then(string activityId); WorkflowDefinitionVersion Build(); ActivityDefinition BuildActivity(); diff --git a/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs b/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs index 262331a7a..e91129e6a 100644 --- a/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs +++ b/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs @@ -1,5 +1,6 @@ using System; using System.Linq; +using System.Linq.Expressions; using Elsa.Models; using Elsa.Services; using Elsa.Services.Models; @@ -40,6 +41,12 @@ namespace Elsa.WorkflowBuilders return activityBuilder; } + public IActivityBuilder WithId(string id) + { + Id = id; + return this; + } + public IWorkflowBuilder Then(string activityId) { WorkflowBuilder.Connect( diff --git a/src/samples/Sample17/Activities/CreateDocument.cs b/src/samples/Sample17/Activities/CreatePerson.cs similarity index 56% rename from src/samples/Sample17/Activities/CreateDocument.cs rename to src/samples/Sample17/Activities/CreatePerson.cs index 7a2ed8bf5..853f54588 100644 --- a/src/samples/Sample17/Activities/CreateDocument.cs +++ b/src/samples/Sample17/Activities/CreatePerson.cs @@ -9,29 +9,36 @@ using Sample17.Models; namespace Sample17.Activities { - public class CreateDocument : Activity + public class CreatePerson : Activity { private readonly IWorkflowExpressionEvaluator expressionEvaluator; - public CreateDocument(IWorkflowExpressionEvaluator expressionEvaluator) + public CreatePerson(IWorkflowExpressionEvaluator expressionEvaluator) { this.expressionEvaluator = expressionEvaluator; } - + public WorkflowExpression TitleExpression { get => GetState>(); set => SetState(value); } + public WorkflowExpression AgeExpression + { + get => GetState>(); + set => SetState(value); + } + protected override async Task OnExecuteAsync( WorkflowExecutionContext context, CancellationToken cancellationToken) { - var title = await expressionEvaluator.EvaluateAsync(TitleExpression, context, cancellationToken); - var document = new Document { Title = title}; + var name = await expressionEvaluator.EvaluateAsync(TitleExpression, context, cancellationToken); + var age = await expressionEvaluator.EvaluateAsync(AgeExpression, context, cancellationToken); + var person = new Person { FullName = name, Age = age }; - Output["Document"] = document; + Output["Person"] = person; return Done(); } } diff --git a/src/samples/Sample17/CreateDocumentWorkflow.cs b/src/samples/Sample17/CreateDocumentWorkflow.cs deleted file mode 100644 index 7c7886078..000000000 --- a/src/samples/Sample17/CreateDocumentWorkflow.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Elsa.Activities.Console.Activities; -using Elsa.Activities.Primitives; -using Elsa.Expressions; -using Elsa.Services; -using Elsa.Services.Models; -using Sample17.Activities; -using Sample17.Models; - -namespace Sample17 -{ - public class CreateDocumentWorkflow : IWorkflow - { - public void Build(IWorkflowBuilder builder) - { - builder - .StartWith(x => x.TextExpression = new LiteralExpression("Enter a title")) - .Then(id: "DocumentTitleInput") - .Then(x => x.TitleExpression = new JavaScriptExpression("DocumentTitleInput.Input"), id: "CreateDocument") - .Then(x => - { - x.VariableName = "Document"; - x.ValueExpression = new JavaScriptExpression("CreateDocument.Document"); - }) - .Then(x => x.TextExpression = new JavaScriptExpression("`A new document was created with title \"${Document.Title}\"`")); - } - } -} \ No newline at end of file diff --git a/src/samples/Sample17/CreatePersonWorkflow.cs b/src/samples/Sample17/CreatePersonWorkflow.cs new file mode 100644 index 000000000..af843d04f --- /dev/null +++ b/src/samples/Sample17/CreatePersonWorkflow.cs @@ -0,0 +1,41 @@ +using Elsa.Activities.Console.Activities; +using Elsa.Activities.Primitives; +using Elsa.Expressions; +using Elsa.Services; +using Elsa.Services.Models; +using Sample17.Activities; +using Sample17.Models; + +namespace Sample17 +{ + public class CreatePersonWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(x => x.TextExpression = new LiteralExpression("Enter name:")) + .Then().WithId("NameInput") + .Then(x => x.TextExpression = new LiteralExpression("Enter age:")) + .Then().WithId("AgeInput") + .Then( + x => + { + x.TitleExpression = new JavaScriptExpression("NameInput.Input"); + x.AgeExpression = new JavaScriptExpression("AgeInput.Input"); + }).WithId("CreatePerson") + .Then( + x => + { + x.VariableName = "Person"; + x.ValueExpression = new JavaScriptExpression("CreatePerson.Person"); + }) + .Then( + x => + { + x.VariableName = "Age"; + x.ValueExpression = new JavaScriptExpression("CreatePerson.Person.Age"); + }) + .Then(x => x.TextExpression = new JavaScriptExpression("`A new person was created with name \"${Person.FullName}\" and age \"${Age}\"`")); + } + } +} \ No newline at end of file diff --git a/src/samples/Sample17/Models/Document.cs b/src/samples/Sample17/Models/Document.cs deleted file mode 100644 index 4044442db..000000000 --- a/src/samples/Sample17/Models/Document.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Sample17.Models -{ - public class Document - { - public string Title { get; set; } - - public override string ToString() => Title; - } -} \ No newline at end of file diff --git a/src/samples/Sample17/Models/Person.cs b/src/samples/Sample17/Models/Person.cs new file mode 100644 index 000000000..c88f2ea3b --- /dev/null +++ b/src/samples/Sample17/Models/Person.cs @@ -0,0 +1,10 @@ +namespace Sample17.Models +{ + public class Person + { + public string FullName { get; set; } + public int Age { get; set; } + + public override string ToString() => FullName; + } +} \ No newline at end of file diff --git a/src/samples/Sample17/Program.cs b/src/samples/Sample17/Program.cs index 5a2d3a156..11ab1fe04 100644 --- a/src/samples/Sample17/Program.cs +++ b/src/samples/Sample17/Program.cs @@ -14,8 +14,8 @@ namespace Sample17 { var services = BuildServices(); var workflowInvoker = services.GetRequiredService(); - var workflow = await workflowInvoker.StartAsync(); - var document = workflow.CurrentScope.GetVariable("Document"); + var workflowExecutionContext = await workflowInvoker.StartAsync(); + var document = workflowExecutionContext.CurrentScope.GetVariable("Document"); Console.WriteLine("Created document: {0}", document); } @@ -24,9 +24,10 @@ namespace Sample17 { return new ServiceCollection() .AddWorkflows() - .AddWorkflow() + .AddWorkflow() .AddConsoleActivities() - .AddActivity() + .AddActivity() + .AddSingleton(Console.In) .BuildServiceProvider(); } } From b9878f94bb27c332810106c927b70c8aa5000b38 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 24 Oct 2019 22:06:14 +0200 Subject: [PATCH 05/13] Extend SMTP options (#107) * Extend SMTP options * Remove unused imports --- .../Extensions/ServiceCollectionExtensions.cs | 23 ++++++++++++++++++- .../Options/SmtpCredentials.cs | 8 +++++++ .../Options/SmtpOptions.cs | 9 ++++++++ .../Services/IActivityBuilder.cs | 1 - .../WorkflowBuilders/ActivityBuilder.cs | 1 - .../DashboardServiceCollectionExtensions.cs | 1 - .../Extensions/ServiceCollectionExtensions.cs | 1 - .../Services/MongoWorkflowDefinitionStore.cs | 1 - src/samples/Sample01/Program.cs | 1 - src/samples/Sample02/Program.cs | 1 - src/samples/Sample03/Program.cs | 1 - src/samples/Sample04/Program.cs | 1 - src/samples/Sample05/Program.cs | 1 - src/samples/Sample06/Startup.cs | 1 - src/samples/Sample07/Startup.cs | 1 - src/samples/Sample08/Startup.cs | 1 - src/samples/Sample09/Program.cs | 1 - src/samples/Sample10/Program.cs | 1 - src/samples/Sample11/Program.cs | 1 - src/samples/Sample12/Program.cs | 1 - src/samples/Sample13/Program.cs | 1 - 21 files changed, 39 insertions(+), 19 deletions(-) create mode 100644 src/activities/Elsa.Activities.Email/Options/SmtpCredentials.cs diff --git a/src/activities/Elsa.Activities.Email/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Email/Extensions/ServiceCollectionExtensions.cs index fa967fcee..f3bdeff76 100644 --- a/src/activities/Elsa.Activities.Email/Extensions/ServiceCollectionExtensions.cs +++ b/src/activities/Elsa.Activities.Email/Extensions/ServiceCollectionExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Net; using System.Net.Mail; using Elsa.Activities.Email.Activities; using Elsa.Activities.Email.Options; @@ -23,8 +24,28 @@ namespace Elsa.Activities.Email.Extensions private static SmtpClient CreateSmtpClient(IServiceProvider serviceProvider) { var options = serviceProvider.GetRequiredService>().Value; + var smtpClient = new SmtpClient(options.Host, options.Port); + var credentials = options.Credentials; - return new SmtpClient(options.Host, options.Port); + if (credentials != null && !string.IsNullOrWhiteSpace(credentials.Username)) + smtpClient.Credentials = new NetworkCredential(credentials.Username, credentials.Password); + + if (options.Timeout != null) + smtpClient.Timeout = (int)options.Timeout.Value.TotalSeconds; + + if (options.DeliveryFormat != null) + smtpClient.DeliveryFormat = options.DeliveryFormat.Value; + + if (options.DeliveryMethod != null) + smtpClient.DeliveryMethod = options.DeliveryMethod.Value; + + if (options.EnableSsl != null) + smtpClient.EnableSsl = options.EnableSsl.Value; + + if (!string.IsNullOrWhiteSpace(options.PickupDirectoryLocation)) + smtpClient.PickupDirectoryLocation = options.PickupDirectoryLocation; + + return smtpClient; } } } \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Email/Options/SmtpCredentials.cs b/src/activities/Elsa.Activities.Email/Options/SmtpCredentials.cs new file mode 100644 index 000000000..0f814dec6 --- /dev/null +++ b/src/activities/Elsa.Activities.Email/Options/SmtpCredentials.cs @@ -0,0 +1,8 @@ +namespace Elsa.Activities.Email.Options +{ + public class SmtpCredentials + { + public string Username { get; set; } + public string Password { get; set; } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Email/Options/SmtpOptions.cs b/src/activities/Elsa.Activities.Email/Options/SmtpOptions.cs index 5e8184996..25e267de5 100644 --- a/src/activities/Elsa.Activities.Email/Options/SmtpOptions.cs +++ b/src/activities/Elsa.Activities.Email/Options/SmtpOptions.cs @@ -1,3 +1,6 @@ +using System; +using System.Net.Mail; + namespace Elsa.Activities.Email.Options { public class SmtpOptions @@ -5,5 +8,11 @@ namespace Elsa.Activities.Email.Options public string Host { get; set; } public int Port { get; set; } public string DefaultSender { get; set; } + public SmtpCredentials Credentials { get; set; } + public TimeSpan? Timeout { get; set; } + public SmtpDeliveryFormat? DeliveryFormat { get; set; } + public SmtpDeliveryMethod? DeliveryMethod { get; set; } + public bool? EnableSsl { get; set; } + public string PickupDirectoryLocation { get; set; } } } \ 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 d579fc7b5..f521ad1b7 100644 --- a/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs +++ b/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs @@ -1,5 +1,4 @@ using System; -using System.Linq.Expressions; using Elsa.Models; using Elsa.Services.Models; diff --git a/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs b/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs index e91129e6a..bac4dc39e 100644 --- a/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs +++ b/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs @@ -1,6 +1,5 @@ using System; using System.Linq; -using System.Linq.Expressions; using Elsa.Models; using Elsa.Services; using Elsa.Services.Models; diff --git a/src/dashboard/Elsa.Dashboard/Extensions/DashboardServiceCollectionExtensions.cs b/src/dashboard/Elsa.Dashboard/Extensions/DashboardServiceCollectionExtensions.cs index 31c05ec28..6668f35d2 100644 --- a/src/dashboard/Elsa.Dashboard/Extensions/DashboardServiceCollectionExtensions.cs +++ b/src/dashboard/Elsa.Dashboard/Extensions/DashboardServiceCollectionExtensions.cs @@ -4,7 +4,6 @@ using Elsa.Dashboard.ActionFilters; using Elsa.Dashboard.Options; using Elsa.Dashboard.Services; using Elsa.Mapping; -using Elsa.Persistence; using Elsa.Runtime; using Elsa.Serialization; using Elsa.Serialization.Formatters; diff --git a/src/persistence/Elsa.Persistence.MongoDb/Extensions/ServiceCollectionExtensions.cs b/src/persistence/Elsa.Persistence.MongoDb/Extensions/ServiceCollectionExtensions.cs index 422e5ddcb..23b047581 100644 --- a/src/persistence/Elsa.Persistence.MongoDb/Extensions/ServiceCollectionExtensions.cs +++ b/src/persistence/Elsa.Persistence.MongoDb/Extensions/ServiceCollectionExtensions.cs @@ -1,6 +1,5 @@ using System; using Elsa.Models; -using Elsa.Persistence.Memory; using Elsa.Persistence.MongoDb.Serialization; using Elsa.Persistence.MongoDb.Services; using Microsoft.Extensions.Configuration; diff --git a/src/persistence/Elsa.Persistence.MongoDb/Services/MongoWorkflowDefinitionStore.cs b/src/persistence/Elsa.Persistence.MongoDb/Services/MongoWorkflowDefinitionStore.cs index edf82e8ff..e6be71e95 100644 --- a/src/persistence/Elsa.Persistence.MongoDb/Services/MongoWorkflowDefinitionStore.cs +++ b/src/persistence/Elsa.Persistence.MongoDb/Services/MongoWorkflowDefinitionStore.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Linq; using System.Threading; using System.Threading.Tasks; using Elsa.Extensions; diff --git a/src/samples/Sample01/Program.cs b/src/samples/Sample01/Program.cs index f7b90aeab..007889309 100644 --- a/src/samples/Sample01/Program.cs +++ b/src/samples/Sample01/Program.cs @@ -1,6 +1,5 @@ using System; using System.Threading.Tasks; -using Elsa.Persistence.Memory; using Elsa.Services; using Microsoft.Extensions.DependencyInjection; using Sample01.Activities; diff --git a/src/samples/Sample02/Program.cs b/src/samples/Sample02/Program.cs index 17031321c..a7617d642 100644 --- a/src/samples/Sample02/Program.cs +++ b/src/samples/Sample02/Program.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Elsa.Activities.Console.Activities; using Elsa.Activities.Console.Extensions; using Elsa.Expressions; -using Elsa.Persistence.Memory; using Elsa.Services; using Microsoft.Extensions.DependencyInjection; diff --git a/src/samples/Sample03/Program.cs b/src/samples/Sample03/Program.cs index 4b1dbba48..eab461aa2 100644 --- a/src/samples/Sample03/Program.cs +++ b/src/samples/Sample03/Program.cs @@ -5,7 +5,6 @@ using Elsa.Activities.Console.Activities; using Elsa.Activities.Console.Extensions; using Elsa.Expressions; using Elsa.Models; -using Elsa.Persistence.Memory; using Elsa.Services; using Microsoft.Extensions.DependencyInjection; diff --git a/src/samples/Sample04/Program.cs b/src/samples/Sample04/Program.cs index 3a02dc358..b8e1cfb76 100644 --- a/src/samples/Sample04/Program.cs +++ b/src/samples/Sample04/Program.cs @@ -1,7 +1,6 @@ using System; using System.Threading.Tasks; using Elsa.Activities.Console.Extensions; -using Elsa.Persistence.Memory; using Elsa.Services; using Microsoft.Extensions.DependencyInjection; using Sample04.Activities; diff --git a/src/samples/Sample05/Program.cs b/src/samples/Sample05/Program.cs index b45b3a1ad..af6b6c6be 100644 --- a/src/samples/Sample05/Program.cs +++ b/src/samples/Sample05/Program.cs @@ -1,7 +1,6 @@ using System.Threading.Tasks; using Elsa.Activities.Console.Extensions; using Elsa.Activities.Timers.Extensions; -using Elsa.Persistence.Memory; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; diff --git a/src/samples/Sample06/Startup.cs b/src/samples/Sample06/Startup.cs index d9a364a9d..88b6befc0 100644 --- a/src/samples/Sample06/Startup.cs +++ b/src/samples/Sample06/Startup.cs @@ -1,5 +1,4 @@ using Elsa.Activities.Http.Extensions; -using Elsa.Persistence.Memory; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; diff --git a/src/samples/Sample07/Startup.cs b/src/samples/Sample07/Startup.cs index 867502b95..5d77b221c 100644 --- a/src/samples/Sample07/Startup.cs +++ b/src/samples/Sample07/Startup.cs @@ -1,7 +1,6 @@ using Elsa.Activities.Email.Extensions; using Elsa.Activities.Http.Extensions; using Elsa.Activities.Timers.Extensions; -using Elsa.Persistence.Memory; using Elsa.Runtime; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; diff --git a/src/samples/Sample08/Startup.cs b/src/samples/Sample08/Startup.cs index 648f3b588..d129097e8 100644 --- a/src/samples/Sample08/Startup.cs +++ b/src/samples/Sample08/Startup.cs @@ -2,7 +2,6 @@ using Elsa.Activities.Http.Extensions; using Elsa.Activities.MassTransit.Extensions; using Elsa.Activities.Timers.Extensions; -using Elsa.Persistence.Memory; using Elsa.Runtime; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; diff --git a/src/samples/Sample09/Program.cs b/src/samples/Sample09/Program.cs index 3decab293..2f4c4cbc2 100644 --- a/src/samples/Sample09/Program.cs +++ b/src/samples/Sample09/Program.cs @@ -4,7 +4,6 @@ using System.Reflection; using System.Threading.Tasks; using Elsa.Activities.Console.Extensions; using Elsa.Models; -using Elsa.Persistence.Memory; using Elsa.Serialization; using Elsa.Serialization.Formatters; using Elsa.Services; diff --git a/src/samples/Sample10/Program.cs b/src/samples/Sample10/Program.cs index f254c7f1f..ed7b6b5b1 100644 --- a/src/samples/Sample10/Program.cs +++ b/src/samples/Sample10/Program.cs @@ -5,7 +5,6 @@ using Elsa.Activities.Console.Extensions; using Elsa.Extensions; using Elsa.Models; using Elsa.Persistence; -using Elsa.Persistence.Memory; using Elsa.Persistence.YesSql.Extensions; using Elsa.Runtime; using Elsa.Services; diff --git a/src/samples/Sample11/Program.cs b/src/samples/Sample11/Program.cs index 02830ee07..42be5550d 100644 --- a/src/samples/Sample11/Program.cs +++ b/src/samples/Sample11/Program.cs @@ -5,7 +5,6 @@ using Elsa.Activities.Console.Extensions; using Elsa.Activities.Workflows; using Elsa.Extensions; using Elsa.Models; -using Elsa.Persistence.Memory; using Elsa.Services; using Microsoft.Extensions.DependencyInjection; diff --git a/src/samples/Sample12/Program.cs b/src/samples/Sample12/Program.cs index 1f5174753..028e6f648 100644 --- a/src/samples/Sample12/Program.cs +++ b/src/samples/Sample12/Program.cs @@ -6,7 +6,6 @@ using Elsa.Activities.UserTask.Activities; using Elsa.Activities.UserTask.Extensions; using Elsa.Extensions; using Elsa.Models; -using Elsa.Persistence.Memory; using Elsa.Services; using Elsa.Services.Extensions; using Elsa.Services.Models; diff --git a/src/samples/Sample13/Program.cs b/src/samples/Sample13/Program.cs index ccabb44c6..b70d0b866 100644 --- a/src/samples/Sample13/Program.cs +++ b/src/samples/Sample13/Program.cs @@ -1,7 +1,6 @@ using System; using System.Threading.Tasks; using Elsa.Activities.Console.Extensions; -using Elsa.Persistence.Memory; using Elsa.Scripting; using Elsa.Services; using Microsoft.Extensions.DependencyInjection; From 42ddf7ac49b57f052b8bd4e4ed97cadae0d544cd Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 24 Oct 2019 23:33:16 +0200 Subject: [PATCH 06/13] Update Elsa.Scrutor package reference --- src/core/Elsa.Core/Elsa.Core.csproj | 2 +- src/dashboard/Elsa.Dashboard/Elsa.Dashboard.csproj | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/src/core/Elsa.Core/Elsa.Core.csproj b/src/core/Elsa.Core/Elsa.Core.csproj index 58347620c..1e8b11eb6 100644 --- a/src/core/Elsa.Core/Elsa.Core.csproj +++ b/src/core/Elsa.Core/Elsa.Core.csproj @@ -29,7 +29,7 @@ - + diff --git a/src/dashboard/Elsa.Dashboard/Elsa.Dashboard.csproj b/src/dashboard/Elsa.Dashboard/Elsa.Dashboard.csproj index 8b9ac28a7..da6587839 100644 --- a/src/dashboard/Elsa.Dashboard/Elsa.Dashboard.csproj +++ b/src/dashboard/Elsa.Dashboard/Elsa.Dashboard.csproj @@ -21,7 +21,6 @@ - From 99ee3cacce8a0f2fd12c76d9a404c89852080223 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 25 Oct 2019 17:55:39 +0200 Subject: [PATCH 07/13] Downgrade dependencies --- .../Elsa.Activities.Http/Elsa.Activities.Http.csproj | 5 ++--- src/samples/Sample17/Activities/CreatePerson.cs | 3 +++ src/samples/Sample17/CreatePersonWorkflow.cs | 2 +- 3 files changed, 6 insertions(+), 4 deletions(-) diff --git a/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj b/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj index f781b23a9..d2a5da3ab 100644 --- a/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj +++ b/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj @@ -19,12 +19,11 @@ - + - - + diff --git a/src/samples/Sample17/Activities/CreatePerson.cs b/src/samples/Sample17/Activities/CreatePerson.cs index 853f54588..e98d91a3e 100644 --- a/src/samples/Sample17/Activities/CreatePerson.cs +++ b/src/samples/Sample17/Activities/CreatePerson.cs @@ -39,6 +39,9 @@ namespace Sample17.Activities var person = new Person { FullName = name, Age = age }; Output["Person"] = person; + Output["FullName"] = person.FullName; + Output["Age"] = person.Age; + return Done(); } } diff --git a/src/samples/Sample17/CreatePersonWorkflow.cs b/src/samples/Sample17/CreatePersonWorkflow.cs index af843d04f..c4cb7ae71 100644 --- a/src/samples/Sample17/CreatePersonWorkflow.cs +++ b/src/samples/Sample17/CreatePersonWorkflow.cs @@ -35,7 +35,7 @@ namespace Sample17 x.VariableName = "Age"; x.ValueExpression = new JavaScriptExpression("CreatePerson.Person.Age"); }) - .Then(x => x.TextExpression = new JavaScriptExpression("`A new person was created with name \"${Person.FullName}\" and age \"${Age}\"`")); + .Then(x => x.TextExpression = new JavaScriptExpression("`A new person was created with name \"${Person.FullName}\" and age \"${CreatePerson.Age}\"`")); } } } \ No newline at end of file From 06412c0cc0c5b3bd7b2d8a249c52f2be1340bbe0 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 25 Oct 2019 18:39:54 +0200 Subject: [PATCH 08/13] Add support for custom title and description (#109) --- .../Services/ActivityBase.cs | 23 ++++++++++++++++--- .../Areas/Elsa/Views/Shared/_Layout.cshtml | 4 ++-- .../ActivityDescriber.cs | 2 +- 3 files changed, 23 insertions(+), 6 deletions(-) diff --git a/src/core/Elsa.Abstractions/Services/ActivityBase.cs b/src/core/Elsa.Abstractions/Services/ActivityBase.cs index beb439670..43e180f70 100644 --- a/src/core/Elsa.Abstractions/Services/ActivityBase.cs +++ b/src/core/Elsa.Abstractions/Services/ActivityBase.cs @@ -2,6 +2,7 @@ using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; +using Elsa.Attributes; using Elsa.Extensions; using Elsa.Models; using Elsa.Results; @@ -12,16 +13,32 @@ using Newtonsoft.Json.Linq; namespace Elsa.Services { public abstract class ActivityBase : IActivity - { + { public JObject State { get; set; } = new JObject(); public Variables Output { get; set; } = new Variables(); - + [JsonIgnore] public Variables TransientOutput { get; } = new Variables(); - + public virtual string Type => GetType().Name; + + [ActivityProperty(Label = "ID", Hint = "Optionally provide a custom ID for this activity. You can then reference this activity from expressions.")] public string Id { get; set; } + [ActivityProperty(Hint = "Optionally provide a custom title for this activity.")] + public string Title + { + get => GetState(); + set => SetState(value); + } + + [ActivityProperty(Hint = "Optionally provide a custom description for this activity.")] + public string Description + { + get => GetState(); + set => SetState(value); + } + public Task CanExecuteAsync(WorkflowExecutionContext context, CancellationToken cancellationToken) => OnCanExecuteAsync(context, cancellationToken); public Task ExecuteAsync(WorkflowExecutionContext context, CancellationToken cancellationToken) => OnExecuteAsync(context, cancellationToken); public Task HaltedAsync(WorkflowExecutionContext context, CancellationToken cancellationToken) => OnHaltedAsync(context, cancellationToken); 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 33740a359..020830b5c 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.WorkflowDesigner/ActivityDescriber.cs b/src/dashboard/Elsa.WorkflowDesigner/ActivityDescriber.cs index 3893ef73e..73e2a110c 100644 --- a/src/dashboard/Elsa.WorkflowDesigner/ActivityDescriber.cs +++ b/src/dashboard/Elsa.WorkflowDesigner/ActivityDescriber.cs @@ -61,7 +61,7 @@ namespace Elsa.WorkflowDesigner var activityProperty = propertyInfo.GetCustomAttribute(); if (activityProperty == null) - yield break; + continue; yield return new ActivityPropertyDescriptor ( From cb25239fd1f6724bc2409d791f2d305ab31c5b26 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 26 Oct 2019 12:46:20 +0200 Subject: [PATCH 09/13] 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 @@ - - + + From aef8741d849a057890e75d5435e15d154374e17b Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 27 Oct 2019 15:38:37 +0100 Subject: [PATCH 10/13] Add Liquid expressions (#111) * Reorganize projects * Implement Liquid expression evaluator * Refactor activity references to use `Name` --- Elsa.sln | 55 +++- Samples.sln | 62 +++- .../Activities}/ForEach.cs | 139 ++++----- .../Activities}/Fork.cs | 58 ++-- .../Activities}/IfElse.cs | 91 +++--- .../Activities}/Join.cs | 286 +++++++++--------- .../Activities}/Switch.cs | 102 +++---- .../Activities}/While.cs | 5 +- .../Elsa.Activities.ControlFlow.csproj | 12 + .../ControlFlowServiceCollectionExtensions.cs | 21 ++ .../Activities/HttpRequestEvent.cs | 2 +- .../Elsa.Activities.Http.csproj | 2 + ...pActivitiesServiceCollectionExtensions.cs} | 109 +++---- .../HttpJavaScriptHandler.cs} | 27 +- .../Handlers/HttpLiquidHandler.cs | 21 ++ .../Models/HttpRequestModel.cs | 2 +- .../Activities}/Correlate.cs | 2 +- .../Activities}/Finish.cs | 3 +- .../Activities}/Signaled.cs | 2 +- .../Activities}/Start.cs | 2 +- .../Activities}/Trigger.cs | 6 +- .../Activities}/TriggerWorkflow.cs | 3 +- .../Elsa.Activities.Workflows.csproj | 12 + ...flowActivityServiceCollectionExtensions.cs | 19 ++ .../Elsa.Abstractions.csproj | 5 +- .../Models/ActivityDefinition.cs | 8 + src/core/Elsa.Abstractions/OutcomeNames.cs | 1 - .../Scripting/IScriptEngineConfigurator.cs | 10 - .../Services/ActivityBase.cs | 9 +- .../Services/IActivityBuilder.cs | 11 +- .../Services/IOutcomeBuilder.cs | 4 +- .../Services/IWorkflowBuilder.cs | 4 +- .../Services/Models/IActivity.cs | 7 +- .../Services/Models/Workflow.cs | 2 +- .../Models/WorkflowExecutionContext.cs | 22 +- .../{Primitives => }/SetVariable.cs | 108 +++---- src/core/Elsa.Core/Elsa.Core.csproj | 4 +- .../ElsaServiceCollectionExtensions.cs | 39 +-- .../WorkflowBuilders/ActivityBuilder.cs | 26 +- .../WorkflowBuilders/OutcomeBuilder.cs | 8 +- .../WorkflowBuilders/WorkflowBuilder.cs | 28 +- src/core/Elsa/Elsa.csproj | 19 ++ .../Elsa/ElsaServiceCollectionExtensions.cs | 29 ++ .../Elsa.Persistence.MongoDb.csproj | 1 - .../MongoElsaBuilder.cs | 2 +- .../Elsa.Persistence.YesSql.csproj | 1 - src/samples/Sample01/Program.cs | 2 +- src/samples/Sample01/Sample01.csproj | 3 +- src/samples/Sample02/Program.cs | 2 +- src/samples/Sample02/Sample02.csproj | 3 +- src/samples/Sample03/Program.cs | 2 +- src/samples/Sample03/Sample03.csproj | 3 +- src/samples/Sample04/CalculatorWorkflow.cs | 23 +- src/samples/Sample04/Program.cs | 2 +- src/samples/Sample04/Sample04.csproj | 3 +- src/samples/Sample05/Program.cs | 4 +- src/samples/Sample05/RecurringWorkflow.cs | 1 + src/samples/Sample05/Sample05.csproj | 6 +- src/samples/Sample06/Sample06.csproj | 3 +- src/samples/Sample06/Startup.cs | 2 +- .../Sample07/DocumentApprovalWorkflow.cs | 19 +- src/samples/Sample07/Sample07.csproj | 5 +- src/samples/Sample07/Startup.cs | 2 +- src/samples/Sample08/Sample08.csproj | 7 +- src/samples/Sample08/Startup.cs | 2 +- .../Sample08/Workflows/CreateOrderWorkflow.cs | 4 +- .../Sample08/Workflows/HandleOrderWorkflow.cs | 3 +- src/samples/Sample09/Program.cs | 9 +- src/samples/Sample09/Sample09.csproj | 3 +- src/samples/Sample10/Program.cs | 52 ++-- src/samples/Sample10/Sample10.csproj | 6 +- src/samples/Sample11/CorrelationWorkflow.cs | 2 + src/samples/Sample11/Program.cs | 3 +- src/samples/Sample11/Sample11.csproj | 3 +- src/samples/Sample12/Program.cs | 2 +- src/samples/Sample12/Sample12.csproj | 4 +- .../Sample13/ActivityOutputWorkflow.cs | 9 +- src/samples/Sample13/Program.cs | 7 +- src/samples/Sample13/Sample13.csproj | 3 +- .../Sample13/ScriptEngineConfigurator.cs | 11 +- src/samples/Sample14/Program.cs | 48 ++- src/samples/Sample14/Sample14.csproj | 6 +- src/samples/Sample15/Program.cs | 34 +-- src/samples/Sample15/Sample15.csproj | 3 +- src/samples/Sample16/Sample16.csproj | 5 +- src/samples/Sample16/Startup.cs | 2 +- src/samples/Sample17/CreatePersonWorkflow.cs | 11 +- src/samples/Sample17/Program.cs | 4 +- src/samples/Sample17/Sample17.csproj | 3 +- src/samples/Sample18/EmailReminderWorkflow.cs | 50 +++ src/samples/Sample18/Program.cs | 60 ++++ src/samples/Sample18/Sample18.csproj | 16 + .../Elsa.Scripting.JavaScript.csproj | 15 + .../JavaScriptServiceCollectionExtensions.cs | 17 ++ .../CommonJavaScriptEngineHandler.cs} | 110 +++---- .../DateTimeScriptEngineConfigurator.cs | 18 +- .../JavaScriptExpression.cs | 19 +- .../EvaluatingJavaScriptExpression.cs | 18 ++ .../JavaScriptExpressionEvaluator.cs} | 244 ++++++++------- .../Elsa.Scripting.Liquid.csproj | 15 + .../LiquidServiceCollectionExtensions.cs | 29 ++ .../LiquidTemplateManagerExtensions.cs | 18 ++ .../Extensions/TemplateContextExtensions.cs | 23 ++ .../Filters/JsonFilter.cs | 45 +++ .../Handlers/CommonLiquidContextHandler.cs | 51 ++++ .../Helpers/LiquidObjectAccessor.cs | 23 ++ .../Helpers/LiquidPropertyAccessor.cs | 19 ++ .../Elsa.Scripting.Liquid/LiquidExpression.cs | 12 + .../Messages/EvaluatingLiquidExpression.cs | 18 ++ .../Options/LiquidOptions.cs | 10 + .../Services/ILiquidFilter.cs | 11 + .../Services/ILiquidTemplateManager.cs | 23 ++ .../Services/LiquidExpressionEvaluator.cs | 42 +++ .../Services/LiquidTemplateManager.cs | 57 ++++ .../Elsa.AutoMapper.Extensions.csproj | 44 +-- .../NodaTime/InstantProfile.cs | 0 .../ServiceCollectionExtensions.cs | 76 ++--- 117 files changed, 1798 insertions(+), 1007 deletions(-) rename src/{core/Elsa.Core/Activities/ControlFlow => activities/Elsa.Activities.ControlFlow/Activities}/ForEach.cs (95%) rename src/{core/Elsa.Core/Activities/ControlFlow => activities/Elsa.Activities.ControlFlow/Activities}/Fork.cs (92%) rename src/{core/Elsa.Core/Activities/ControlFlow => activities/Elsa.Activities.ControlFlow/Activities}/IfElse.cs (95%) rename src/{core/Elsa.Core/Activities/ControlFlow => activities/Elsa.Activities.ControlFlow/Activities}/Join.cs (96%) rename src/{core/Elsa.Core/Activities/ControlFlow => activities/Elsa.Activities.ControlFlow/Activities}/Switch.cs (95%) rename src/{core/Elsa.Core/Activities/ControlFlow => activities/Elsa.Activities.ControlFlow/Activities}/While.cs (92%) create mode 100644 src/activities/Elsa.Activities.ControlFlow/Elsa.Activities.ControlFlow.csproj create mode 100644 src/activities/Elsa.Activities.ControlFlow/Extensions/ControlFlowServiceCollectionExtensions.cs rename src/activities/Elsa.Activities.Http/Extensions/{ServiceCollectionExtensions.cs => HttpActivitiesServiceCollectionExtensions.cs} (89%) rename src/activities/Elsa.Activities.Http/{Scripting/HttpScriptEngineConfigurator.cs => Handlers/HttpJavaScriptHandler.cs} (66%) create mode 100644 src/activities/Elsa.Activities.Http/Handlers/HttpLiquidHandler.cs rename src/{core/Elsa.Core/Activities/Workflows => activities/Elsa.Activities.Workflows/Activities}/Correlate.cs (96%) rename src/{core/Elsa.Core/Activities/Workflows => activities/Elsa.Activities.Workflows/Activities}/Finish.cs (94%) rename src/{core/Elsa.Core/Activities/Workflows => activities/Elsa.Activities.Workflows/Activities}/Signaled.cs (97%) rename src/{core/Elsa.Core/Activities/Workflows => activities/Elsa.Activities.Workflows/Activities}/Start.cs (90%) rename src/{core/Elsa.Core/Activities/Workflows => activities/Elsa.Activities.Workflows/Activities}/Trigger.cs (93%) rename src/{core/Elsa.Core/Activities/Workflows => activities/Elsa.Activities.Workflows/Activities}/TriggerWorkflow.cs (96%) create mode 100644 src/activities/Elsa.Activities.Workflows/Elsa.Activities.Workflows.csproj create mode 100644 src/activities/Elsa.Activities.Workflows/Extensions/WorkflowActivityServiceCollectionExtensions.cs delete mode 100644 src/core/Elsa.Abstractions/Scripting/IScriptEngineConfigurator.cs rename src/core/Elsa.Core/Activities/{Primitives => }/SetVariable.cs (95%) create mode 100644 src/core/Elsa/Elsa.csproj create mode 100644 src/core/Elsa/ElsaServiceCollectionExtensions.cs create mode 100644 src/samples/Sample18/EmailReminderWorkflow.cs create mode 100644 src/samples/Sample18/Program.cs create mode 100644 src/samples/Sample18/Sample18.csproj create mode 100644 src/scripting/Elsa.Scripting.JavaScript/Elsa.Scripting.JavaScript.csproj create mode 100644 src/scripting/Elsa.Scripting.JavaScript/Extensions/JavaScriptServiceCollectionExtensions.cs rename src/{core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs => scripting/Elsa.Scripting.JavaScript/Handlers/CommonJavaScriptEngineHandler.cs} (58%) rename src/{core/Elsa.Core/Scripting => scripting/Elsa.Scripting.JavaScript/Handlers}/DateTimeScriptEngineConfigurator.cs (84%) rename src/{core/Elsa.Core/Expressions => scripting/Elsa.Scripting.JavaScript}/JavaScriptExpression.cs (51%) create mode 100644 src/scripting/Elsa.Scripting.JavaScript/Messages/EvaluatingJavaScriptExpression.cs rename src/{core/Elsa.Core/Expressions/JavaScriptEvaluator.cs => scripting/Elsa.Scripting.JavaScript/Services/JavaScriptExpressionEvaluator.cs} (75%) create mode 100644 src/scripting/Elsa.Scripting.Liquid/Elsa.Scripting.Liquid.csproj create mode 100644 src/scripting/Elsa.Scripting.Liquid/Extensions/LiquidServiceCollectionExtensions.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Extensions/LiquidTemplateManagerExtensions.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Extensions/TemplateContextExtensions.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Filters/JsonFilter.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Handlers/CommonLiquidContextHandler.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Helpers/LiquidObjectAccessor.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Helpers/LiquidPropertyAccessor.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/LiquidExpression.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Messages/EvaluatingLiquidExpression.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Options/LiquidOptions.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Services/ILiquidFilter.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Services/ILiquidTemplateManager.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Services/LiquidExpressionEvaluator.cs create mode 100644 src/scripting/Elsa.Scripting.Liquid/Services/LiquidTemplateManager.cs rename src/{core => utils}/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj (97%) rename src/{core => utils}/Elsa.AutoMapper.Extensions/NodaTime/InstantProfile.cs (100%) rename src/{core => utils}/Elsa.AutoMapper.Extensions/ServiceCollectionExtensions.cs (97%) diff --git a/Elsa.sln b/Elsa.sln index 47e897968..d72ce7df6 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -37,8 +37,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Persistence.YesSql", " EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Activities.UserTask", "src\activities\Elsa.Activities.UserTask\Elsa.Activities.UserTask.csproj", "{BC0F57E2-AB64-4026-B6E3-E03D9133DB26}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.AutoMapper.Extensions", "src\core\Elsa.AutoMapper.Extensions\Elsa.AutoMapper.Extensions.csproj", "{6CEF6AFB-635D-4FB4-B9EC-0806DD95E1BC}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Elsa.Persistence.EntityFrameworkCore", "src\persistence\Elsa.Persistence.EntityFrameworkCore\Elsa.Persistence.EntityFrameworkCore.csproj", "{349D4C87-E17F-4F51-B493-1994BF631D37}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dashboard", "dashboard", "{8B87D79A-F0DE-4F88-81D3-8C8C69108ADB}" @@ -53,6 +51,22 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Persistence.DocumentDb EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.Dropbox", "src\activities\Elsa.Activities.Dropbox\Elsa.Activities.Dropbox.csproj", "{5A3C9D59-21FE-4BE3-A00A-FF3F198EEC97}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{9E1AF14C-0BEC-4BC2-810C-85FC25A9DDD4}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripting", "scripting", "{5E230C5F-7A64-4E95-A790-DF40CC66DA31}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.AutoMapper.Extensions", "src\utils\Elsa.AutoMapper.Extensions\Elsa.AutoMapper.Extensions.csproj", "{72DA50EC-8A99-4D4F-BF61-DEF105BDAE10}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Scripting.JavaScript", "src\scripting\Elsa.Scripting.JavaScript\Elsa.Scripting.JavaScript.csproj", "{DA91DAF2-4C59-4468-8F1D-6A500D218A1E}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Scripting.Liquid", "src\scripting\Elsa.Scripting.Liquid\Elsa.Scripting.Liquid.csproj", "{9A2D1E3E-328F-4E02-86F8-84974925C1E3}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.ControlFlow", "src\activities\Elsa.Activities.ControlFlow\Elsa.Activities.ControlFlow.csproj", "{3FC85C9D-CC20-4F54-9936-029F19FBFBE1}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.Workflows", "src\activities\Elsa.Activities.Workflows\Elsa.Activities.Workflows.csproj", "{1BB8EC9E-AEBD-438B-9FA7-B7FA78999D7D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa", "src\core\Elsa\Elsa.csproj", "{ED2A4FE5-821B-46F7-B72F-E66DE0D66C93}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -95,10 +109,6 @@ Global {BC0F57E2-AB64-4026-B6E3-E03D9133DB26}.Debug|Any CPU.Build.0 = Debug|Any CPU {BC0F57E2-AB64-4026-B6E3-E03D9133DB26}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC0F57E2-AB64-4026-B6E3-E03D9133DB26}.Release|Any CPU.Build.0 = Release|Any CPU - {6CEF6AFB-635D-4FB4-B9EC-0806DD95E1BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {6CEF6AFB-635D-4FB4-B9EC-0806DD95E1BC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {6CEF6AFB-635D-4FB4-B9EC-0806DD95E1BC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {6CEF6AFB-635D-4FB4-B9EC-0806DD95E1BC}.Release|Any CPU.Build.0 = Release|Any CPU {349D4C87-E17F-4F51-B493-1994BF631D37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {349D4C87-E17F-4F51-B493-1994BF631D37}.Debug|Any CPU.Build.0 = Debug|Any CPU {349D4C87-E17F-4F51-B493-1994BF631D37}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -123,6 +133,30 @@ Global {5A3C9D59-21FE-4BE3-A00A-FF3F198EEC97}.Debug|Any CPU.Build.0 = Debug|Any CPU {5A3C9D59-21FE-4BE3-A00A-FF3F198EEC97}.Release|Any CPU.ActiveCfg = Release|Any CPU {5A3C9D59-21FE-4BE3-A00A-FF3F198EEC97}.Release|Any CPU.Build.0 = Release|Any CPU + {72DA50EC-8A99-4D4F-BF61-DEF105BDAE10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {72DA50EC-8A99-4D4F-BF61-DEF105BDAE10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {72DA50EC-8A99-4D4F-BF61-DEF105BDAE10}.Release|Any CPU.ActiveCfg = Release|Any CPU + {72DA50EC-8A99-4D4F-BF61-DEF105BDAE10}.Release|Any CPU.Build.0 = Release|Any CPU + {DA91DAF2-4C59-4468-8F1D-6A500D218A1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DA91DAF2-4C59-4468-8F1D-6A500D218A1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DA91DAF2-4C59-4468-8F1D-6A500D218A1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DA91DAF2-4C59-4468-8F1D-6A500D218A1E}.Release|Any CPU.Build.0 = Release|Any CPU + {9A2D1E3E-328F-4E02-86F8-84974925C1E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9A2D1E3E-328F-4E02-86F8-84974925C1E3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9A2D1E3E-328F-4E02-86F8-84974925C1E3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9A2D1E3E-328F-4E02-86F8-84974925C1E3}.Release|Any CPU.Build.0 = Release|Any CPU + {3FC85C9D-CC20-4F54-9936-029F19FBFBE1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3FC85C9D-CC20-4F54-9936-029F19FBFBE1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3FC85C9D-CC20-4F54-9936-029F19FBFBE1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3FC85C9D-CC20-4F54-9936-029F19FBFBE1}.Release|Any CPU.Build.0 = Release|Any CPU + {1BB8EC9E-AEBD-438B-9FA7-B7FA78999D7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1BB8EC9E-AEBD-438B-9FA7-B7FA78999D7D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1BB8EC9E-AEBD-438B-9FA7-B7FA78999D7D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1BB8EC9E-AEBD-438B-9FA7-B7FA78999D7D}.Release|Any CPU.Build.0 = Release|Any CPU + {ED2A4FE5-821B-46F7-B72F-E66DE0D66C93}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED2A4FE5-821B-46F7-B72F-E66DE0D66C93}.Debug|Any CPU.Build.0 = Debug|Any CPU + {ED2A4FE5-821B-46F7-B72F-E66DE0D66C93}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED2A4FE5-821B-46F7-B72F-E66DE0D66C93}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -140,7 +174,6 @@ Global {76710230-AC6D-4E54-9A06-15F34C155118} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} {4DB0741F-EAD9-4E37-862D-A1DA12462B7D} = {76710230-AC6D-4E54-9A06-15F34C155118} {BC0F57E2-AB64-4026-B6E3-E03D9133DB26} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} - {6CEF6AFB-635D-4FB4-B9EC-0806DD95E1BC} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3} {349D4C87-E17F-4F51-B493-1994BF631D37} = {76710230-AC6D-4E54-9A06-15F34C155118} {8B87D79A-F0DE-4F88-81D3-8C8C69108ADB} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} {493FC3E3-34FE-4F8D-AF37-38A5F5541FBD} = {8B87D79A-F0DE-4F88-81D3-8C8C69108ADB} @@ -148,6 +181,14 @@ Global {120E4BF9-4A42-4BB9-A0B2-9597160D6E37} = {8B87D79A-F0DE-4F88-81D3-8C8C69108ADB} {39EBECE7-538B-431C-AAB7-901AB16E6D4A} = {76710230-AC6D-4E54-9A06-15F34C155118} {5A3C9D59-21FE-4BE3-A00A-FF3F198EEC97} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} + {9E1AF14C-0BEC-4BC2-810C-85FC25A9DDD4} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} + {5E230C5F-7A64-4E95-A790-DF40CC66DA31} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} + {72DA50EC-8A99-4D4F-BF61-DEF105BDAE10} = {9E1AF14C-0BEC-4BC2-810C-85FC25A9DDD4} + {DA91DAF2-4C59-4468-8F1D-6A500D218A1E} = {5E230C5F-7A64-4E95-A790-DF40CC66DA31} + {9A2D1E3E-328F-4E02-86F8-84974925C1E3} = {5E230C5F-7A64-4E95-A790-DF40CC66DA31} + {3FC85C9D-CC20-4F54-9936-029F19FBFBE1} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} + {1BB8EC9E-AEBD-438B-9FA7-B7FA78999D7D} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} + {ED2A4FE5-821B-46F7-B72F-E66DE0D66C93} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158} diff --git a/Samples.sln b/Samples.sln index 5dab74820..59fe19861 100644 --- a/Samples.sln +++ b/Samples.sln @@ -57,8 +57,6 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Persistence.YesSql", " EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample10", "src\samples\Sample10\Sample10.csproj", "{E0076858-353F-4302-9690-920204E38BF6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.AutoMapper.Extensions", "src\core\Elsa.AutoMapper.Extensions\Elsa.AutoMapper.Extensions.csproj", "{F7633BE5-0335-4148-8FB7-EAED9A2276A2}" -EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample11", "src\samples\Sample11\Sample11.csproj", "{0C3BB425-2FED-4ADF-8DBE-D68DF0CA5A41}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.UserTask", "src\activities\Elsa.Activities.UserTask\Elsa.Activities.UserTask.csproj", "{823F80B6-E241-43F7-83BD-29FBB58A77F9}" @@ -89,6 +87,24 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample16", "src\samples\Sam EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample17", "src\samples\Sample17\Sample17.csproj", "{8854DF17-D566-49C9-8210-68AB8F5659FA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "scripting", "scripting", "{9C3C685E-3D7D-4638-A031-24F71CF74B52}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.ControlFlow", "src\activities\Elsa.Activities.ControlFlow\Elsa.Activities.ControlFlow.csproj", "{1F8E7DFB-824E-4B9F-9866-482B191CF005}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.Workflows", "src\activities\Elsa.Activities.Workflows\Elsa.Activities.Workflows.csproj", "{9017817D-A3D1-4068-97BF-FCA1EF3BFC50}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa", "src\core\Elsa\Elsa.csproj", "{6BB5EB72-AD9A-41ED-A6AA-0FF1093EA41B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Scripting.JavaScript", "src\scripting\Elsa.Scripting.JavaScript\Elsa.Scripting.JavaScript.csproj", "{18BE8F72-E528-4617-B1B1-07BD35337DFC}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Scripting.Liquid", "src\scripting\Elsa.Scripting.Liquid\Elsa.Scripting.Liquid.csproj", "{01DC21DD-C6E2-4B21-864E-97D29448F777}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "utils", "utils", "{0D276728-DDA8-46A0-91A9-D44401D66DA9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.AutoMapper.Extensions", "src\utils\Elsa.AutoMapper.Extensions\Elsa.AutoMapper.Extensions.csproj", "{102626A4-8BA6-45A8-A69C-F5EB1F155BE7}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample18", "src\samples\Sample18\Sample18.csproj", "{616AA0BA-FBDD-4ABE-B688-A3F79F38C698}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -167,10 +183,6 @@ Global {E0076858-353F-4302-9690-920204E38BF6}.Debug|Any CPU.Build.0 = Debug|Any CPU {E0076858-353F-4302-9690-920204E38BF6}.Release|Any CPU.ActiveCfg = Release|Any CPU {E0076858-353F-4302-9690-920204E38BF6}.Release|Any CPU.Build.0 = Release|Any CPU - {F7633BE5-0335-4148-8FB7-EAED9A2276A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F7633BE5-0335-4148-8FB7-EAED9A2276A2}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F7633BE5-0335-4148-8FB7-EAED9A2276A2}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F7633BE5-0335-4148-8FB7-EAED9A2276A2}.Release|Any CPU.Build.0 = Release|Any CPU {0C3BB425-2FED-4ADF-8DBE-D68DF0CA5A41}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {0C3BB425-2FED-4ADF-8DBE-D68DF0CA5A41}.Debug|Any CPU.Build.0 = Debug|Any CPU {0C3BB425-2FED-4ADF-8DBE-D68DF0CA5A41}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -227,6 +239,34 @@ Global {8854DF17-D566-49C9-8210-68AB8F5659FA}.Debug|Any CPU.Build.0 = Debug|Any CPU {8854DF17-D566-49C9-8210-68AB8F5659FA}.Release|Any CPU.ActiveCfg = Release|Any CPU {8854DF17-D566-49C9-8210-68AB8F5659FA}.Release|Any CPU.Build.0 = Release|Any CPU + {1F8E7DFB-824E-4B9F-9866-482B191CF005}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F8E7DFB-824E-4B9F-9866-482B191CF005}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1F8E7DFB-824E-4B9F-9866-482B191CF005}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F8E7DFB-824E-4B9F-9866-482B191CF005}.Release|Any CPU.Build.0 = Release|Any CPU + {9017817D-A3D1-4068-97BF-FCA1EF3BFC50}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9017817D-A3D1-4068-97BF-FCA1EF3BFC50}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9017817D-A3D1-4068-97BF-FCA1EF3BFC50}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9017817D-A3D1-4068-97BF-FCA1EF3BFC50}.Release|Any CPU.Build.0 = Release|Any CPU + {6BB5EB72-AD9A-41ED-A6AA-0FF1093EA41B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BB5EB72-AD9A-41ED-A6AA-0FF1093EA41B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BB5EB72-AD9A-41ED-A6AA-0FF1093EA41B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BB5EB72-AD9A-41ED-A6AA-0FF1093EA41B}.Release|Any CPU.Build.0 = Release|Any CPU + {18BE8F72-E528-4617-B1B1-07BD35337DFC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {18BE8F72-E528-4617-B1B1-07BD35337DFC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {18BE8F72-E528-4617-B1B1-07BD35337DFC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {18BE8F72-E528-4617-B1B1-07BD35337DFC}.Release|Any CPU.Build.0 = Release|Any CPU + {01DC21DD-C6E2-4B21-864E-97D29448F777}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {01DC21DD-C6E2-4B21-864E-97D29448F777}.Debug|Any CPU.Build.0 = Debug|Any CPU + {01DC21DD-C6E2-4B21-864E-97D29448F777}.Release|Any CPU.ActiveCfg = Release|Any CPU + {01DC21DD-C6E2-4B21-864E-97D29448F777}.Release|Any CPU.Build.0 = Release|Any CPU + {102626A4-8BA6-45A8-A69C-F5EB1F155BE7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {102626A4-8BA6-45A8-A69C-F5EB1F155BE7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {102626A4-8BA6-45A8-A69C-F5EB1F155BE7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {102626A4-8BA6-45A8-A69C-F5EB1F155BE7}.Release|Any CPU.Build.0 = Release|Any CPU + {616AA0BA-FBDD-4ABE-B688-A3F79F38C698}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {616AA0BA-FBDD-4ABE-B688-A3F79F38C698}.Debug|Any CPU.Build.0 = Debug|Any CPU + {616AA0BA-FBDD-4ABE-B688-A3F79F38C698}.Release|Any CPU.ActiveCfg = Release|Any CPU + {616AA0BA-FBDD-4ABE-B688-A3F79F38C698}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -253,7 +293,6 @@ Global {600C4AE0-0585-4181-9CEC-2BB43430C713} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} {905C1067-2119-443D-B5FE-DA8E027FAD62} = {600C4AE0-0585-4181-9CEC-2BB43430C713} {E0076858-353F-4302-9690-920204E38BF6} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} - {F7633BE5-0335-4148-8FB7-EAED9A2276A2} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3} {0C3BB425-2FED-4ADF-8DBE-D68DF0CA5A41} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} {823F80B6-E241-43F7-83BD-29FBB58A77F9} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} {D0F68FBB-17C6-421E-B8AE-AAAFE111BF0B} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} @@ -270,6 +309,15 @@ Global {8C51A528-F9B5-464E-AAD1-1ADC44697085} = {600C4AE0-0585-4181-9CEC-2BB43430C713} {DFE72CF4-C77C-4932-A00F-A8D325E603CE} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} {8854DF17-D566-49C9-8210-68AB8F5659FA} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} + {9C3C685E-3D7D-4638-A031-24F71CF74B52} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} + {1F8E7DFB-824E-4B9F-9866-482B191CF005} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} + {9017817D-A3D1-4068-97BF-FCA1EF3BFC50} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} + {6BB5EB72-AD9A-41ED-A6AA-0FF1093EA41B} = {35F44BE9-13D0-417C-A01A-F0787BEE6DC3} + {18BE8F72-E528-4617-B1B1-07BD35337DFC} = {9C3C685E-3D7D-4638-A031-24F71CF74B52} + {01DC21DD-C6E2-4B21-864E-97D29448F777} = {9C3C685E-3D7D-4638-A031-24F71CF74B52} + {0D276728-DDA8-46A0-91A9-D44401D66DA9} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925} + {102626A4-8BA6-45A8-A69C-F5EB1F155BE7} = {0D276728-DDA8-46A0-91A9-D44401D66DA9} + {616AA0BA-FBDD-4ABE-B688-A3F79F38C698} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158} diff --git a/src/core/Elsa.Core/Activities/ControlFlow/ForEach.cs b/src/activities/Elsa.Activities.ControlFlow/Activities/ForEach.cs similarity index 95% rename from src/core/Elsa.Core/Activities/ControlFlow/ForEach.cs rename to src/activities/Elsa.Activities.ControlFlow/Activities/ForEach.cs index bed3b6707..a0ed42d01 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/ForEach.cs +++ b/src/activities/Elsa.Activities.ControlFlow/Activities/ForEach.cs @@ -1,70 +1,71 @@ -using System.Collections.Generic; -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 = "Iterate over a collection.", Icon = "far fa-circle")] - public class ForEach : Activity - { - private readonly IWorkflowExpressionEvaluator expressionEvaluator; - - public ForEach(IWorkflowExpressionEvaluator expressionEvaluator) - { - this.expressionEvaluator = expressionEvaluator; - } - - [ActivityProperty(Hint = "Enter an expression that evaluates to an array of items to iterate over.")] - public WorkflowExpression> CollectionExpression - { - get => GetState(() => new JavaScriptExpression>("[]")); - set => SetState(value); - } - - [ActivityProperty(Hint = "Enter a name for the iterator variable.")] - public string IteratorName - { - get => GetState(() => "CurrentValue"); - set => SetState(value); - } - - public int CurrentIndex - { - get => GetState(() => 0); - set => SetState(value); - } - - protected override async Task OnExecuteAsync( - WorkflowExecutionContext context, - CancellationToken cancellationToken) - { - var collection = await expressionEvaluator.EvaluateAsync(CollectionExpression, context, cancellationToken); - var index = CurrentIndex; - - if (index >= collection.Count) - { - context.EndScope(); - CurrentIndex = 0; - return Done(); - } - - var value = collection[index]; - CurrentIndex++; - - if (index == 0) - { - context.BeginScope(); - } - - context.CurrentScope.SetVariable(IteratorName, value); - - return Outcome(OutcomeNames.Iterate); - } - } +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Attributes; +using Elsa.Expressions; +using Elsa.Extensions; +using Elsa.Results; +using Elsa.Scripting.JavaScript; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Activities.ControlFlow.Activities +{ + [ActivityDefinition(Category = "Control Flow", Description = "Iterate over a collection.", Icon = "far fa-circle")] + public class ForEach : Activity + { + private readonly IWorkflowExpressionEvaluator expressionEvaluator; + + public ForEach(IWorkflowExpressionEvaluator expressionEvaluator) + { + this.expressionEvaluator = expressionEvaluator; + } + + [ActivityProperty(Hint = "Enter an expression that evaluates to an array of items to iterate over.")] + public WorkflowExpression> CollectionExpression + { + get => GetState(() => new JavaScriptExpression>("[]")); + set => SetState(value); + } + + [ActivityProperty(Hint = "Enter a name for the iterator variable.")] + public string IteratorName + { + get => GetState(() => "CurrentValue"); + set => SetState(value); + } + + public int CurrentIndex + { + get => GetState(() => 0); + set => SetState(value); + } + + protected override async Task OnExecuteAsync( + WorkflowExecutionContext context, + CancellationToken cancellationToken) + { + var collection = await expressionEvaluator.EvaluateAsync(CollectionExpression, context, cancellationToken); + var index = CurrentIndex; + + if (index >= collection.Count) + { + context.EndScope(); + CurrentIndex = 0; + return Done(); + } + + var value = collection[index]; + CurrentIndex++; + + if (index == 0) + { + context.BeginScope(); + } + + context.CurrentScope.SetVariable(IteratorName, value); + + return Outcome(OutcomeNames.Iterate); + } + } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Fork.cs b/src/activities/Elsa.Activities.ControlFlow/Activities/Fork.cs similarity index 92% rename from src/core/Elsa.Core/Activities/ControlFlow/Fork.cs rename to src/activities/Elsa.Activities.ControlFlow/Activities/Fork.cs index 0d9e7aaf5..815831736 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/Fork.cs +++ b/src/activities/Elsa.Activities.ControlFlow/Activities/Fork.cs @@ -1,30 +1,30 @@ -using System.Collections.Generic; -using Elsa.Attributes; -using Elsa.Results; -using Elsa.Services; -using Elsa.Services.Models; - -namespace Elsa.Activities.ControlFlow -{ - [ActivityDefinition( - Category = "Control Flow", - Description = "Fork workflow execution into multiple branches.", - Icon = "fas fa-code-branch fa-rotate-180", - Outcomes = "x => x.state.branches")] - public class Fork : Activity - { - [ActivityProperty( - Hint = "Enter one or more names representing branches, separated with a comma. Example: Branch 1, Branch 2" - )] - public IList Branches - { - get => GetState(() => new List()); - set => SetState(value); - } - - protected override ActivityExecutionResult OnExecute(WorkflowExecutionContext workflowContext) - { - return Outcomes(Branches); - } - } +using System.Collections.Generic; +using Elsa.Attributes; +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Activities.ControlFlow.Activities +{ + [ActivityDefinition( + Category = "Control Flow", + Description = "Fork workflow execution into multiple branches.", + Icon = "fas fa-code-branch fa-rotate-180", + Outcomes = "x => x.state.branches")] + public class Fork : Activity + { + [ActivityProperty( + Hint = "Enter one or more names representing branches, separated with a comma. Example: Branch 1, Branch 2" + )] + public IList Branches + { + get => GetState(() => new List()); + set => SetState(value); + } + + protected override ActivityExecutionResult OnExecute(WorkflowExecutionContext workflowContext) + { + return Outcomes(Branches); + } + } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/IfElse.cs b/src/activities/Elsa.Activities.ControlFlow/Activities/IfElse.cs similarity index 95% rename from src/core/Elsa.Core/Activities/ControlFlow/IfElse.cs rename to src/activities/Elsa.Activities.ControlFlow/Activities/IfElse.cs index f58316cae..887e9da77 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/IfElse.cs +++ b/src/activities/Elsa.Activities.ControlFlow/Activities/IfElse.cs @@ -1,46 +1,47 @@ -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( - DisplayName = "If/Else", - Category = "Control Flow", - 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 - { - private readonly IWorkflowExpressionEvaluator expressionEvaluator; - - public IfElse(IWorkflowExpressionEvaluator expressionEvaluator) - { - this.expressionEvaluator = expressionEvaluator; - } - - [ActivityProperty(Hint = "The expression to evaluate. The evaluated value will be used to switch on.")] - public WorkflowExpression ConditionExpression - { - get => GetState(() => new JavaScriptExpression("true")); - set => SetState(value); - } - - protected override async Task OnExecuteAsync(WorkflowExecutionContext workflowContext, - CancellationToken cancellationToken) - { - var result = await expressionEvaluator.EvaluateAsync( - ConditionExpression, - workflowContext, - cancellationToken - ); - return Outcome(result ? OutcomeNames.True : OutcomeNames.False); - } - } +using System.Threading; +using System.Threading.Tasks; +using Elsa.Attributes; +using Elsa.Expressions; +using Elsa.Extensions; +using Elsa.Results; +using Elsa.Scripting.JavaScript; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Activities.ControlFlow.Activities +{ + [ActivityDefinition( + DisplayName = "If/Else", + Category = "Control Flow", + 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 + { + private readonly IWorkflowExpressionEvaluator expressionEvaluator; + + public IfElse(IWorkflowExpressionEvaluator expressionEvaluator) + { + this.expressionEvaluator = expressionEvaluator; + } + + [ActivityProperty(Hint = "The expression to evaluate. The evaluated value will be used to switch on.")] + public WorkflowExpression ConditionExpression + { + get => GetState(() => new JavaScriptExpression("true")); + set => SetState(value); + } + + protected override async Task OnExecuteAsync(WorkflowExecutionContext workflowContext, + CancellationToken cancellationToken) + { + var result = await expressionEvaluator.EvaluateAsync( + ConditionExpression, + workflowContext, + cancellationToken + ); + return Outcome(result ? OutcomeNames.True : OutcomeNames.False); + } + } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Join.cs b/src/activities/Elsa.Activities.ControlFlow/Activities/Join.cs similarity index 96% rename from src/core/Elsa.Core/Activities/ControlFlow/Join.cs rename to src/activities/Elsa.Activities.ControlFlow/Activities/Join.cs index dc8ed556a..e977d6133 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/Join.cs +++ b/src/activities/Elsa.Activities.ControlFlow/Activities/Join.cs @@ -1,144 +1,144 @@ -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Elsa.Attributes; -using Elsa.Design; -using Elsa.Results; -using Elsa.Services; -using Elsa.Services.Extensions; -using Elsa.Services.Models; - -namespace Elsa.Activities.ControlFlow -{ - [ActivityDefinition( - Category = "Control Flow", - Description = "Merge workflow execution back into a single branch.", - 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 - { - public Join() - { - InboundTransitions = new List().AsReadOnly(); - } - - public enum JoinMode - { - WaitAll, - WaitAny - } - - [ActivityProperty( - Type = ActivityPropertyTypes.Select, - Hint = "Either 'WaitAll' or 'WaitAny'") - ] - [SelectOptions("WaitAll", "WaitAny")] - public JoinMode Mode - { - get => GetState(() => JoinMode.WaitAll); - set => SetState(value); - } - - public IReadOnlyCollection InboundTransitions - { - get => GetState>(); - set => SetState(value); - } - - protected override ActivityExecutionResult OnExecute(WorkflowExecutionContext workflowContext) - { - var recordedInboundTransitions = InboundTransitions ?? new List(); - var workflow = workflowContext.Workflow; - var inboundConnections = workflow.GetInboundConnections(Id); - var done = false; - - switch (Mode) - { - case JoinMode.WaitAll: - done = inboundConnections.All(x => recordedInboundTransitions.Contains(GetTransitionKey(x))); - break; - case JoinMode.WaitAny: - done = inboundConnections.Any(x => recordedInboundTransitions.Contains(GetTransitionKey(x))); - break; - } - - if (done) - { - // Remove any inbound blocking activities. - var ancestorActivityIds = workflow.GetInboundActivityPath(Id).ToList(); - var blockingActivities = - workflow.BlockingActivities.Where(x => ancestorActivityIds.Contains(x.Id)).ToList(); - - foreach (var blockingActivity in blockingActivities) - { - workflow.BlockingActivities.Remove(blockingActivity); - } - } - - return done ? Done() : Noop(); - } - - private void RecordInboundTransitions(WorkflowExecutionContext workflowContext, IActivity activity) - { - var workflow = workflowContext.Workflow; - - // Get outbound connections of the executing activity. - var outboundConnections = workflow.GetOutboundConnections(activity.Id); - - // Get any connection that is pointing to this activity. - var inboundTransitionsQuery = - from connection in outboundConnections - let destinationActivity = connection.Target.Activity - where destinationActivity.Type == nameof(Join) - select connection; - - var inboundConnections = inboundTransitionsQuery.ToList(); - - // For each inbound connection, record the transition. - foreach (var inboundConnection in inboundConnections) - { - var joinActivity = (Join)inboundConnection.Target.Activity; - var inboundTransitions = joinActivity.InboundTransitions ?? new List(); - joinActivity.InboundTransitions = inboundTransitions - .Union(new[] { GetTransitionKey(inboundConnection) }) - .Distinct() - .ToList(); - } - } - - private string GetTransitionKey(Connection connection) - { - var sourceActivityId = connection.Source.Activity.Id; - var sourceOutcomeName = connection.Source.Outcome; - - return $"@{sourceActivityId}_{sourceOutcomeName}"; - } - - Task IWorkflowEventHandler.ActivityExecutedAsync( - WorkflowExecutionContext workflowContext, - IActivity activity, - CancellationToken cancellationToken) - { - RecordInboundTransitions(workflowContext, activity); - - return Task.CompletedTask; - } - - public Task ActivityFaultedAsync( - WorkflowExecutionContext workflowExecutionContext, - IActivity activity, - string message, - CancellationToken cancellationToken) => Task.CompletedTask; - - Task IWorkflowEventHandler.InvokingHaltedActivitiesAsync( - WorkflowExecutionContext workflowExecutionContext, - CancellationToken cancellationToken) => Task.CompletedTask; - - Task IWorkflowEventHandler.WorkflowInvokedAsync( - WorkflowExecutionContext workflowExecutionContext, - CancellationToken cancellationToken) => Task.CompletedTask; - } +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Attributes; +using Elsa.Design; +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Extensions; +using Elsa.Services.Models; + +namespace Elsa.Activities.ControlFlow.Activities +{ + [ActivityDefinition( + Category = "Control Flow", + Description = "Merge workflow execution back into a single branch.", + 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 + { + public Join() + { + InboundTransitions = new List().AsReadOnly(); + } + + public enum JoinMode + { + WaitAll, + WaitAny + } + + [ActivityProperty( + Type = ActivityPropertyTypes.Select, + Hint = "Either 'WaitAll' or 'WaitAny'") + ] + [SelectOptions("WaitAll", "WaitAny")] + public JoinMode Mode + { + get => GetState(() => JoinMode.WaitAll); + set => SetState(value); + } + + public IReadOnlyCollection InboundTransitions + { + get => GetState>(); + set => SetState(value); + } + + protected override ActivityExecutionResult OnExecute(WorkflowExecutionContext workflowContext) + { + var recordedInboundTransitions = InboundTransitions ?? new List(); + var workflow = workflowContext.Workflow; + var inboundConnections = workflow.GetInboundConnections(Id); + var done = false; + + switch (Mode) + { + case JoinMode.WaitAll: + done = inboundConnections.All(x => recordedInboundTransitions.Contains(GetTransitionKey(x))); + break; + case JoinMode.WaitAny: + done = inboundConnections.Any(x => recordedInboundTransitions.Contains(GetTransitionKey(x))); + break; + } + + if (done) + { + // Remove any inbound blocking activities. + var ancestorActivityIds = workflow.GetInboundActivityPath(Id).ToList(); + var blockingActivities = + workflow.BlockingActivities.Where(x => ancestorActivityIds.Contains(x.Id)).ToList(); + + foreach (var blockingActivity in blockingActivities) + { + workflow.BlockingActivities.Remove(blockingActivity); + } + } + + return done ? Done() : Noop(); + } + + private void RecordInboundTransitions(WorkflowExecutionContext workflowContext, IActivity activity) + { + var workflow = workflowContext.Workflow; + + // Get outbound connections of the executing activity. + var outboundConnections = workflow.GetOutboundConnections(activity.Id); + + // Get any connection that is pointing to this activity. + var inboundTransitionsQuery = + from connection in outboundConnections + let destinationActivity = connection.Target.Activity + where destinationActivity.Type == nameof(Join) + select connection; + + var inboundConnections = inboundTransitionsQuery.ToList(); + + // For each inbound connection, record the transition. + foreach (var inboundConnection in inboundConnections) + { + var joinActivity = (Join)inboundConnection.Target.Activity; + var inboundTransitions = joinActivity.InboundTransitions ?? new List(); + joinActivity.InboundTransitions = inboundTransitions + .Union(new[] { GetTransitionKey(inboundConnection) }) + .Distinct() + .ToList(); + } + } + + private string GetTransitionKey(Connection connection) + { + var sourceActivityId = connection.Source.Activity.Id; + var sourceOutcomeName = connection.Source.Outcome; + + return $"@{sourceActivityId}_{sourceOutcomeName}"; + } + + Task IWorkflowEventHandler.ActivityExecutedAsync( + WorkflowExecutionContext workflowContext, + IActivity activity, + CancellationToken cancellationToken) + { + RecordInboundTransitions(workflowContext, activity); + + return Task.CompletedTask; + } + + public Task ActivityFaultedAsync( + WorkflowExecutionContext workflowExecutionContext, + IActivity activity, + string message, + CancellationToken cancellationToken) => Task.CompletedTask; + + Task IWorkflowEventHandler.InvokingHaltedActivitiesAsync( + WorkflowExecutionContext workflowExecutionContext, + CancellationToken cancellationToken) => Task.CompletedTask; + + Task IWorkflowEventHandler.WorkflowInvokedAsync( + WorkflowExecutionContext workflowExecutionContext, + CancellationToken cancellationToken) => Task.CompletedTask; + } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/Switch.cs b/src/activities/Elsa.Activities.ControlFlow/Activities/Switch.cs similarity index 95% rename from src/core/Elsa.Core/Activities/ControlFlow/Switch.cs rename to src/activities/Elsa.Activities.ControlFlow/Activities/Switch.cs index 4fdf11895..2e1382023 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/Switch.cs +++ b/src/activities/Elsa.Activities.ControlFlow/Activities/Switch.cs @@ -1,52 +1,52 @@ -using System.Collections.Generic; -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 = "Switch execution based on a given expression.", - 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 - { - private readonly IWorkflowExpressionEvaluator expressionEvaluator; - - public Switch(IWorkflowExpressionEvaluator expressionEvaluator) - { - this.expressionEvaluator = expressionEvaluator; - Cases = new List(); - } - - [ActivityProperty(Hint = "The expression to evaluate. The evaluated value will be used to switch on.")] - public WorkflowExpression Expression - { - get => GetState>(); - set => SetState(value); - } - - [ActivityProperty(Hint = "A comma-separated list of possible outcomes of the expression.")] - public IReadOnlyCollection Cases - { - get => GetState>(); - set => SetState(value); - } - - protected override async Task OnExecuteAsync( - WorkflowExecutionContext workflowContext, - CancellationToken cancellationToken) - { - var result = await expressionEvaluator.EvaluateAsync(Expression, workflowContext, cancellationToken); - return Outcome(result); - } - } +using System.Collections.Generic; +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.Activities +{ + [ActivityDefinition( + Category = "Control Flow", + Description = "Switch execution based on a given expression.", + 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 + { + private readonly IWorkflowExpressionEvaluator expressionEvaluator; + + public Switch(IWorkflowExpressionEvaluator expressionEvaluator) + { + this.expressionEvaluator = expressionEvaluator; + Cases = new List(); + } + + [ActivityProperty(Hint = "The expression to evaluate. The evaluated value will be used to switch on.")] + public WorkflowExpression Expression + { + get => GetState>(); + set => SetState(value); + } + + [ActivityProperty(Hint = "A comma-separated list of possible outcomes of the expression.")] + public IReadOnlyCollection Cases + { + get => GetState>(); + set => SetState(value); + } + + protected override async Task OnExecuteAsync( + WorkflowExecutionContext workflowContext, + CancellationToken cancellationToken) + { + var result = await expressionEvaluator.EvaluateAsync(Expression, workflowContext, cancellationToken); + return Outcome(result); + } + } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/ControlFlow/While.cs b/src/activities/Elsa.Activities.ControlFlow/Activities/While.cs similarity index 92% rename from src/core/Elsa.Core/Activities/ControlFlow/While.cs rename to src/activities/Elsa.Activities.ControlFlow/Activities/While.cs index 0ae2d5060..2ada3455b 100644 --- a/src/core/Elsa.Core/Activities/ControlFlow/While.cs +++ b/src/activities/Elsa.Activities.ControlFlow/Activities/While.cs @@ -4,10 +4,11 @@ using Elsa.Attributes; using Elsa.Expressions; using Elsa.Extensions; using Elsa.Results; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; -namespace Elsa.Activities.ControlFlow +namespace Elsa.Activities.ControlFlow.Activities { [ActivityDefinition(Category = "Control Flow", Description = "Execute while a given condition is true.", Icon = "far fa-circle")] public class While : Activity @@ -47,7 +48,7 @@ namespace Elsa.Activities.ControlFlow HasStarted = true; context.BeginScope(); - return Outcome(OutcomeNames.Loop); + return Outcome(OutcomeNames.Iterate); } return Done(); diff --git a/src/activities/Elsa.Activities.ControlFlow/Elsa.Activities.ControlFlow.csproj b/src/activities/Elsa.Activities.ControlFlow/Elsa.Activities.ControlFlow.csproj new file mode 100644 index 000000000..acced5f61 --- /dev/null +++ b/src/activities/Elsa.Activities.ControlFlow/Elsa.Activities.ControlFlow.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + + + + + + + + diff --git a/src/activities/Elsa.Activities.ControlFlow/Extensions/ControlFlowServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.ControlFlow/Extensions/ControlFlowServiceCollectionExtensions.cs new file mode 100644 index 000000000..5ee7763de --- /dev/null +++ b/src/activities/Elsa.Activities.ControlFlow/Extensions/ControlFlowServiceCollectionExtensions.cs @@ -0,0 +1,21 @@ +using Elsa.Activities.ControlFlow.Activities; +using Elsa.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Activities.ControlFlow.Extensions +{ + public static class ControlFlowServiceCollectionExtensions + { + public static IServiceCollection AddControlFlowActivities(this IServiceCollection services) + { + return services + .AddActivity() + .AddActivity() + .AddActivity() + .AddActivity() + .AddSingleton(sp => sp.GetRequiredService()) + .AddActivity() + .AddActivity(); + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Http/Activities/HttpRequestEvent.cs b/src/activities/Elsa.Activities.Http/Activities/HttpRequestEvent.cs index 6422f0f5a..3de7b6cf7 100644 --- a/src/activities/Elsa.Activities.Http/Activities/HttpRequestEvent.cs +++ b/src/activities/Elsa.Activities.Http/Activities/HttpRequestEvent.cs @@ -115,7 +115,7 @@ namespace Elsa.Activities.Http.Activities var parser = SelectContentParser(request.ContentType); var content = await request.ReadContentAsBytesAsync(cancellationToken); - model.Content = await parser.ParseAsync(content, request.ContentType); + model.Body = await parser.ParseAsync(content, request.ContentType); } Output["Content"] = model; diff --git a/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj b/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj index ac69a60a6..dcc0ca91d 100644 --- a/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj +++ b/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj @@ -15,6 +15,8 @@ + + diff --git a/src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Http/Extensions/HttpActivitiesServiceCollectionExtensions.cs similarity index 89% rename from src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs rename to src/activities/Elsa.Activities.Http/Extensions/HttpActivitiesServiceCollectionExtensions.cs index e2ec79ade..f3fbc1532 100644 --- a/src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs +++ b/src/activities/Elsa.Activities.Http/Extensions/HttpActivitiesServiceCollectionExtensions.cs @@ -1,55 +1,56 @@ -using System; -using Elsa.Activities.Http.Activities; -using Elsa.Activities.Http.Formatters; -using Elsa.Activities.Http.Options; -using Elsa.Activities.Http.RequestHandlers.Handlers; -using Elsa.Activities.Http.Scripting; -using Elsa.Activities.Http.Services; -using Elsa.Scripting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; -using Microsoft.Extensions.Options; - -namespace Elsa.Activities.Http.Extensions -{ - public static class ServiceCollectionExtensions - { - public static IServiceCollection AddHttpActivities( - this IServiceCollection services, - Action> options = null) - { - options?.Invoke(services.AddOptions()); - - services.TryAddSingleton(); - services.AddHttpClient(nameof(HttpRequestAction)); - - services - .AddActivity() - .AddActivity() - .AddActivity(); - - services - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddSingleton() - .AddHttpContextAccessor() - .AddDataProtection(); - - return services - .AddScoped(sp => sp.GetRequiredService().HttpContext) - .AddRequestHandler() - .AddRequestHandler(); - } - - public static IServiceCollection AddRequestHandler(this IServiceCollection services) - where THandler : class, IRequestHandler - { - return services.AddScoped(); - } - } +using System; +using Elsa.Activities.Http.Activities; +using Elsa.Activities.Http.Formatters; +using Elsa.Activities.Http.Options; +using Elsa.Activities.Http.RequestHandlers.Handlers; +using Elsa.Activities.Http.Services; +using Elsa.Scripting; +using Elsa.Scripting.JavaScript; +using MediatR; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.Infrastructure; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; + +namespace Elsa.Activities.Http.Extensions +{ + public static class HttpActivitiesServiceCollectionExtensions + { + public static IServiceCollection AddHttpActivities( + this IServiceCollection services, + Action> options = null) + { + options?.Invoke(services.AddOptions()); + + services.TryAddSingleton(); + services.AddHttpClient(nameof(HttpRequestAction)); + + services + .AddActivity() + .AddActivity() + .AddActivity(); + + services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddHttpContextAccessor() + .AddMediatR(typeof(HttpActivitiesServiceCollectionExtensions)) + .AddDataProtection(); + + return services + .AddScoped(sp => sp.GetRequiredService().HttpContext) + .AddRequestHandler() + .AddRequestHandler(); + } + + public static IServiceCollection AddRequestHandler(this IServiceCollection services) + where THandler : class, IRequestHandler + { + return services.AddScoped(); + } + } } \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Http/Scripting/HttpScriptEngineConfigurator.cs b/src/activities/Elsa.Activities.Http/Handlers/HttpJavaScriptHandler.cs similarity index 66% rename from src/activities/Elsa.Activities.Http/Scripting/HttpScriptEngineConfigurator.cs rename to src/activities/Elsa.Activities.Http/Handlers/HttpJavaScriptHandler.cs index bf04fd928..35febcda3 100644 --- a/src/activities/Elsa.Activities.Http/Scripting/HttpScriptEngineConfigurator.cs +++ b/src/activities/Elsa.Activities.Http/Handlers/HttpJavaScriptHandler.cs @@ -1,31 +1,36 @@ using System; +using System.Threading; +using System.Threading.Tasks; using Elsa.Activities.Http.Models; using Elsa.Activities.Http.Services; -using Elsa.Scripting; +using Elsa.Scripting.JavaScript.Messages; using Elsa.Services.Models; -using Jint; +using MediatR; using Microsoft.AspNetCore.Http; -namespace Elsa.Activities.Http.Scripting +namespace Elsa.Activities.Http.Handlers { - public class HttpScriptEngineConfigurator : IScriptEngineConfigurator + public class HttpJavaScriptHandler : INotificationHandler { - private readonly ITokenService _tokenService; + private readonly ITokenService tokenService; private readonly IAbsoluteUrlProvider absoluteUrlProvider; private readonly IHttpContextAccessor httpContextAccessor; - public HttpScriptEngineConfigurator( + public HttpJavaScriptHandler( ITokenService tokenService, IAbsoluteUrlProvider absoluteUrlProvider, IHttpContextAccessor httpContextAccessor) { - this._tokenService = tokenService; + this.tokenService = tokenService; this.absoluteUrlProvider = absoluteUrlProvider; this.httpContextAccessor = httpContextAccessor; } - - public void Configure(Engine engine, WorkflowExecutionContext workflowExecutionContext) + + public Task Handle(EvaluatingJavaScriptExpression notification, CancellationToken cancellationToken) { + var engine = notification.Engine; + var workflowExecutionContext = notification.WorkflowExecutionContext; + engine.SetValue( "queryString", (Func) (key => httpContextAccessor.HttpContext.Request.Query[key].ToString()) @@ -38,13 +43,15 @@ namespace Elsa.Activities.Http.Scripting "signalUrl", (Func) (signal => GenerateUrl(signal, workflowExecutionContext)) ); + + return Task.CompletedTask; } private string GenerateUrl(string signal, WorkflowExecutionContext workflowExecutionContext) { var workflowInstanceId = workflowExecutionContext.Workflow.Id; var payload = new Signal(signal, workflowInstanceId); - var token = _tokenService.CreateToken(payload); + var token = tokenService.CreateToken(payload); var url = $"/workflows/signal?token={token}"; return absoluteUrlProvider.ToAbsoluteUrl(url).ToString(); diff --git a/src/activities/Elsa.Activities.Http/Handlers/HttpLiquidHandler.cs b/src/activities/Elsa.Activities.Http/Handlers/HttpLiquidHandler.cs new file mode 100644 index 000000000..d8c146dc4 --- /dev/null +++ b/src/activities/Elsa.Activities.Http/Handlers/HttpLiquidHandler.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; +using Elsa.Activities.Http.Models; +using Elsa.Scripting.Liquid.Messages; +using Fluid; +using MediatR; + +namespace Elsa.Activities.Http.Handlers +{ + public class HttpLiquidHandler : INotificationHandler + { + public Task Handle(EvaluatingLiquidExpression notification, CancellationToken cancellationToken) + { + var context = notification.TemplateContext; + + context.MemberAccessStrategy.Register(); + + return Task.CompletedTask; + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Http/Models/HttpRequestModel.cs b/src/activities/Elsa.Activities.Http/Models/HttpRequestModel.cs index dba4dee73..913f2bded 100644 --- a/src/activities/Elsa.Activities.Http/Models/HttpRequestModel.cs +++ b/src/activities/Elsa.Activities.Http/Models/HttpRequestModel.cs @@ -9,7 +9,7 @@ namespace Elsa.Activities.Http.Models public string Method { get; set; } public IDictionary QueryString { get; set; } public IDictionary Headers { get; set; } - public object Content { get; set; } + public object Body { get; set; } public IDictionary Form { get; set; } } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/Workflows/Correlate.cs b/src/activities/Elsa.Activities.Workflows/Activities/Correlate.cs similarity index 96% rename from src/core/Elsa.Core/Activities/Workflows/Correlate.cs rename to src/activities/Elsa.Activities.Workflows/Activities/Correlate.cs index 84c207bba..643879d51 100644 --- a/src/core/Elsa.Core/Activities/Workflows/Correlate.cs +++ b/src/activities/Elsa.Activities.Workflows/Activities/Correlate.cs @@ -7,7 +7,7 @@ using Elsa.Results; using Elsa.Services; using Elsa.Services.Models; -namespace Elsa.Activities.Workflows +namespace Elsa.Activities.Workflows.Activities { /// /// Sets the CorrelationId of the workflow to a given value. diff --git a/src/core/Elsa.Core/Activities/Workflows/Finish.cs b/src/activities/Elsa.Activities.Workflows/Activities/Finish.cs similarity index 94% rename from src/core/Elsa.Core/Activities/Workflows/Finish.cs rename to src/activities/Elsa.Activities.Workflows/Activities/Finish.cs index 2b6ab24c3..6f0e6dbcc 100644 --- a/src/core/Elsa.Core/Activities/Workflows/Finish.cs +++ b/src/activities/Elsa.Activities.Workflows/Activities/Finish.cs @@ -5,10 +5,11 @@ using Elsa.Expressions; using Elsa.Extensions; using Elsa.Models; using Elsa.Results; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; -namespace Elsa.Activities.Workflows +namespace Elsa.Activities.Workflows.Activities { [ActivityDefinition( Category = "Workflows", diff --git a/src/core/Elsa.Core/Activities/Workflows/Signaled.cs b/src/activities/Elsa.Activities.Workflows/Activities/Signaled.cs similarity index 97% rename from src/core/Elsa.Core/Activities/Workflows/Signaled.cs rename to src/activities/Elsa.Activities.Workflows/Activities/Signaled.cs index 40dde85aa..6bb96bc2e 100644 --- a/src/core/Elsa.Core/Activities/Workflows/Signaled.cs +++ b/src/activities/Elsa.Activities.Workflows/Activities/Signaled.cs @@ -7,7 +7,7 @@ using Elsa.Results; using Elsa.Services; using Elsa.Services.Models; -namespace Elsa.Activities.Workflows +namespace Elsa.Activities.Workflows.Activities { /// /// Halts workflow execution until the specified signal is received. diff --git a/src/core/Elsa.Core/Activities/Workflows/Start.cs b/src/activities/Elsa.Activities.Workflows/Activities/Start.cs similarity index 90% rename from src/core/Elsa.Core/Activities/Workflows/Start.cs rename to src/activities/Elsa.Activities.Workflows/Activities/Start.cs index 3988c5d1b..d3f3acd19 100644 --- a/src/core/Elsa.Core/Activities/Workflows/Start.cs +++ b/src/activities/Elsa.Activities.Workflows/Activities/Start.cs @@ -3,7 +3,7 @@ using Elsa.Results; using Elsa.Services; using Elsa.Services.Models; -namespace Elsa.Activities.Workflows +namespace Elsa.Activities.Workflows.Activities { [ActivityDefinition( Category = "Workflows", diff --git a/src/core/Elsa.Core/Activities/Workflows/Trigger.cs b/src/activities/Elsa.Activities.Workflows/Activities/Trigger.cs similarity index 93% rename from src/core/Elsa.Core/Activities/Workflows/Trigger.cs rename to src/activities/Elsa.Activities.Workflows/Activities/Trigger.cs index 519b089b9..469fbd632 100644 --- a/src/core/Elsa.Core/Activities/Workflows/Trigger.cs +++ b/src/activities/Elsa.Activities.Workflows/Activities/Trigger.cs @@ -5,10 +5,12 @@ using Elsa.Expressions; using Elsa.Extensions; using Elsa.Models; using Elsa.Results; +using Elsa.Scripting.JavaScript; +using Elsa.Scripting.JavaScript.Services; using Elsa.Services; using Elsa.Services.Models; -namespace Elsa.Activities.Workflows +namespace Elsa.Activities.Workflows.Activities { /// /// Triggers the specified signal. @@ -41,7 +43,7 @@ namespace Elsa.Activities.Workflows )] public WorkflowExpression Input { - get => GetState(() => new WorkflowExpression(JavaScriptEvaluator.SyntaxName, "{}")); + get => GetState(() => new WorkflowExpression(JavaScriptExpressionEvaluator.SyntaxName, "{}")); set => SetState(value); } diff --git a/src/core/Elsa.Core/Activities/Workflows/TriggerWorkflow.cs b/src/activities/Elsa.Activities.Workflows/Activities/TriggerWorkflow.cs similarity index 96% rename from src/core/Elsa.Core/Activities/Workflows/TriggerWorkflow.cs rename to src/activities/Elsa.Activities.Workflows/Activities/TriggerWorkflow.cs index e5ce96ea9..d1c5704cd 100644 --- a/src/core/Elsa.Core/Activities/Workflows/TriggerWorkflow.cs +++ b/src/activities/Elsa.Activities.Workflows/Activities/TriggerWorkflow.cs @@ -6,10 +6,11 @@ using Elsa.Expressions; using Elsa.Extensions; using Elsa.Models; using Elsa.Results; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; -namespace Elsa.Activities.Workflows +namespace Elsa.Activities.Workflows.Activities { [ActivityDefinition( Category = "Workflows", diff --git a/src/activities/Elsa.Activities.Workflows/Elsa.Activities.Workflows.csproj b/src/activities/Elsa.Activities.Workflows/Elsa.Activities.Workflows.csproj new file mode 100644 index 000000000..acced5f61 --- /dev/null +++ b/src/activities/Elsa.Activities.Workflows/Elsa.Activities.Workflows.csproj @@ -0,0 +1,12 @@ + + + + netstandard2.0 + + + + + + + + diff --git a/src/activities/Elsa.Activities.Workflows/Extensions/WorkflowActivityServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Workflows/Extensions/WorkflowActivityServiceCollectionExtensions.cs new file mode 100644 index 000000000..55c36a0cb --- /dev/null +++ b/src/activities/Elsa.Activities.Workflows/Extensions/WorkflowActivityServiceCollectionExtensions.cs @@ -0,0 +1,19 @@ +using Elsa.Activities.Workflows.Activities; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Activities.Workflows.Extensions +{ + public static class WorkflowActivityServiceCollectionExtensions + { + public static IServiceCollection AddWorkflowActivities(this IServiceCollection services) + { + return services + .AddActivity() + .AddActivity() + .AddActivity() + .AddActivity() + .AddActivity() + .AddActivity(); + } + } +} \ 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 9a157f5ed..158045e83 100644 --- a/src/core/Elsa.Abstractions/Elsa.Abstractions.csproj +++ b/src/core/Elsa.Abstractions/Elsa.Abstractions.csproj @@ -15,7 +15,6 @@ - @@ -25,4 +24,8 @@ + + + + diff --git a/src/core/Elsa.Abstractions/Models/ActivityDefinition.cs b/src/core/Elsa.Abstractions/Models/ActivityDefinition.cs index 60f42a72f..98ef66fe2 100644 --- a/src/core/Elsa.Abstractions/Models/ActivityDefinition.cs +++ b/src/core/Elsa.Abstractions/Models/ActivityDefinition.cs @@ -12,6 +12,7 @@ namespace Elsa.Models public ActivityDefinition() { + State = new JObject(); } public ActivityDefinition(string id, string type, JObject state, int left = 0, int top = 0) @@ -25,6 +26,13 @@ namespace Elsa.Models public string Id { get; set; } public string Type { get; set; } + + public string Name + { + get => State.ContainsKey(nameof(Name)) ? State[nameof(Name)].Value() : default; + set => State[nameof(Name)] = value; + } + public string DisplayName { get; set; } public string Description { get; set; } public int Left { get; set; } diff --git a/src/core/Elsa.Abstractions/OutcomeNames.cs b/src/core/Elsa.Abstractions/OutcomeNames.cs index 772227618..2d62709fd 100644 --- a/src/core/Elsa.Abstractions/OutcomeNames.cs +++ b/src/core/Elsa.Abstractions/OutcomeNames.cs @@ -7,6 +7,5 @@ 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/Scripting/IScriptEngineConfigurator.cs b/src/core/Elsa.Abstractions/Scripting/IScriptEngineConfigurator.cs deleted file mode 100644 index dcc35c4f9..000000000 --- a/src/core/Elsa.Abstractions/Scripting/IScriptEngineConfigurator.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Elsa.Services.Models; -using Jint; - -namespace Elsa.Scripting -{ - public interface IScriptEngineConfigurator - { - void Configure(Engine engine, WorkflowExecutionContext workflowExecutionContext); - } -} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Services/ActivityBase.cs b/src/core/Elsa.Abstractions/Services/ActivityBase.cs index 43e180f70..41545e515 100644 --- a/src/core/Elsa.Abstractions/Services/ActivityBase.cs +++ b/src/core/Elsa.Abstractions/Services/ActivityBase.cs @@ -21,9 +21,14 @@ namespace Elsa.Services public Variables TransientOutput { get; } = new Variables(); public virtual string Type => GetType().Name; - - [ActivityProperty(Label = "ID", Hint = "Optionally provide a custom ID for this activity. You can then reference this activity from expressions.")] + public string Id { get; set; } + + [ActivityProperty(Label = "Name", Hint = "Optionally provide a name for this activity. You can reference named activities from expressions.")] + public string Name { + get => GetState(); + set => SetState(value); + } [ActivityProperty(Hint = "Optionally provide a custom title for this activity.")] public string Title diff --git a/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs b/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs index 4d714c92c..fa5b3928e 100644 --- a/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs +++ b/src/core/Elsa.Abstractions/Services/IActivityBuilder.cs @@ -7,15 +7,16 @@ namespace Elsa.Services public interface IActivityBuilder { string Id { get; set; } + string Name { get; set; } ActivityDefinition Activity { get; } - IActivityBuilder StartWith(Action setup = default, string id = default) where T: class, IActivity; - IActivityBuilder Add(Action setup = default, string id = null) where T : class, IActivity; + IActivityBuilder StartWith(Action setup = default, string name = null) where T: class, IActivity; + IActivityBuilder Add(Action setup = default, string name = null) where T : class, IActivity; IOutcomeBuilder When(string outcome); - IActivityBuilder Then(Action setup = null, Action branch = null, string id = null) where T : class, IActivity; - IActivityBuilder WithId(string id); + IActivityBuilder Then(Action setup = null, Action branch = null, string name = null) where T : class, IActivity; + IActivityBuilder WithName(string name); IActivityBuilder WithDisplayName(string displayName); IActivityBuilder WithDescription(string description); - IWorkflowBuilder Then(string activityId); + IWorkflowBuilder Then(string activityName); IActivityBuilder Then(IActivityBuilder targetActivity); WorkflowDefinitionVersion Build(); ActivityDefinition BuildActivity(); diff --git a/src/core/Elsa.Abstractions/Services/IOutcomeBuilder.cs b/src/core/Elsa.Abstractions/Services/IOutcomeBuilder.cs index 24aa668d4..2f7682232 100644 --- a/src/core/Elsa.Abstractions/Services/IOutcomeBuilder.cs +++ b/src/core/Elsa.Abstractions/Services/IOutcomeBuilder.cs @@ -8,8 +8,8 @@ namespace Elsa.Services { IActivityBuilder Source { get; } string Outcome { get; } - IActivityBuilder Then(Action setup = default, Action branch = null, string id = default) where T : class, IActivity; + IActivityBuilder Then(Action setup = default, Action branch = default, string name = default) where T : class, IActivity; WorkflowDefinitionVersion Build(); - IConnectionBuilder Then(string activityId); + IConnectionBuilder Then(string activityName); } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Services/IWorkflowBuilder.cs b/src/core/Elsa.Abstractions/Services/IWorkflowBuilder.cs index dfbf312ab..db6e46201 100644 --- a/src/core/Elsa.Abstractions/Services/IWorkflowBuilder.cs +++ b/src/core/Elsa.Abstractions/Services/IWorkflowBuilder.cs @@ -14,8 +14,8 @@ namespace Elsa.Services IWorkflowBuilder WithName(string name); IWorkflowBuilder WithDescription(string description); IWorkflowBuilder AsSingleton(bool value = true); - IActivityBuilder Add(Action setupActivity = default, string id = default) where T : class, IActivity; - IActivityBuilder StartWith(Action setup = default, string id = default) where T: class, IActivity; + IActivityBuilder Add(Action setupActivity = default, string name = default) where T : class, IActivity; + IActivityBuilder StartWith(Action setup = default, string name = default) where T: class, IActivity; IConnectionBuilder Connect(IActivityBuilder source, IActivityBuilder target, string outcome = default); IConnectionBuilder Connect(Func source, Func target, string outcome = default); WorkflowDefinitionVersion Build(); diff --git a/src/core/Elsa.Abstractions/Services/Models/IActivity.cs b/src/core/Elsa.Abstractions/Services/Models/IActivity.cs index db59293de..e72265ac0 100644 --- a/src/core/Elsa.Abstractions/Services/Models/IActivity.cs +++ b/src/core/Elsa.Abstractions/Services/Models/IActivity.cs @@ -28,10 +28,15 @@ namespace Elsa.Services.Models string Type { get; } /// - /// Unique identifier of this activity + /// Unique identifier of this activity. /// string Id { get; set; } + /// + /// Name identifier of this activity. + /// + string Name { get; set; } + /// /// Returns a value of whether the specified activity can execute. /// diff --git a/src/core/Elsa.Abstractions/Services/Models/Workflow.cs b/src/core/Elsa.Abstractions/Services/Models/Workflow.cs index 61cadfb18..d61409e8d 100644 --- a/src/core/Elsa.Abstractions/Services/Models/Workflow.cs +++ b/src/core/Elsa.Abstractions/Services/Models/Workflow.cs @@ -62,7 +62,7 @@ namespace Elsa.Services.Models return new WorkflowInstance { Id = Id, - DefinitionId = Definition.Id, + DefinitionId = Definition.DefinitionId, Version = Definition.Version, CorrelationId = CorrelationId, Status = Status, diff --git a/src/core/Elsa.Abstractions/Services/Models/WorkflowExecutionContext.cs b/src/core/Elsa.Abstractions/Services/Models/WorkflowExecutionContext.cs index 27c21ffe3..592386793 100644 --- a/src/core/Elsa.Abstractions/Services/Models/WorkflowExecutionContext.cs +++ b/src/core/Elsa.Abstractions/Services/Models/WorkflowExecutionContext.cs @@ -31,7 +31,7 @@ namespace Elsa.Services.Models public WorkflowExecutionScope CurrentScope => Workflow.Scopes.Peek(); public Variables TransientState { get; } = new Variables(); public IActivity CurrentActivity { get; private set; } - public void ScheduleActivities(params IActivity[] activities) => ScheduleActivities((IEnumerable) activities); + public void ScheduleActivities(params IActivity[] activities) => ScheduleActivities((IEnumerable)activities); public void ScheduleActivities(IEnumerable activities) { @@ -43,7 +43,7 @@ namespace Elsa.Services.Models public void BeginScope() => Workflow.Scopes.Push(new WorkflowExecutionScope()); public void EndScope() => Workflow.Scopes.Pop(); - + public void ScheduleActivity(IActivity activity) { scheduledActivities.Push(activity); @@ -53,7 +53,7 @@ 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) { // Get the first scope (starting from the oldest one) containing the variable (existing variable). Otherwise use the current scope (new variable declaration) @@ -67,6 +67,13 @@ namespace Elsa.Services.Models var scope = Workflow.Scopes.FirstOrDefault(x => x.Variables.ContainsKey(name)) ?? CurrentScope; return scope.GetVariable(name); } + + public object 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; @@ -75,7 +82,7 @@ namespace Elsa.Services.Models Workflow.StartedAt = clock.GetCurrentInstant(); Workflow.Status = WorkflowStatus.Executing; } - + public void Fault(IActivity activity, Exception exception) => Fault(activity, exception.Message); public void Fault(IActivity activity, string errorMessage) @@ -101,11 +108,16 @@ namespace Elsa.Services.Models Workflow.Status = WorkflowStatus.Finished; Workflow.BlockingActivities.Clear(); } - + public void Abort() { Workflow.AbortedAt = clock.GetCurrentInstant(); Workflow.Status = WorkflowStatus.Aborted; } + + public Variables GetVariables() => Workflow.Scopes + .Reverse() + .Select(x => x.Variables) + .Aggregate(Variables.Empty, (x, y) => new Variables(x.Union(y))); } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/Primitives/SetVariable.cs b/src/core/Elsa.Core/Activities/SetVariable.cs similarity index 95% rename from src/core/Elsa.Core/Activities/Primitives/SetVariable.cs rename to src/core/Elsa.Core/Activities/SetVariable.cs index 6706e9830..9ed35ee77 100644 --- a/src/core/Elsa.Core/Activities/Primitives/SetVariable.cs +++ b/src/core/Elsa.Core/Activities/SetVariable.cs @@ -1,55 +1,55 @@ -using System.Threading; -using System.Threading.Tasks; -using Elsa.Attributes; -using Elsa.Expressions; -using Elsa.Results; -using Elsa.Services; -using Elsa.Services.Models; - -namespace Elsa.Activities.Primitives -{ - [ActivityDefinition( - DisplayName = "Set Variable", - Description = "Set variable on the workflow.", - 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 - { - private readonly IWorkflowExpressionEvaluator expressionEvaluator; - - public SetVariable(IWorkflowExpressionEvaluator expressionEvaluator) - { - this.expressionEvaluator = expressionEvaluator; - } - - [ActivityProperty(Hint = "The name of the variable to store the value into.")] - public string VariableName - { - get => GetState(); - set => SetState(value); - } - - [ActivityProperty(Hint = "An expression that evaluates to the value to store in the variable.")] - public WorkflowExpression ValueExpression - { - get => GetState(); - set => SetState(value); - } - - protected override async Task OnExecuteAsync( - WorkflowExecutionContext workflowContext, - CancellationToken cancellationToken) - { - var value = await expressionEvaluator.EvaluateAsync( - ValueExpression, - ValueExpression.Type, - workflowContext, - cancellationToken - ); - workflowContext.SetVariable(VariableName, value); - return Done(); - } - } +using System.Threading; +using System.Threading.Tasks; +using Elsa.Attributes; +using Elsa.Expressions; +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Activities +{ + [ActivityDefinition( + DisplayName = "Set Variable", + Description = "Set variable on the workflow.", + 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 + { + private readonly IWorkflowExpressionEvaluator expressionEvaluator; + + public SetVariable(IWorkflowExpressionEvaluator expressionEvaluator) + { + this.expressionEvaluator = expressionEvaluator; + } + + [ActivityProperty(Hint = "The name of the variable to store the value into.")] + public string VariableName + { + get => GetState(); + set => SetState(value); + } + + [ActivityProperty(Hint = "An expression that evaluates to the value to store in the variable.")] + public WorkflowExpression ValueExpression + { + get => GetState(); + set => SetState(value); + } + + protected override async Task OnExecuteAsync( + WorkflowExecutionContext workflowContext, + CancellationToken cancellationToken) + { + var value = await expressionEvaluator.EvaluateAsync( + ValueExpression, + ValueExpression.Type, + workflowContext, + cancellationToken + ); + workflowContext.SetVariable(VariableName, value); + return Done(); + } + } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Elsa.Core.csproj b/src/core/Elsa.Core/Elsa.Core.csproj index b36a56d18..4779a0fbf 100644 --- a/src/core/Elsa.Core/Elsa.Core.csproj +++ b/src/core/Elsa.Core/Elsa.Core.csproj @@ -2,7 +2,7 @@ netstandard2.0 - latest + 8 Elsa 1.0.0 Sipke Schoorstra @@ -36,8 +36,8 @@ + - \ No newline at end of file diff --git a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs index 0ff5d3a2b..5e089837d 100644 --- a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs @@ -2,16 +2,13 @@ using System; using System.Collections.Generic; using System.Linq; using Elsa; -using Elsa.Activities.ControlFlow; -using Elsa.Activities.Primitives; -using Elsa.Activities.Workflows; +using Elsa.Activities; using Elsa.AutoMapper.Extensions; using Elsa.Caching; using Elsa.Expressions; using Elsa.Mapping; using Elsa.Persistence; using Elsa.Persistence.Memory; -using Elsa.Scripting; using Elsa.Serialization; using Elsa.Serialization.Formatters; using Elsa.Services; @@ -28,7 +25,7 @@ namespace Microsoft.Extensions.DependencyInjection { public static class ElsaServiceCollectionExtensions { - public static IServiceCollection AddWorkflows( + public static IServiceCollection AddElsaCore( this IServiceCollection services, Action configure = null) { @@ -115,12 +112,9 @@ namespace Microsoft.Extensions.DependencyInjection .TryAddProvider(ServiceLifetime.Singleton) .TryAddProvider(ServiceLifetime.Singleton) .TryAddProvider(ServiceLifetime.Singleton) - .TryAddProvider(ServiceLifetime.Singleton) - .AddSingleton() - .AddSingleton() .AddScoped() .AddSingleton() - .AddSingleton() + .AddScoped() .AddSingleton() .AddSingleton() .AddScoped() @@ -133,9 +127,7 @@ namespace Microsoft.Extensions.DependencyInjection .AddTransient() .AddTransient>(sp => sp.GetRequiredService) .AddAutoMapperProfile(ServiceLifetime.Singleton) - .AddPrimitiveActivities() - .AddControlFlowActivities() - .AddWorkflowActivities(); + .AddPrimitiveActivities(); return configuration; } @@ -170,28 +162,5 @@ namespace Microsoft.Extensions.DependencyInjection return services .AddActivity(); } - - private static IServiceCollection AddControlFlowActivities(this IServiceCollection services) - { - return services - .AddActivity() - .AddActivity() - .AddActivity() - .AddActivity() - .AddSingleton(sp => sp.GetRequiredService()) - .AddActivity() - .AddActivity(); - } - - private static IServiceCollection AddWorkflowActivities(this IServiceCollection services) - { - return services - .AddActivity() - .AddActivity() - .AddActivity() - .AddActivity() - .AddActivity() - .AddActivity(); - } } } \ No newline at end of file diff --git a/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs b/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs index 3b8ff0c27..18e434cf3 100644 --- a/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs +++ b/src/core/Elsa.Core/WorkflowBuilders/ActivityBuilder.cs @@ -8,27 +8,27 @@ namespace Elsa.WorkflowBuilders { public class ActivityBuilder : IActivityBuilder { - public ActivityBuilder(WorkflowBuilder workflowBuilder, ActivityDefinition activity, string id) + public ActivityBuilder(WorkflowBuilder workflowBuilder, ActivityDefinition activity) { WorkflowBuilder = workflowBuilder; Activity = activity; - Id = id; } public WorkflowBuilder WorkflowBuilder { get; } public ActivityDefinition Activity { get; } public string Id { get; set; } + public string Name { 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 + public IActivityBuilder StartWith(Action setup = default, string name = default) where T : class, IActivity { - return Add(setup, id); + return Add(setup, name); } - public IActivityBuilder Add(Action setup = default, string id = null) where T : class, IActivity + public IActivityBuilder Add(Action setup = default, string name = default) where T : class, IActivity { - return WorkflowBuilder.Add(setup, id); + return WorkflowBuilder.Add(setup, name); } public IOutcomeBuilder When(string outcome) @@ -36,15 +36,15 @@ namespace Elsa.WorkflowBuilders return new OutcomeBuilder(WorkflowBuilder, this, outcome); } - public IActivityBuilder Then(Action setup = null, Action branch = null, string id = null) where T : class, IActivity + public IActivityBuilder Then(Action setup = null, Action branch = null, string name = default) where T : class, IActivity { - var activityBuilder = When(null).Then(setup, branch, id); + var activityBuilder = When(null).Then(setup, branch, name); return activityBuilder; } - public IActivityBuilder WithId(string id) + public IActivityBuilder WithName(string name) { - Id = id; + Name = name; return this; } @@ -60,11 +60,11 @@ namespace Elsa.WorkflowBuilders return this; } - public IWorkflowBuilder Then(string activityId) + public IWorkflowBuilder Then(string activityName) { WorkflowBuilder.Connect( () => this, - () => WorkflowBuilder.Activities.First(x => x.Id == activityId) + () => WorkflowBuilder.Activities.First(x => x.Name== activityName) ); return WorkflowBuilder; @@ -79,6 +79,8 @@ namespace Elsa.WorkflowBuilders public ActivityDefinition BuildActivity() { Activity.Id = Id; + Activity.Name = Name; + Activity.DisplayName = Name; Activity.Description = Description; Activity.DisplayName = DisplayName; return Activity; diff --git a/src/core/Elsa.Core/WorkflowBuilders/OutcomeBuilder.cs b/src/core/Elsa.Core/WorkflowBuilders/OutcomeBuilder.cs index e0498329d..d0e68dab6 100644 --- a/src/core/Elsa.Core/WorkflowBuilders/OutcomeBuilder.cs +++ b/src/core/Elsa.Core/WorkflowBuilders/OutcomeBuilder.cs @@ -19,9 +19,9 @@ namespace Elsa.WorkflowBuilders public IActivityBuilder Source { get; } public string Outcome { get; } - public IActivityBuilder Then(Action setup = default, Action branch = null, string id = default) where T : class, IActivity + public IActivityBuilder Then(Action setup = default, Action branch = default, string name = default) where T : class, IActivity { - var target = WorkflowBuilder.Add(setup, id); + var target = WorkflowBuilder.Add(setup, name); branch?.Invoke(target); WorkflowBuilder.Connect(Source, target, Outcome); @@ -30,11 +30,11 @@ namespace Elsa.WorkflowBuilders public WorkflowDefinitionVersion Build() => WorkflowBuilder.Build(); - public IConnectionBuilder Then(string activityId) + public IConnectionBuilder Then(string activityName) { return WorkflowBuilder.Connect( () => Source, - () => WorkflowBuilder.Activities.First(x => x.Id == activityId), + () => WorkflowBuilder.Activities.First(x => x.Name == activityName), Outcome); } } diff --git a/src/core/Elsa.Core/WorkflowBuilders/WorkflowBuilder.cs b/src/core/Elsa.Core/WorkflowBuilders/WorkflowBuilder.cs index fd0248d48..cf15dedcf 100644 --- a/src/core/Elsa.Core/WorkflowBuilders/WorkflowBuilder.cs +++ b/src/core/Elsa.Core/WorkflowBuilders/WorkflowBuilder.cs @@ -60,31 +60,27 @@ namespace Elsa.WorkflowBuilders IsSingleton = value; return this; } - + public IWorkflowBuilder Disable() { IsDisabled = true; return this; } - + public IWorkflowBuilder Enable() { IsDisabled = false; return this; } - public IActivityBuilder Add( - Action setupActivity = default, - string id = default) where T : class, IActivity + public IActivityBuilder Add(Action setupActivity = default, string name = default) where T : class, IActivity { var activity = activityResolver.ResolveActivity(setupActivity); var activityBlueprint = ActivityDefinition.FromActivity(activity); - var activityBuilder = new ActivityBuilder(this, activityBlueprint, id); + var activityBuilder = new ActivityBuilder(this, activityBlueprint); var activityDescriptor = ActivityDescriber.Describe(); - - if (id != null) - activity.Id = id; - + + activity.Name = name; activityBuilder.DisplayName = activityDescriptor.DisplayName; activityBuilder.Description = activityDescriptor.Description; activityBuilders.Add(activityBuilder); @@ -111,11 +107,9 @@ namespace Elsa.WorkflowBuilders } - public IActivityBuilder StartWith( - Action setupActivity, - string id = null) where T : class, IActivity + public IActivityBuilder StartWith(Action setupActivity, string name = default) where T : class, IActivity { - return Add(setupActivity, id); + return Add(setupActivity, name); } public WorkflowDefinitionVersion Build() @@ -143,7 +137,11 @@ namespace Elsa.WorkflowBuilders connections, IsSingleton, IsDisabled, - Variables.Empty); + Variables.Empty) + { + IsPublished = true, + IsLatest = true + }; } } } \ No newline at end of file diff --git a/src/core/Elsa/Elsa.csproj b/src/core/Elsa/Elsa.csproj new file mode 100644 index 000000000..fbca2784e --- /dev/null +++ b/src/core/Elsa/Elsa.csproj @@ -0,0 +1,19 @@ + + + + netstandard2.0 + + + + + + + + + + + + + + + diff --git a/src/core/Elsa/ElsaServiceCollectionExtensions.cs b/src/core/Elsa/ElsaServiceCollectionExtensions.cs new file mode 100644 index 000000000..7bfc2ac35 --- /dev/null +++ b/src/core/Elsa/ElsaServiceCollectionExtensions.cs @@ -0,0 +1,29 @@ +using System; +using Elsa; +using Elsa.Activities.ControlFlow.Extensions; +using Elsa.Activities.UserTask.Extensions; +using Elsa.Activities.Workflows.Extensions; +using Elsa.Scripting.JavaScript; +using Elsa.Scripting.JavaScript.Extensions; +using Elsa.Scripting.Liquid; +using Elsa.Scripting.Liquid.Extensions; + +// ReSharper disable once CheckNamespace +namespace Microsoft.Extensions.DependencyInjection +{ + public static class ElsaServiceCollectionExtensions + { + public static IServiceCollection AddElsa( + this IServiceCollection services, + Action configure = null) + { + return services + .AddElsaCore(configure) + .AddJavaScriptExpressionEvaluator() + .AddLiquidExpressionEvaluator() + .AddControlFlowActivities() + .AddWorkflowActivities() + .AddUserTaskActivities(); + } + } +} \ No newline at end of file diff --git a/src/persistence/Elsa.Persistence.MongoDb/Elsa.Persistence.MongoDb.csproj b/src/persistence/Elsa.Persistence.MongoDb/Elsa.Persistence.MongoDb.csproj index a903e0773..619892b70 100644 --- a/src/persistence/Elsa.Persistence.MongoDb/Elsa.Persistence.MongoDb.csproj +++ b/src/persistence/Elsa.Persistence.MongoDb/Elsa.Persistence.MongoDb.csproj @@ -14,7 +14,6 @@ - diff --git a/src/persistence/Elsa.Persistence.MongoDb/MongoElsaBuilder.cs b/src/persistence/Elsa.Persistence.MongoDb/MongoElsaBuilder.cs index 1012414e9..36ffca378 100644 --- a/src/persistence/Elsa.Persistence.MongoDb/MongoElsaBuilder.cs +++ b/src/persistence/Elsa.Persistence.MongoDb/MongoElsaBuilder.cs @@ -1,6 +1,6 @@ using Microsoft.Extensions.DependencyInjection; -namespace Elsa.Persistence.MongoDb.Extensions +namespace Elsa.Persistence.MongoDb { public class MongoElsaBuilder : ElsaBuilder { diff --git a/src/persistence/Elsa.Persistence.YesSql/Elsa.Persistence.YesSql.csproj b/src/persistence/Elsa.Persistence.YesSql/Elsa.Persistence.YesSql.csproj index efffc9194..4207316a4 100644 --- a/src/persistence/Elsa.Persistence.YesSql/Elsa.Persistence.YesSql.csproj +++ b/src/persistence/Elsa.Persistence.YesSql/Elsa.Persistence.YesSql.csproj @@ -15,7 +15,6 @@ - diff --git a/src/samples/Sample01/Program.cs b/src/samples/Sample01/Program.cs index 007889309..76cb9ddce 100644 --- a/src/samples/Sample01/Program.cs +++ b/src/samples/Sample01/Program.cs @@ -15,7 +15,7 @@ namespace Sample01 { // Setup a service collection. var services = new ServiceCollection() - .AddWorkflows() + .AddElsa() .AddActivity() .AddActivity() .BuildServiceProvider(); diff --git a/src/samples/Sample01/Sample01.csproj b/src/samples/Sample01/Sample01.csproj index fe14b3066..c5ed0af75 100644 --- a/src/samples/Sample01/Sample01.csproj +++ b/src/samples/Sample01/Sample01.csproj @@ -6,8 +6,7 @@ - - + diff --git a/src/samples/Sample02/Program.cs b/src/samples/Sample02/Program.cs index a7617d642..5dcfb9dbd 100644 --- a/src/samples/Sample02/Program.cs +++ b/src/samples/Sample02/Program.cs @@ -17,7 +17,7 @@ namespace Sample02 { // Setup a service collection. var services = new ServiceCollection() - .AddWorkflows() + .AddElsa() .AddConsoleActivities() .BuildServiceProvider(); diff --git a/src/samples/Sample02/Sample02.csproj b/src/samples/Sample02/Sample02.csproj index fe14b3066..c5ed0af75 100644 --- a/src/samples/Sample02/Sample02.csproj +++ b/src/samples/Sample02/Sample02.csproj @@ -6,8 +6,7 @@ - - + diff --git a/src/samples/Sample03/Program.cs b/src/samples/Sample03/Program.cs index eab461aa2..67d9c78a9 100644 --- a/src/samples/Sample03/Program.cs +++ b/src/samples/Sample03/Program.cs @@ -19,7 +19,7 @@ namespace Sample03 { // Setup a service collection. var services = new ServiceCollection() - .AddWorkflows() + .AddElsa() .AddConsoleActivities() .BuildServiceProvider(); diff --git a/src/samples/Sample03/Sample03.csproj b/src/samples/Sample03/Sample03.csproj index fe14b3066..c5ed0af75 100644 --- a/src/samples/Sample03/Sample03.csproj +++ b/src/samples/Sample03/Sample03.csproj @@ -6,8 +6,7 @@ - - + diff --git a/src/samples/Sample04/CalculatorWorkflow.cs b/src/samples/Sample04/CalculatorWorkflow.cs index 347eb15a5..4ec754408 100644 --- a/src/samples/Sample04/CalculatorWorkflow.cs +++ b/src/samples/Sample04/CalculatorWorkflow.cs @@ -1,8 +1,10 @@ using Elsa; +using Elsa.Activities; using Elsa.Activities.Console.Activities; using Elsa.Activities.ControlFlow; -using Elsa.Activities.Primitives; +using Elsa.Activities.ControlFlow.Activities; using Elsa.Expressions; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; using Sample04.Activities; @@ -26,7 +28,7 @@ namespace Sample04 whileActivity => { whileActivity - .When(OutcomeNames.Loop) + .When(OutcomeNames.Iterate) .Then(x => x.TextExpression = new LiteralExpression("Enter number 1:")) .Then(x => x.VariableName = "number1") .Then(x => x.TextExpression = new LiteralExpression("Enter number 2:")) @@ -43,29 +45,28 @@ namespace Sample04 { @switch .When("add") - .Then(SetupOperation, id: "perform-sum") + .Then(SetupOperation).WithName("PerformSum") .Then("ShowResult"); @switch .When("subtract") - .Then(SetupOperation, id: "perform-subtract") + .Then(SetupOperation).WithName("PerformSubtract") .Then("ShowResult"); @switch .When("multiply") - .Then(SetupOperation, id: "perform-multiply") + .Then(SetupOperation).WithName("PerformMultiply") .Then("ShowResult"); @switch .When("divide") - .Then(SetupOperation, id: "perform-divide") + .Then(SetupOperation).WithName("PerformDivide") .Then("ShowResult"); - }, - "inspect-selected-operation" - ) - .Add(x => x.TextExpression = new JavaScriptExpression("`Result: ${result}`")).WithId("ShowResult") + } + ).WithName("InspectSelectedOperation") + .Add(x => x.TextExpression = new JavaScriptExpression("`Result: ${result}`")).WithName("ShowResult") .Then(x => x.TextExpression = new LiteralExpression("Try again? (y/n)")) - .Then().WithId("TryAgain") + .Then().WithName("TryAgain") .Then(x => { x.VariableName = "Exit"; diff --git a/src/samples/Sample04/Program.cs b/src/samples/Sample04/Program.cs index 2dc98f0e2..1881030ee 100644 --- a/src/samples/Sample04/Program.cs +++ b/src/samples/Sample04/Program.cs @@ -18,7 +18,7 @@ namespace Sample04 // Setup a service collection. var services = new ServiceCollection() .AddSingleton(Console.In) - .AddWorkflows() + .AddElsa() .AddConsoleActivities() .AddActivity() .AddActivity() diff --git a/src/samples/Sample04/Sample04.csproj b/src/samples/Sample04/Sample04.csproj index fe14b3066..c5ed0af75 100644 --- a/src/samples/Sample04/Sample04.csproj +++ b/src/samples/Sample04/Sample04.csproj @@ -6,8 +6,7 @@ - - + diff --git a/src/samples/Sample05/Program.cs b/src/samples/Sample05/Program.cs index af6b6c6be..d54591ef2 100644 --- a/src/samples/Sample05/Program.cs +++ b/src/samples/Sample05/Program.cs @@ -31,9 +31,9 @@ namespace Sample05 private static void ConfigureServices(IServiceCollection services) { services - .AddWorkflows() - .AddWorkflow() + .AddElsa() .AddConsoleActivities() + .AddWorkflow() .AddTimerActivities(options => options.Configure(x => x.SweepInterval = Period.FromSeconds(1))); } } diff --git a/src/samples/Sample05/RecurringWorkflow.cs b/src/samples/Sample05/RecurringWorkflow.cs index 3b7f6db49..653e1bb18 100644 --- a/src/samples/Sample05/RecurringWorkflow.cs +++ b/src/samples/Sample05/RecurringWorkflow.cs @@ -2,6 +2,7 @@ using System; using Elsa.Activities.Console.Activities; using Elsa.Activities.Timers.Activities; using Elsa.Expressions; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; diff --git a/src/samples/Sample05/Sample05.csproj b/src/samples/Sample05/Sample05.csproj index 9de31932e..c07e4470c 100644 --- a/src/samples/Sample05/Sample05.csproj +++ b/src/samples/Sample05/Sample05.csproj @@ -6,13 +6,11 @@ - - - + - + diff --git a/src/samples/Sample06/Sample06.csproj b/src/samples/Sample06/Sample06.csproj index 6fd93c961..d5c71a812 100644 --- a/src/samples/Sample06/Sample06.csproj +++ b/src/samples/Sample06/Sample06.csproj @@ -5,8 +5,7 @@ - - + diff --git a/src/samples/Sample06/Startup.cs b/src/samples/Sample06/Startup.cs index 88b6befc0..5711cf2aa 100644 --- a/src/samples/Sample06/Startup.cs +++ b/src/samples/Sample06/Startup.cs @@ -19,7 +19,7 @@ namespace Sample06 public void ConfigureServices(IServiceCollection services) { services - .AddWorkflows() + .AddElsa() .AddHttpActivities() .AddWorkflow(); } diff --git a/src/samples/Sample07/DocumentApprovalWorkflow.cs b/src/samples/Sample07/DocumentApprovalWorkflow.cs index b93148ca4..3d00ca300 100644 --- a/src/samples/Sample07/DocumentApprovalWorkflow.cs +++ b/src/samples/Sample07/DocumentApprovalWorkflow.cs @@ -2,13 +2,16 @@ using System; using System.Net; using System.Net.Http; using Elsa; +using Elsa.Activities; using Elsa.Activities.Email.Activities; using Elsa.Activities.Http.Activities; using Elsa.Activities.ControlFlow; -using Elsa.Activities.Primitives; +using Elsa.Activities.ControlFlow.Activities; using Elsa.Activities.Timers.Activities; using Elsa.Activities.Workflows; +using Elsa.Activities.Workflows.Activities; using Elsa.Expressions; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; @@ -25,14 +28,13 @@ namespace Sample07 x.Method = HttpMethod.Post.Method; x.Path = new Uri("/documents", UriKind.Relative); x.ReadContent = true; - }, - "WaitForDocument" - ) + } + ).WithName("WaitForDocument") .Then( x => { x.VariableName = "Document"; - x.ValueExpression = new JavaScriptExpression("lastResult().Content"); + x.ValueExpression = new JavaScriptExpression("WaitForDocument.Content"); } ) .Then( @@ -83,9 +85,8 @@ namespace Sample07 fork .When("Remind") .Then( - x => x.TimeoutExpression = new LiteralExpression("00:00:10"), - id: "RemindTimer" - ) + x => x.TimeoutExpression = new LiteralExpression("00:00:10") + ).WithName("RemindTimer") .Then( x => x.ConditionExpression = new JavaScriptExpression("!!Approved"), ifElse => @@ -112,7 +113,7 @@ namespace Sample07 ); } ) - .Then(x => x.Mode = Join.JoinMode.WaitAny, id: "Join") + .Then(x => x.Mode = Join.JoinMode.WaitAny).WithName("Join") .Then( x => { diff --git a/src/samples/Sample07/Sample07.csproj b/src/samples/Sample07/Sample07.csproj index 6c824e1a3..c9e18fa3e 100644 --- a/src/samples/Sample07/Sample07.csproj +++ b/src/samples/Sample07/Sample07.csproj @@ -5,10 +5,7 @@ - - - - + diff --git a/src/samples/Sample07/Startup.cs b/src/samples/Sample07/Startup.cs index 5d77b221c..f3391cb08 100644 --- a/src/samples/Sample07/Startup.cs +++ b/src/samples/Sample07/Startup.cs @@ -21,7 +21,7 @@ namespace Sample07 public void ConfigureServices(IServiceCollection services) { services - .AddWorkflows() + .AddElsa() .AddTaskExecutingServer() .AddHttpActivities(options => options.Bind(Configuration.GetSection("Http"))) .AddEmailActivities(options => options.Bind(Configuration.GetSection("Smtp"))) diff --git a/src/samples/Sample08/Sample08.csproj b/src/samples/Sample08/Sample08.csproj index 491dc0a22..0eab756a9 100644 --- a/src/samples/Sample08/Sample08.csproj +++ b/src/samples/Sample08/Sample08.csproj @@ -5,15 +5,12 @@ - + - - - - + diff --git a/src/samples/Sample08/Startup.cs b/src/samples/Sample08/Startup.cs index d129097e8..773ed631d 100644 --- a/src/samples/Sample08/Startup.cs +++ b/src/samples/Sample08/Startup.cs @@ -25,7 +25,7 @@ namespace Sample08 public void ConfigureServices(IServiceCollection services) { services - .AddWorkflows() + .AddElsa() .AddTaskExecutingServer() .AddHttpActivities() .AddTimerActivities(options => options.Configure(x => x.SweepInterval = Period.FromSeconds(10))) diff --git a/src/samples/Sample08/Workflows/CreateOrderWorkflow.cs b/src/samples/Sample08/Workflows/CreateOrderWorkflow.cs index d3be3e9f8..72f0a4317 100644 --- a/src/samples/Sample08/Workflows/CreateOrderWorkflow.cs +++ b/src/samples/Sample08/Workflows/CreateOrderWorkflow.cs @@ -1,12 +1,14 @@ using System; using System.Net; using System.Net.Http; +using Elsa.Activities; using Elsa.Activities.Email.Activities; using Elsa.Activities.Http.Activities; using Elsa.Activities.MassTransit.Activities; using Elsa.Activities.ControlFlow; -using Elsa.Activities.Primitives; +using Elsa.Activities.ControlFlow.Activities; using Elsa.Expressions; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; using Sample08.Messages; diff --git a/src/samples/Sample08/Workflows/HandleOrderWorkflow.cs b/src/samples/Sample08/Workflows/HandleOrderWorkflow.cs index 0eeb1512d..7db935f84 100644 --- a/src/samples/Sample08/Workflows/HandleOrderWorkflow.cs +++ b/src/samples/Sample08/Workflows/HandleOrderWorkflow.cs @@ -1,8 +1,9 @@ using System; +using Elsa.Activities; using Elsa.Activities.MassTransit.Activities; using Elsa.Activities.Timers.Activities; -using Elsa.Activities.Primitives; using Elsa.Expressions; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; using Sample08.Messages; diff --git a/src/samples/Sample09/Program.cs b/src/samples/Sample09/Program.cs index 2f4c4cbc2..6dd567f85 100644 --- a/src/samples/Sample09/Program.cs +++ b/src/samples/Sample09/Program.cs @@ -30,18 +30,17 @@ namespace Sample09 private static IServiceProvider BuildServices() { return new ServiceCollection() - .AddWorkflows() + .AddElsa() .AddConsoleActivities() + .AddSingleton(Console.In) .BuildServiceProvider(); } private static async Task ReadEmbeddedResourceAsync(string resourceName) { var assembly = typeof(Program).GetTypeInfo().Assembly; - using (var reader = new StreamReader(assembly.GetManifestResourceStream(resourceName))) - { - return await reader.ReadToEndAsync(); - } + using var reader = new StreamReader(assembly.GetManifestResourceStream(resourceName)); + return await reader.ReadToEndAsync(); } } } \ No newline at end of file diff --git a/src/samples/Sample09/Sample09.csproj b/src/samples/Sample09/Sample09.csproj index 918e35090..3b231370f 100644 --- a/src/samples/Sample09/Sample09.csproj +++ b/src/samples/Sample09/Sample09.csproj @@ -7,8 +7,7 @@ - - + diff --git a/src/samples/Sample10/Program.cs b/src/samples/Sample10/Program.cs index ed7b6b5b1..8cf8c85f4 100644 --- a/src/samples/Sample10/Program.cs +++ b/src/samples/Sample10/Program.cs @@ -25,46 +25,44 @@ namespace Sample10 var startupRunner = services.GetRequiredService(); await startupRunner.StartupAsync(); - using (var scope = services.CreateScope()) - { - var definitionStore = scope.ServiceProvider.GetRequiredService(); - var workflowDefinitionId = nameof(HelloWorldWorkflow); + using var scope = services.CreateScope(); + var definitionStore = scope.ServiceProvider.GetRequiredService(); + const string workflowDefinitionId = nameof(HelloWorldWorkflow); - // When running this program multiple times, we should delete the created workflow definition before adding it to the store again. - await definitionStore.DeleteAsync(workflowDefinitionId); + // When running this program multiple times, we should delete the created workflow definition before adding it to the store again. + await definitionStore.DeleteAsync(workflowDefinitionId); - // Create a workflow definition. - var registry = services.GetService(); - var workflowDefinition = await registry.GetWorkflowDefinitionAsync(); + // Create a workflow definition. + var registry = services.GetService(); + var workflowDefinition = await registry.GetWorkflowDefinitionAsync(); - // Mark this definition as the "latest" version. - workflowDefinition.IsLatest = true; - workflowDefinition.Version = 1; + // Mark this definition as the "latest" version. + workflowDefinition.IsLatest = true; + workflowDefinition.Version = 1; - // Persist the workflow definition. + // Persist the workflow definition. - await definitionStore.SaveAsync(workflowDefinition); + await definitionStore.SaveAsync(workflowDefinition); - // Load the workflow definition. - workflowDefinition = await definitionStore.GetByIdAsync( - workflowDefinition.DefinitionId, - VersionOptions.Latest); + // Load the workflow definition. + workflowDefinition = await definitionStore.GetByIdAsync( + workflowDefinition.DefinitionId, + VersionOptions.Latest); - // Execute the workflow. - var invoker = scope.ServiceProvider.GetRequiredService(); - var executionContext = await invoker.StartAsync(workflowDefinition); + // Execute the workflow. + var invoker = scope.ServiceProvider.GetRequiredService(); + var executionContext = await invoker.StartAsync(workflowDefinition); - // Persist the workflow instance. - var instanceStore = scope.ServiceProvider.GetRequiredService(); - var workflowInstance = executionContext.Workflow.ToInstance(); - await instanceStore.SaveAsync(workflowInstance); - } + // Persist the workflow instance. + var instanceStore = scope.ServiceProvider.GetRequiredService(); + var workflowInstance = executionContext.Workflow.ToInstance(); + await instanceStore.SaveAsync(workflowInstance); } private static IServiceProvider BuildServices() { return new ServiceCollection() - .AddWorkflows( + .AddElsa( x => x.AddYesSqlStores( options => options .UseSqLite( diff --git a/src/samples/Sample10/Sample10.csproj b/src/samples/Sample10/Sample10.csproj index 7c038a34f..6064a5515 100644 --- a/src/samples/Sample10/Sample10.csproj +++ b/src/samples/Sample10/Sample10.csproj @@ -7,11 +7,7 @@ - - - - - + diff --git a/src/samples/Sample11/CorrelationWorkflow.cs b/src/samples/Sample11/CorrelationWorkflow.cs index 01a535769..020b55a1d 100644 --- a/src/samples/Sample11/CorrelationWorkflow.cs +++ b/src/samples/Sample11/CorrelationWorkflow.cs @@ -1,6 +1,8 @@ using Elsa.Activities.Console.Activities; using Elsa.Activities.Workflows; +using Elsa.Activities.Workflows.Activities; using Elsa.Expressions; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; diff --git a/src/samples/Sample11/Program.cs b/src/samples/Sample11/Program.cs index 42be5550d..b2e8f7a9e 100644 --- a/src/samples/Sample11/Program.cs +++ b/src/samples/Sample11/Program.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Threading.Tasks; using Elsa.Activities.Console.Extensions; using Elsa.Activities.Workflows; +using Elsa.Activities.Workflows.Activities; using Elsa.Extensions; using Elsa.Models; using Elsa.Services; @@ -49,7 +50,7 @@ namespace Sample11 private static IServiceProvider BuildServices() { return new ServiceCollection() - .AddWorkflows() + .AddElsa() .AddConsoleActivities() .AddWorkflow() .BuildServiceProvider(); diff --git a/src/samples/Sample11/Sample11.csproj b/src/samples/Sample11/Sample11.csproj index 4b0964690..7d6f6f3e4 100644 --- a/src/samples/Sample11/Sample11.csproj +++ b/src/samples/Sample11/Sample11.csproj @@ -6,8 +6,7 @@ - - + diff --git a/src/samples/Sample12/Program.cs b/src/samples/Sample12/Program.cs index 028e6f648..904e0ef80 100644 --- a/src/samples/Sample12/Program.cs +++ b/src/samples/Sample12/Program.cs @@ -46,7 +46,7 @@ namespace Sample12 private static IServiceProvider BuildServices() { return new ServiceCollection() - .AddWorkflows() + .AddElsa() .AddConsoleActivities() .AddUserTaskActivities() .AddWorkflow() diff --git a/src/samples/Sample12/Sample12.csproj b/src/samples/Sample12/Sample12.csproj index 626e77073..7d6f6f3e4 100644 --- a/src/samples/Sample12/Sample12.csproj +++ b/src/samples/Sample12/Sample12.csproj @@ -6,9 +6,7 @@ - - - + diff --git a/src/samples/Sample13/ActivityOutputWorkflow.cs b/src/samples/Sample13/ActivityOutputWorkflow.cs index 919a548ad..bf6c49cc7 100644 --- a/src/samples/Sample13/ActivityOutputWorkflow.cs +++ b/src/samples/Sample13/ActivityOutputWorkflow.cs @@ -1,5 +1,6 @@ using Elsa.Activities.Console.Activities; using Elsa.Expressions; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; @@ -11,10 +12,10 @@ namespace Sample13 { builder .StartWith(x => x.TextExpression = new LiteralExpression("Hi! What's your name?")) - .Then(id: "name") - .Then(x => x.TextExpression = new JavaScriptExpression("`${name.Input}, that's a great name! Now, what's your age?`")) - .Then(id: "age") - .Then(x => x.TextExpression = new JavaScriptExpression("`I see! So you were born in ${getDateOfBirth(parseInt(age.Input))}. What a year to be alive!`")); + .Then().WithName("Name") + .Then(x => x.TextExpression = new JavaScriptExpression("`${Name.Input}, that's a great name! Now, what's your age?`")) + .Then().WithName("Age") + .Then(x => x.TextExpression = new JavaScriptExpression("`I see! So you were born in ${getDateOfBirth(parseInt(Age.Input))}. What a year to be alive!`")); } } } \ No newline at end of file diff --git a/src/samples/Sample13/Program.cs b/src/samples/Sample13/Program.cs index b70d0b866..b677a2adb 100644 --- a/src/samples/Sample13/Program.cs +++ b/src/samples/Sample13/Program.cs @@ -2,7 +2,9 @@ using System.Threading.Tasks; using Elsa.Activities.Console.Extensions; using Elsa.Scripting; +using Elsa.Scripting.JavaScript; using Elsa.Services; +using MediatR; using Microsoft.Extensions.DependencyInjection; namespace Sample13 @@ -16,9 +18,10 @@ namespace Sample13 { // Setup a service collection. var services = new ServiceCollection() - .AddWorkflows() + .AddElsa() .AddConsoleActivities() - .AddSingleton() + .AddSingleton(Console.In) + .AddMediatR(typeof(Program)) .BuildServiceProvider(); // Create a workflow. diff --git a/src/samples/Sample13/Sample13.csproj b/src/samples/Sample13/Sample13.csproj index fe14b3066..c5ed0af75 100644 --- a/src/samples/Sample13/Sample13.csproj +++ b/src/samples/Sample13/Sample13.csproj @@ -6,8 +6,7 @@ - - + diff --git a/src/samples/Sample13/ScriptEngineConfigurator.cs b/src/samples/Sample13/ScriptEngineConfigurator.cs index d59495862..710c65f41 100644 --- a/src/samples/Sample13/ScriptEngineConfigurator.cs +++ b/src/samples/Sample13/ScriptEngineConfigurator.cs @@ -1,7 +1,12 @@ using System; +using System.Threading; +using System.Threading.Tasks; using Elsa.Scripting; +using Elsa.Scripting.JavaScript; +using Elsa.Scripting.JavaScript.Messages; using Elsa.Services.Models; using Jint; +using MediatR; using NodaTime; namespace Sample13 @@ -9,7 +14,7 @@ namespace Sample13 /// /// Add custom JavaScript functions that are easy to use in workflow expressions. /// - public class ScriptEngineConfigurator : IScriptEngineConfigurator + public class ScriptEngineConfigurator : INotificationHandler { private readonly IClock clock; @@ -18,9 +23,11 @@ namespace Sample13 this.clock = clock; } - public void Configure(Engine engine, WorkflowExecutionContext workflowExecutionContext) + public Task Handle(EvaluatingJavaScriptExpression notification, CancellationToken cancellationToken) { + var engine = notification.Engine; engine.SetValue("getDateOfBirth", (Func) (age => clock.GetCurrentInstant().Minus(Duration.FromDays(int.Parse(age) * 365)).ToDateTimeUtc().Year)); + return Task.CompletedTask; } } } \ No newline at end of file diff --git a/src/samples/Sample14/Program.cs b/src/samples/Sample14/Program.cs index b2c3e2898..291de3f84 100644 --- a/src/samples/Sample14/Program.cs +++ b/src/samples/Sample14/Program.cs @@ -35,43 +35,41 @@ namespace Sample14 workflowDefinition.IsLatest = true; workflowDefinition.Version = 1; - using (var scope = services.CreateScope()) - { - var dbContext = scope.ServiceProvider.GetRequiredService(); + using var scope = services.CreateScope(); + var dbContext = scope.ServiceProvider.GetRequiredService(); - // Ensure DB exists. - await dbContext.Database.EnsureCreatedAsync(); + // Ensure DB exists. + await dbContext.Database.EnsureCreatedAsync(); - // Persist the workflow definition. - var definitionStore = scope.ServiceProvider.GetRequiredService(); - await definitionStore.SaveAsync(workflowDefinition); + // Persist the workflow definition. + var definitionStore = scope.ServiceProvider.GetRequiredService(); + await definitionStore.SaveAsync(workflowDefinition); - // Flush to DB. - await dbContext.SaveChangesAsync(); + // Flush to DB. + await dbContext.SaveChangesAsync(); - // Load the workflow definition. - workflowDefinition = await definitionStore.GetByIdAsync( - workflowDefinition.DefinitionId, - VersionOptions.Latest); + // Load the workflow definition. + workflowDefinition = await definitionStore.GetByIdAsync( + workflowDefinition.DefinitionId, + VersionOptions.Latest); - // Execute the workflow. - var invoker = scope.ServiceProvider.GetRequiredService(); - var executionContext = await invoker.StartAsync(workflowDefinition); + // Execute the workflow. + var invoker = scope.ServiceProvider.GetRequiredService(); + var executionContext = await invoker.StartAsync(workflowDefinition); - // Persist the workflow instance. - var instanceStore = scope.ServiceProvider.GetRequiredService(); - var workflowInstance = executionContext.Workflow.ToInstance(); - await instanceStore.SaveAsync(workflowInstance); + // Persist the workflow instance. + var instanceStore = scope.ServiceProvider.GetRequiredService(); + var workflowInstance = executionContext.Workflow.ToInstance(); + await instanceStore.SaveAsync(workflowInstance); - // Flush to DB. - await dbContext.SaveChangesAsync(); - } + // Flush to DB. + await dbContext.SaveChangesAsync(); } private static IServiceProvider BuildServices() { return new ServiceCollection() - .AddWorkflows( + .AddElsa( x => x.AddEntityFrameworkStores( options => options .UseSqlite(@"Data Source=c:\data\elsa.entity-framework-core.db;Cache=Shared"))) diff --git a/src/samples/Sample14/Sample14.csproj b/src/samples/Sample14/Sample14.csproj index 6ff20127b..e1841cbc5 100644 --- a/src/samples/Sample14/Sample14.csproj +++ b/src/samples/Sample14/Sample14.csproj @@ -7,11 +7,7 @@ - - - - - + diff --git a/src/samples/Sample15/Program.cs b/src/samples/Sample15/Program.cs index b023d37ac..b33028e5a 100644 --- a/src/samples/Sample15/Program.cs +++ b/src/samples/Sample15/Program.cs @@ -31,26 +31,24 @@ namespace Sample15 workflowDefinition.IsLatest = true; workflowDefinition.Version = 1; - using (var scope = services.CreateScope()) - { - // Persist the workflow definition. - var definitionStore = scope.ServiceProvider.GetRequiredService(); - await definitionStore.SaveAsync(workflowDefinition); + using var scope = services.CreateScope(); + // Persist the workflow definition. + var definitionStore = scope.ServiceProvider.GetRequiredService(); + await definitionStore.SaveAsync(workflowDefinition); - // Load the workflow definition. - workflowDefinition = await definitionStore.GetByIdAsync( - workflowDefinition.DefinitionId, - VersionOptions.Latest); + // Load the workflow definition. + workflowDefinition = await definitionStore.GetByIdAsync( + workflowDefinition.DefinitionId, + VersionOptions.Latest); - // Execute the workflow. - var invoker = scope.ServiceProvider.GetRequiredService(); - var executionContext = await invoker.StartAsync(workflowDefinition); + // Execute the workflow. + var invoker = scope.ServiceProvider.GetRequiredService(); + var executionContext = await invoker.StartAsync(workflowDefinition); - // Persist the workflow instance. - var instanceStore = scope.ServiceProvider.GetRequiredService(); - var workflowInstance = executionContext.Workflow.ToInstance(); - await instanceStore.SaveAsync(workflowInstance); - } + // Persist the workflow instance. + var instanceStore = scope.ServiceProvider.GetRequiredService(); + var workflowInstance = executionContext.Workflow.ToInstance(); + await instanceStore.SaveAsync(workflowInstance); } private static IServiceProvider BuildServices() @@ -64,7 +62,7 @@ namespace Sample15 .Build(); return new ServiceCollection() - .AddWorkflows(x => x.AddMongoDbStores(configuration, "Elsa", "MongoDb")) + .AddElsa(x => x.AddMongoDbStores(configuration, "Elsa", "MongoDb")) .AddStartupRunner() .AddConsoleActivities() .AddWorkflow() diff --git a/src/samples/Sample15/Sample15.csproj b/src/samples/Sample15/Sample15.csproj index cee345018..c9a3bf036 100644 --- a/src/samples/Sample15/Sample15.csproj +++ b/src/samples/Sample15/Sample15.csproj @@ -7,8 +7,7 @@ - - + diff --git a/src/samples/Sample16/Sample16.csproj b/src/samples/Sample16/Sample16.csproj index 3542d0b21..c115b3f18 100644 --- a/src/samples/Sample16/Sample16.csproj +++ b/src/samples/Sample16/Sample16.csproj @@ -5,10 +5,7 @@ - - - - + diff --git a/src/samples/Sample16/Startup.cs b/src/samples/Sample16/Startup.cs index 3b32ee100..aebc8711b 100644 --- a/src/samples/Sample16/Startup.cs +++ b/src/samples/Sample16/Startup.cs @@ -27,7 +27,7 @@ namespace Sample16 services // Add workflow services. - .AddWorkflows(x => x.AddMongoDbStores(Configuration, "Sample16", "MongoDb")) + .AddElsa(x => x.AddMongoDbStores(Configuration, "Sample16", "MongoDb")) .AddTaskExecutingServer() // Add activities we'd like to use. diff --git a/src/samples/Sample17/CreatePersonWorkflow.cs b/src/samples/Sample17/CreatePersonWorkflow.cs index c4cb7ae71..187c79128 100644 --- a/src/samples/Sample17/CreatePersonWorkflow.cs +++ b/src/samples/Sample17/CreatePersonWorkflow.cs @@ -1,6 +1,7 @@ -using Elsa.Activities.Console.Activities; -using Elsa.Activities.Primitives; +using Elsa.Activities; +using Elsa.Activities.Console.Activities; using Elsa.Expressions; +using Elsa.Scripting.JavaScript; using Elsa.Services; using Elsa.Services.Models; using Sample17.Activities; @@ -14,15 +15,15 @@ namespace Sample17 { builder .StartWith(x => x.TextExpression = new LiteralExpression("Enter name:")) - .Then().WithId("NameInput") + .Then().WithName("NameInput") .Then(x => x.TextExpression = new LiteralExpression("Enter age:")) - .Then().WithId("AgeInput") + .Then().WithName("AgeInput") .Then( x => { x.TitleExpression = new JavaScriptExpression("NameInput.Input"); x.AgeExpression = new JavaScriptExpression("AgeInput.Input"); - }).WithId("CreatePerson") + }).WithName("CreatePerson") .Then( x => { diff --git a/src/samples/Sample17/Program.cs b/src/samples/Sample17/Program.cs index 11ab1fe04..6ebacae65 100644 --- a/src/samples/Sample17/Program.cs +++ b/src/samples/Sample17/Program.cs @@ -23,11 +23,11 @@ namespace Sample17 private static IServiceProvider BuildServices() { return new ServiceCollection() - .AddWorkflows() - .AddWorkflow() + .AddElsa() .AddConsoleActivities() .AddActivity() .AddSingleton(Console.In) + .AddWorkflow() .BuildServiceProvider(); } } diff --git a/src/samples/Sample17/Sample17.csproj b/src/samples/Sample17/Sample17.csproj index 32f47e3b5..629e53a8d 100644 --- a/src/samples/Sample17/Sample17.csproj +++ b/src/samples/Sample17/Sample17.csproj @@ -7,8 +7,7 @@ - - + diff --git a/src/samples/Sample18/EmailReminderWorkflow.cs b/src/samples/Sample18/EmailReminderWorkflow.cs new file mode 100644 index 000000000..9d1a15ac6 --- /dev/null +++ b/src/samples/Sample18/EmailReminderWorkflow.cs @@ -0,0 +1,50 @@ +using System; +using Elsa; +using Elsa.Activities; +using Elsa.Activities.Console.Activities; +using Elsa.Activities.ControlFlow.Activities; +using Elsa.Activities.Email.Activities; +using Elsa.Activities.Timers.Activities; +using Elsa.Expressions; +using Elsa.Scripting.JavaScript; +using Elsa.Scripting.Liquid; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Sample18 +{ + public class EmailReminderWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder builder) + { + builder + .WithId("RecurringWorkflow") + .AsSingleton() + .StartWith(x => x.TextExpression = new LiteralExpression("Enter a reminder")) + .Then().WithName("ReminderInput") + .Then( + x => + { + x.VariableName = "Todo"; + x.ValueExpression = new JavaScriptExpression("ReminderInput.Input"); + }) + .Then( + x => x.ConditionExpression = new JavaScriptExpression("true"), + doWhile => + { + doWhile + .When(OutcomeNames.Iterate) + .Then(x => x.TimeoutExpression = new LiteralExpression("00:00:01")) + .Then(x => x.TextExpression = new LiquidExpression("Sending reminder: \"{{ Activities.ReminderInput.Input }}\"")) + .Then( + x => + { + x.To = new LiteralExpression("john@acme.com"); + x.Subject = new LiteralExpression("A kind reminder"); + x.Body = new LiquidExpression("Here is a kind reminder of the thing you need to do, which is: {{ Variables.Todo }}"); + }) + .Then(doWhile); + }); + } + } +} \ No newline at end of file diff --git a/src/samples/Sample18/Program.cs b/src/samples/Sample18/Program.cs new file mode 100644 index 000000000..5b211b18f --- /dev/null +++ b/src/samples/Sample18/Program.cs @@ -0,0 +1,60 @@ +using System.IO; +using System.Net.Mail; +using System.Threading.Tasks; +using Elsa.Activities.Console.Extensions; +using Elsa.Activities.Email.Extensions; +using Elsa.Activities.Timers.Extensions; +using Elsa.Services; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using NodaTime; + +namespace Sample18 +{ + /// + /// A minimal workflows program defined in code with a strongly-typed workflow class. + /// + internal static class Program + { + private static string PickupLocation = "c:\\data\\smtp-outbox"; + + private static async Task Main() + { + var host = new HostBuilder() + .ConfigureServices(ConfigureServices) + .ConfigureLogging(logging => logging.AddConsole()) + .UseConsoleLifetime() + .Build(); + + Directory.CreateDirectory(PickupLocation); + + using (host) + { + await host.StartAsync(); + + var workflowInvoker = host.Services.GetRequiredService(); + await workflowInvoker.StartAsync(); + + await host.WaitForShutdownAsync(); + } + } + + private static void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton(System.Console.In) + .AddElsa() + .AddConsoleActivities() + .AddEmailActivities(options => options.Configure( + smtp => + { + smtp.DefaultSender = "reminder@acme.com"; + smtp.DeliveryMethod = SmtpDeliveryMethod.SpecifiedPickupDirectory; + smtp.PickupDirectoryLocation = PickupLocation; + })) + .AddWorkflow() + .AddTimerActivities(options => options.Configure(x => x.SweepInterval = Period.FromSeconds(1))); + } + } +} \ No newline at end of file diff --git a/src/samples/Sample18/Sample18.csproj b/src/samples/Sample18/Sample18.csproj new file mode 100644 index 000000000..edb32d535 --- /dev/null +++ b/src/samples/Sample18/Sample18.csproj @@ -0,0 +1,16 @@ + + + + Exe + netcoreapp3.0 + + + + + + + + + + + diff --git a/src/scripting/Elsa.Scripting.JavaScript/Elsa.Scripting.JavaScript.csproj b/src/scripting/Elsa.Scripting.JavaScript/Elsa.Scripting.JavaScript.csproj new file mode 100644 index 000000000..b75b47663 --- /dev/null +++ b/src/scripting/Elsa.Scripting.JavaScript/Elsa.Scripting.JavaScript.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/src/scripting/Elsa.Scripting.JavaScript/Extensions/JavaScriptServiceCollectionExtensions.cs b/src/scripting/Elsa.Scripting.JavaScript/Extensions/JavaScriptServiceCollectionExtensions.cs new file mode 100644 index 000000000..3e43bee6d --- /dev/null +++ b/src/scripting/Elsa.Scripting.JavaScript/Extensions/JavaScriptServiceCollectionExtensions.cs @@ -0,0 +1,17 @@ +using Elsa.Scripting.JavaScript.Services; +using Elsa.Services; +using MediatR; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Scripting.JavaScript.Extensions +{ + public static class JavaScriptServiceCollectionExtensions + { + public static IServiceCollection AddJavaScriptExpressionEvaluator(this IServiceCollection services) + { + return services + .TryAddProvider(ServiceLifetime.Scoped) + .AddMediatR(typeof(JavaScriptServiceCollectionExtensions)); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs b/src/scripting/Elsa.Scripting.JavaScript/Handlers/CommonJavaScriptEngineHandler.cs similarity index 58% rename from src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs rename to src/scripting/Elsa.Scripting.JavaScript/Handlers/CommonJavaScriptEngineHandler.cs index adcd6a908..77ba9d663 100644 --- a/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs +++ b/src/scripting/Elsa.Scripting.JavaScript/Handlers/CommonJavaScriptEngineHandler.cs @@ -1,55 +1,57 @@ -using System; -using System.Collections.Generic; -using System.Dynamic; -using System.Globalization; -using System.Linq; -using Elsa.Models; -using Elsa.Services.Models; -using Jint; -using Newtonsoft.Json.Linq; - -namespace Elsa.Scripting -{ - public class CommonScriptEngineConfigurator : IScriptEngineConfigurator - { - public void Configure(Engine engine, WorkflowExecutionContext workflowExecutionContext) - { - var context = workflowExecutionContext; - var workflow = workflowExecutionContext.Workflow; - - engine.SetValue("input", (Func) (name => workflow.Input.GetVariable(name))); - engine.SetValue("variable", (Func) (name => context.CurrentScope.GetVariable(name))); - engine.SetValue("lastResult", (Func) (name => context.CurrentScope.LastResult)); - engine.SetValue("correlationId", (Func) (() => context.Workflow.CorrelationId)); - engine.SetValue("currentCulture", (Func) (() => CultureInfo.InvariantCulture)); - - var variables = workflowExecutionContext.Workflow.Scopes.Reverse() - .Select(x => x.Variables) - .Aggregate(Variables.Empty, (x, y) => new Variables(x.Union(y))); - - foreach (var variable in variables) - { - // Jint causes an exception when evaluating expressions using the backtick syntax in combination with JObjects. - // Therefore converting JObjects to ExpandoObjects, allowing expressions such as `My age is ${person.age}`. - - var value = variable.Value is JObject jObject - ? jObject.ToObject() - : variable.Value; - - engine.SetValue(variable.Key, value); - } - - foreach (var activity in workflowExecutionContext.Workflow.Activities.Where(x => x.Output != null)) - { - var expando = new ExpandoObject() as IDictionary; - - foreach (var variable in activity.Output) - { - expando[variable.Key] = variable.Value; - } - - engine.SetValue(activity.Id, expando); - } - } - } +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Scripting.JavaScript.Messages; +using MediatR; +using Newtonsoft.Json.Linq; + +namespace Elsa.Scripting.JavaScript.Handlers +{ + public class CommonJavaScriptEngineHandler : INotificationHandler + { + public Task Handle(EvaluatingJavaScriptExpression notification, CancellationToken cancellationToken) + { + var executionContext = notification.WorkflowExecutionContext; + var workflow = executionContext.Workflow; + var engine = notification.Engine; + + engine.SetValue("input", (Func) (name => workflow.Input.GetVariable(name))); + engine.SetValue("variable", (Func) (name => executionContext.CurrentScope.GetVariable(name))); + engine.SetValue("lastResult", (Func) (name => executionContext.CurrentScope.LastResult)); + engine.SetValue("correlationId", (Func) (() => executionContext.Workflow.CorrelationId)); + engine.SetValue("currentCulture", (Func) (() => CultureInfo.InvariantCulture)); + + var variables = executionContext.GetVariables(); + + foreach (var variable in variables) + { + // Jint causes an exception when evaluating expressions using the backtick syntax in combination with JObjects. + // Therefore converting JObjects to ExpandoObjects, allowing expressions such as `My age is ${person.age}`. + + var value = variable.Value is JObject jObject + ? jObject.ToObject() + : variable.Value; + + engine.SetValue(variable.Key, value); + } + + foreach (var activity in executionContext.Workflow.Activities.Where(x => !string.IsNullOrWhiteSpace(x.Name) && x.Output != null)) + { + var expando = new ExpandoObject() as IDictionary; + + foreach (var variable in activity.Output) + { + expando[variable.Key] = variable.Value; + } + + engine.SetValue(activity.Name, expando); + } + + return Task.CompletedTask; + } + } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Scripting/DateTimeScriptEngineConfigurator.cs b/src/scripting/Elsa.Scripting.JavaScript/Handlers/DateTimeScriptEngineConfigurator.cs similarity index 84% rename from src/core/Elsa.Core/Scripting/DateTimeScriptEngineConfigurator.cs rename to src/scripting/Elsa.Scripting.JavaScript/Handlers/DateTimeScriptEngineConfigurator.cs index e40461268..9e5ccbb0c 100644 --- a/src/core/Elsa.Core/Scripting/DateTimeScriptEngineConfigurator.cs +++ b/src/scripting/Elsa.Scripting.JavaScript/Handlers/DateTimeScriptEngineConfigurator.cs @@ -1,12 +1,14 @@ using System; using System.Globalization; -using Elsa.Services.Models; -using Jint; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Scripting.JavaScript.Messages; +using MediatR; using NodaTime; -namespace Elsa.Scripting +namespace Elsa.Scripting.JavaScript.Handlers { - public class DateTimeScriptEngineConfigurator : IScriptEngineConfigurator + public class DateTimeScriptEngineConfigurator : INotificationHandler { private readonly IClock clock; @@ -14,9 +16,11 @@ namespace Elsa.Scripting { this.clock = clock; } - - public void Configure(Engine engine, WorkflowExecutionContext workflowExecutionContext) + + public Task Handle(EvaluatingJavaScriptExpression notification, CancellationToken cancellationToken) { + var engine = notification.Engine; + engine.SetValue( "currentInstant", (Func) (() => clock.GetCurrentInstant()) @@ -76,6 +80,8 @@ namespace Elsa.Scripting "instantFromLocalDate", (Func) (value => value.AtStartOfDayInZone(DateTimeZone.Utc).ToInstant()) ); + + return Task.CompletedTask; } private LocalDateTime GetStartOfMonth(Instant instant) diff --git a/src/core/Elsa.Core/Expressions/JavaScriptExpression.cs b/src/scripting/Elsa.Scripting.JavaScript/JavaScriptExpression.cs similarity index 51% rename from src/core/Elsa.Core/Expressions/JavaScriptExpression.cs rename to src/scripting/Elsa.Scripting.JavaScript/JavaScriptExpression.cs index f24b76b4e..76f06d253 100644 --- a/src/core/Elsa.Core/Expressions/JavaScriptExpression.cs +++ b/src/scripting/Elsa.Scripting.JavaScript/JavaScriptExpression.cs @@ -1,9 +1,12 @@ -namespace Elsa.Expressions -{ - public class JavaScriptExpression : WorkflowExpression - { - public JavaScriptExpression(string expression) : base(JavaScriptEvaluator.SyntaxName, expression) - { - } - } +using Elsa.Expressions; +using Elsa.Scripting.JavaScript.Services; + +namespace Elsa.Scripting.JavaScript +{ + public class JavaScriptExpression : WorkflowExpression + { + public JavaScriptExpression(string expression) : base(JavaScriptExpressionEvaluator.SyntaxName, expression) + { + } + } } \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.JavaScript/Messages/EvaluatingJavaScriptExpression.cs b/src/scripting/Elsa.Scripting.JavaScript/Messages/EvaluatingJavaScriptExpression.cs new file mode 100644 index 000000000..c0335fb6b --- /dev/null +++ b/src/scripting/Elsa.Scripting.JavaScript/Messages/EvaluatingJavaScriptExpression.cs @@ -0,0 +1,18 @@ +using Elsa.Services.Models; +using Jint; +using MediatR; + +namespace Elsa.Scripting.JavaScript.Messages +{ + public class EvaluatingJavaScriptExpression : INotification + { + public EvaluatingJavaScriptExpression(Engine engine, WorkflowExecutionContext workflowExecutionContext) + { + Engine = engine; + WorkflowExecutionContext = workflowExecutionContext; + } + + public Engine Engine { get; } + public WorkflowExecutionContext WorkflowExecutionContext { get; } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Expressions/JavaScriptEvaluator.cs b/src/scripting/Elsa.Scripting.JavaScript/Services/JavaScriptExpressionEvaluator.cs similarity index 75% rename from src/core/Elsa.Core/Expressions/JavaScriptEvaluator.cs rename to src/scripting/Elsa.Scripting.JavaScript/Services/JavaScriptExpressionEvaluator.cs index 8ebee3075..7d27745b2 100644 --- a/src/core/Elsa.Core/Expressions/JavaScriptEvaluator.cs +++ b/src/scripting/Elsa.Scripting.JavaScript/Services/JavaScriptExpressionEvaluator.cs @@ -1,125 +1,121 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Elsa.Scripting; -using Elsa.Services; -using Elsa.Services.Models; -using Jint; -using Jint.Native; -using Newtonsoft.Json; -using NodaTime; -using NodaTime.Serialization.JsonNet; - -namespace Elsa.Expressions -{ - public class JavaScriptEvaluator : IExpressionEvaluator - { - private readonly IEnumerable configurators; - private readonly JsonSerializerSettings serializerSettings; - public const string SyntaxName = "JavaScript"; - - public static WorkflowExpression CreateExpression(string expression) - { - return new WorkflowExpression(SyntaxName, expression); - } - - public JavaScriptEvaluator(IEnumerable configurators) - { - this.configurators = configurators; - serializerSettings = new JsonSerializerSettings().ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); - } - - public string Syntax => SyntaxName; - - public Task EvaluateAsync(string expression, Type type, - WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) - { - var engine = new Engine(options => { options.AllowClr(); }); - - ConfigureEngine(engine, workflowExecutionContext); - engine.Execute(expression); - - var result = ConvertValue(engine.GetCompletionValue(), type); - - return Task.FromResult(result); - } - - private void ConfigureEngine(Engine engine, WorkflowExecutionContext workflowExecutionContext) - { - foreach (var configurator in configurators) - { - configurator.Configure(engine, workflowExecutionContext); - } - } - - private object ConvertValue(JsValue value, Type targetType) - { - if (value.IsUndefined()) - return null; - - if (value.IsNull()) - return default; - - if (value.IsBoolean()) - return value.AsBoolean(); - - if (value.IsDate()) - return value.AsDate().ToDateTime(); - - if (value.IsNumber()) - return value.AsNumber(); - - if (value.IsString()) - { - var stringValue = value.AsString(); - return targetType != null - ? targetType == typeof(Uri) - ? new Uri(stringValue, UriKind.RelativeOrAbsolute) - : Convert.ChangeType(stringValue, targetType) - : value.AsString(); - } - - if (value.IsArray()) - { - var arrayInstance = value.AsArray(); - var elementType = targetType.GetElementType() ?? targetType.GenericTypeArguments.First(); - - if (elementType == typeof(byte)) - { - var bytes = new byte[arrayInstance.Length]; - - for (uint i = 0; i < arrayInstance.Length; i++) - { - var jsValue = arrayInstance[i]; - bytes[i] = (byte)jsValue.AsNumber(); - } - - return bytes; - } - - var array = Array.CreateInstance(elementType, arrayInstance.Length); - - for (uint i = 0; i < array.Length; i++) - { - var jsValue = arrayInstance[i]; - var convertedValue = ConvertValue(jsValue, elementType); - array.SetValue(convertedValue, i); - } - - return array; - } - - if (value.IsObject()) - { - var obj = value.AsObject().ToObject(); - var type = targetType ?? obj.GetType(); - var json = JsonConvert.SerializeObject(obj, serializerSettings); - return JsonConvert.DeserializeObject(json, type, serializerSettings); - } - - throw new ArgumentException($"Value type {value.Type} is not supported.", nameof(value)); - } - } +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Expressions; +using Elsa.Scripting.JavaScript.Messages; +using Elsa.Services; +using Elsa.Services.Models; +using Jint; +using Jint.Native; +using MediatR; +using Newtonsoft.Json; +using NodaTime; +using NodaTime.Serialization.JsonNet; + +namespace Elsa.Scripting.JavaScript.Services +{ + public class JavaScriptExpressionEvaluator : IExpressionEvaluator + { + private readonly IMediator mediator; + private readonly JsonSerializerSettings serializerSettings; + public const string SyntaxName = "JavaScript"; + + public static WorkflowExpression CreateExpression(string expression) + { + return new WorkflowExpression(SyntaxName, expression); + } + + public JavaScriptExpressionEvaluator(IMediator mediator) + { + this.mediator = mediator; + serializerSettings = new JsonSerializerSettings().ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); + } + + public string Syntax => SyntaxName; + + public async Task EvaluateAsync(string expression, Type type, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) + { + var engine = new Engine(options => { options.AllowClr(); }); + + await ConfigureEngineAsync(engine, workflowExecutionContext, cancellationToken); + engine.Execute(expression); + + return ConvertValue(engine.GetCompletionValue(), type); + } + + private async Task ConfigureEngineAsync(Engine engine, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) + { + await mediator.Publish(new EvaluatingJavaScriptExpression(engine, workflowExecutionContext), cancellationToken); + } + + private object ConvertValue(JsValue value, Type targetType) + { + if (value.IsUndefined()) + return null; + + if (value.IsNull()) + return default; + + if (value.IsBoolean()) + return value.AsBoolean(); + + if (value.IsDate()) + return value.AsDate().ToDateTime(); + + if (value.IsNumber()) + return value.AsNumber(); + + if (value.IsString()) + { + var stringValue = value.AsString(); + return targetType != null + ? targetType == typeof(Uri) + ? new Uri(stringValue, UriKind.RelativeOrAbsolute) + : Convert.ChangeType(stringValue, targetType) + : value.AsString(); + } + + if (value.IsArray()) + { + var arrayInstance = value.AsArray(); + var elementType = targetType.GetElementType() ?? targetType.GenericTypeArguments.First(); + + if (elementType == typeof(byte)) + { + var bytes = new byte[arrayInstance.Length]; + + for (uint i = 0; i < arrayInstance.Length; i++) + { + var jsValue = arrayInstance[i]; + bytes[i] = (byte)jsValue.AsNumber(); + } + + return bytes; + } + + var array = Array.CreateInstance(elementType, arrayInstance.Length); + + for (uint i = 0; i < array.Length; i++) + { + var jsValue = arrayInstance[i]; + var convertedValue = ConvertValue(jsValue, elementType); + array.SetValue(convertedValue, i); + } + + return array; + } + + if (value.IsObject()) + { + var obj = value.AsObject().ToObject(); + var type = targetType ?? obj.GetType(); + var json = JsonConvert.SerializeObject(obj, serializerSettings); + return JsonConvert.DeserializeObject(json, type, serializerSettings); + } + + throw new ArgumentException($"Value type {value.Type} is not supported.", nameof(value)); + } + } } \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/Elsa.Scripting.Liquid.csproj b/src/scripting/Elsa.Scripting.Liquid/Elsa.Scripting.Liquid.csproj new file mode 100644 index 000000000..0abf3f7b2 --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Elsa.Scripting.Liquid.csproj @@ -0,0 +1,15 @@ + + + + netstandard2.0 + + + + + + + + + + + diff --git a/src/scripting/Elsa.Scripting.Liquid/Extensions/LiquidServiceCollectionExtensions.cs b/src/scripting/Elsa.Scripting.Liquid/Extensions/LiquidServiceCollectionExtensions.cs new file mode 100644 index 000000000..7eb350a39 --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Extensions/LiquidServiceCollectionExtensions.cs @@ -0,0 +1,29 @@ +using Elsa.Scripting.Liquid.Filters; +using Elsa.Scripting.Liquid.Options; +using Elsa.Scripting.Liquid.Services; +using Elsa.Services; +using MediatR; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Scripting.Liquid.Extensions +{ + public static class LiquidServiceCollectionExtensions + { + public static IServiceCollection AddLiquidExpressionEvaluator(this IServiceCollection services) + { + return services + .TryAddProvider(ServiceLifetime.Scoped) + .AddMemoryCache() + .AddMediatR(typeof(LiquidServiceCollectionExtensions)) + .AddScoped() + .AddLiquidFilter("json"); + } + + public static IServiceCollection AddLiquidFilter(this IServiceCollection services, string name) where T : class, ILiquidFilter + { + services.Configure(options => options.FilterRegistrations.Add(name, typeof(T))); + services.AddScoped(); + return services; + } + } +} \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/Extensions/LiquidTemplateManagerExtensions.cs b/src/scripting/Elsa.Scripting.Liquid/Extensions/LiquidTemplateManagerExtensions.cs new file mode 100644 index 000000000..3f04e7beb --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Extensions/LiquidTemplateManagerExtensions.cs @@ -0,0 +1,18 @@ +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Elsa.Scripting.Liquid.Services; +using Fluid; + +namespace Elsa.Scripting.Liquid.Extensions +{ + public static class LiquidTemplateManagerExtensions + { + /// + /// Renders a Liquid template containing HTML. + /// + public static Task RenderAsync(this ILiquidTemplateManager manager, string template, TemplateContext context) + { + return manager.RenderAsync(template, context, HtmlEncoder.Default); + } + } +} \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/Extensions/TemplateContextExtensions.cs b/src/scripting/Elsa.Scripting.Liquid/Extensions/TemplateContextExtensions.cs new file mode 100644 index 000000000..47e04b27e --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Extensions/TemplateContextExtensions.cs @@ -0,0 +1,23 @@ +using System; +using Elsa.Scripting.Liquid.Options; +using Elsa.Scripting.Liquid.Services; +using Fluid; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Scripting.Liquid.Extensions +{ + public static class TemplateContextExtensions + { + internal static void AddAsyncFilters(this TemplateContext templateContext, LiquidOptions options, IServiceProvider services) + { + foreach (var registration in options.FilterRegistrations) + { + templateContext.Filters.AddAsyncFilter(registration.Key, (input, arguments, ctx) => + { + var filter = (ILiquidFilter) services.GetRequiredService(registration.Value); + return filter.ProcessAsync(input, arguments, ctx); + }); + } + } + } +} \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/Filters/JsonFilter.cs b/src/scripting/Elsa.Scripting.Liquid/Filters/JsonFilter.cs new file mode 100644 index 000000000..9b23c57c6 --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Filters/JsonFilter.cs @@ -0,0 +1,45 @@ +using System; +using System.Linq; +using System.Threading.Tasks; +using Elsa.Scripting.Liquid.Services; +using Fluid; +using Fluid.Values; +using Newtonsoft.Json; + +namespace Elsa.Scripting.Liquid.Filters +{ + public class JsonFilter : ILiquidFilter + { + public ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext context) + { + switch (input.Type) + { + case FluidValues.Array: + return new ValueTask(new StringValue(JsonConvert.SerializeObject(input.Enumerate().Select(o => o.ToObjectValue())))); + + case FluidValues.Boolean: + return new ValueTask(new StringValue(JsonConvert.SerializeObject(input.ToBooleanValue()))); + + case FluidValues.Nil: + return new ValueTask(FluidValue.Create("null")); + + case FluidValues.Number: + return new ValueTask(new StringValue(JsonConvert.SerializeObject(input.ToNumberValue()))); + + case FluidValues.DateTime: + case FluidValues.Dictionary: + case FluidValues.Object: + return new ValueTask(new StringValue(JsonConvert.SerializeObject(input.ToObjectValue()))); + + case FluidValues.String: + var stringValue = input.ToStringValue(); + + return string.IsNullOrWhiteSpace(stringValue) + ? new ValueTask(input) + : new ValueTask(new StringValue(JsonConvert.SerializeObject(stringValue))); + } + + throw new NotSupportedException("Unrecognized FluidValue"); + } + } +} \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/Handlers/CommonLiquidContextHandler.cs b/src/scripting/Elsa.Scripting.Liquid/Handlers/CommonLiquidContextHandler.cs new file mode 100644 index 000000000..5cec19685 --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Handlers/CommonLiquidContextHandler.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Scripting.Liquid.Helpers; +using Elsa.Scripting.Liquid.Messages; +using Elsa.Services.Models; +using Fluid; +using Fluid.Values; +using MediatR; + +namespace Elsa.Scripting.Liquid.Handlers +{ + public class CommonLiquidContextHandler : INotificationHandler + { + static CommonLiquidContextHandler() + { + FluidValue.SetTypeMapping(x => new ObjectValue(x)); + } + + public Task Handle(EvaluatingLiquidExpression notification, CancellationToken cancellationToken) + { + var context = notification.TemplateContext; + context.MemberAccessStrategy.Register((x, name) => x.GetValueAsync(name)); + context.MemberAccessStrategy.Register("Input", x => new LiquidPropertyAccessor(name => ToFluidValue(x.Workflow.Input, name))); + context.MemberAccessStrategy.Register("Output", x => new LiquidPropertyAccessor(name => ToFluidValue(x.Workflow.Output, name))); + context.MemberAccessStrategy.Register("Variables", x => new LiquidPropertyAccessor(name => ToFluidValue(x.GetVariables(), name))); + context.MemberAccessStrategy.Register>("Activities", x => new LiquidObjectAccessor(name => GetActivityAsync(x, name))); + context.MemberAccessStrategy.Register, LiquidObjectAccessor>((x, activityName) => new LiquidObjectAccessor(outputKey => GetActivityOutput(x, activityName, outputKey))); + context.MemberAccessStrategy.Register, object>((x, name) => x.GetValueAsync(name)); + context.MemberAccessStrategy.Register((x, name) => ((IDictionary)x)[name]); + + return Task.CompletedTask; + } + + private Task ToFluidValue(IDictionary dictionary, string key) + { + return Task.FromResult(!dictionary.ContainsKey(key) ? default : FluidValue.Create(dictionary[key])); + } + + private async Task GetActivityOutput(LiquidObjectAccessor accessor, string activityName, string outputKey) + { + var activity = await accessor.GetValueAsync(activityName); + var output = activity.Output; + return !output.ContainsKey(outputKey) ? default : output[outputKey]; + } + + private Task GetActivityAsync(WorkflowExecutionContext executionContext, string name) => Task.FromResult(executionContext.Workflow.Activities.FirstOrDefault(x => x.Name == name)); + } +} \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/Helpers/LiquidObjectAccessor.cs b/src/scripting/Elsa.Scripting.Liquid/Helpers/LiquidObjectAccessor.cs new file mode 100644 index 000000000..0ed69a09e --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Helpers/LiquidObjectAccessor.cs @@ -0,0 +1,23 @@ +using System; +using System.Threading.Tasks; + +namespace Elsa.Scripting.Liquid.Helpers +{ + /// + /// Can be used to provide a factory to return an object based on a property name that is unknown at registration time. + /// + public class LiquidObjectAccessor + { + private readonly Func> getter; + + public LiquidObjectAccessor(Func> getter) + { + this.getter = getter; + } + + public Task GetValueAsync(string identifier) + { + return getter(identifier); + } + } +} \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/Helpers/LiquidPropertyAccessor.cs b/src/scripting/Elsa.Scripting.Liquid/Helpers/LiquidPropertyAccessor.cs new file mode 100644 index 000000000..0c0baf3de --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Helpers/LiquidPropertyAccessor.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading.Tasks; +using Fluid.Values; + +namespace Elsa.Scripting.Liquid.Helpers +{ + /// + /// Can be used to provide a factory to return a value based on a property name + /// that is unknown at registration time. + /// + /// e.g. {{ LiquidPropertyAccessor.MyPropertyName }} (MyPropertyName will be passed as the identifier argument to the factory) + /// + public class LiquidPropertyAccessor : LiquidObjectAccessor + { + public LiquidPropertyAccessor(Func> getter) : base(getter) + { + } + } +} \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/LiquidExpression.cs b/src/scripting/Elsa.Scripting.Liquid/LiquidExpression.cs new file mode 100644 index 000000000..eacae0ce9 --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/LiquidExpression.cs @@ -0,0 +1,12 @@ +using Elsa.Expressions; +using Elsa.Scripting.Liquid.Services; + +namespace Elsa.Scripting.Liquid +{ + public class LiquidExpression : WorkflowExpression + { + public LiquidExpression(string expression) : base(LiquidExpressionEvaluator.SyntaxName, expression) + { + } + } +} \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/Messages/EvaluatingLiquidExpression.cs b/src/scripting/Elsa.Scripting.Liquid/Messages/EvaluatingLiquidExpression.cs new file mode 100644 index 000000000..3261a98b9 --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Messages/EvaluatingLiquidExpression.cs @@ -0,0 +1,18 @@ +using Elsa.Services.Models; +using Fluid; +using MediatR; + +namespace Elsa.Scripting.Liquid.Messages +{ + public class EvaluatingLiquidExpression : INotification + { + public EvaluatingLiquidExpression(TemplateContext templateContext, WorkflowExecutionContext workflowExecutionContext) + { + TemplateContext = templateContext; + WorkflowExecutionContext = workflowExecutionContext; + } + + public TemplateContext TemplateContext { get; } + public WorkflowExecutionContext WorkflowExecutionContext { get; } + } +} \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/Options/LiquidOptions.cs b/src/scripting/Elsa.Scripting.Liquid/Options/LiquidOptions.cs new file mode 100644 index 000000000..6153a8d61 --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Options/LiquidOptions.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; + +namespace Elsa.Scripting.Liquid.Options +{ + public class LiquidOptions + { + public Dictionary FilterRegistrations { get; } = new Dictionary(); + } +} diff --git a/src/scripting/Elsa.Scripting.Liquid/Services/ILiquidFilter.cs b/src/scripting/Elsa.Scripting.Liquid/Services/ILiquidFilter.cs new file mode 100644 index 000000000..227039f00 --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Services/ILiquidFilter.cs @@ -0,0 +1,11 @@ +using System.Threading.Tasks; +using Fluid; +using Fluid.Values; + +namespace Elsa.Scripting.Liquid.Services +{ + public interface ILiquidFilter + { + ValueTask ProcessAsync(FluidValue input, FilterArguments arguments, TemplateContext ctx); + } +} diff --git a/src/scripting/Elsa.Scripting.Liquid/Services/ILiquidTemplateManager.cs b/src/scripting/Elsa.Scripting.Liquid/Services/ILiquidTemplateManager.cs new file mode 100644 index 000000000..edffdce6b --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Services/ILiquidTemplateManager.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Fluid; + +namespace Elsa.Scripting.Liquid.Services +{ + /// + /// Provides services to render Liquid templates. + /// + public interface ILiquidTemplateManager + { + /// + /// Renders a Liquid template as a . + /// + Task RenderAsync(string template, TemplateContext context, TextEncoder encoder); + + /// + /// Validates a Liquid template. + /// + bool Validate(string template, out IEnumerable errors); + } +} diff --git a/src/scripting/Elsa.Scripting.Liquid/Services/LiquidExpressionEvaluator.cs b/src/scripting/Elsa.Scripting.Liquid/Services/LiquidExpressionEvaluator.cs new file mode 100644 index 000000000..88ad8e2e9 --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Services/LiquidExpressionEvaluator.cs @@ -0,0 +1,42 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Scripting.Liquid.Extensions; +using Elsa.Scripting.Liquid.Messages; +using Elsa.Services; +using Elsa.Services.Models; +using Fluid; +using MediatR; + +namespace Elsa.Scripting.Liquid.Services +{ + public class LiquidExpressionEvaluator : IExpressionEvaluator + { + private readonly ILiquidTemplateManager liquidTemplateManager; + private readonly IMediator mediator; + public const string SyntaxName = "Liquid"; + + public LiquidExpressionEvaluator(ILiquidTemplateManager liquidTemplateManager, IMediator mediator) + { + this.liquidTemplateManager = liquidTemplateManager; + this.mediator = mediator; + } + + public string Syntax => SyntaxName; + + public async Task EvaluateAsync(string expression, Type type, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) + { + var templateContext = await CreateTemplateContextAsync(workflowExecutionContext); + var result = await liquidTemplateManager.RenderAsync(expression, templateContext); + return string.IsNullOrWhiteSpace(result) ? default : Convert.ChangeType(result, type); + } + + private async Task CreateTemplateContextAsync(WorkflowExecutionContext workflowContext) + { + var context = new TemplateContext(); + await mediator.Publish(new EvaluatingLiquidExpression(context, workflowContext)); + context.Model = workflowContext; + return context; + } + } +} \ No newline at end of file diff --git a/src/scripting/Elsa.Scripting.Liquid/Services/LiquidTemplateManager.cs b/src/scripting/Elsa.Scripting.Liquid/Services/LiquidTemplateManager.cs new file mode 100644 index 000000000..cf62c9855 --- /dev/null +++ b/src/scripting/Elsa.Scripting.Liquid/Services/LiquidTemplateManager.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Elsa.Scripting.Liquid.Extensions; +using Elsa.Scripting.Liquid.Options; +using Fluid; +using Microsoft.Extensions.Caching.Memory; +using Microsoft.Extensions.Options; + +namespace Elsa.Scripting.Liquid.Services +{ + public class LiquidTemplateManager : ILiquidTemplateManager + { + private readonly IMemoryCache memoryCache; + private readonly IServiceProvider serviceProvider; + private readonly LiquidOptions options; + + public LiquidTemplateManager(IMemoryCache memoryCache, IOptions options, IServiceProvider serviceProvider) + { + this.memoryCache = memoryCache; + this.serviceProvider = serviceProvider; + this.options = options.Value; + } + + public async Task RenderAsync(string source, TemplateContext context, TextEncoder encoder) + { + if (string.IsNullOrWhiteSpace(source)) + return default; + + context.AddAsyncFilters(options, serviceProvider); + var result = GetCachedTemplate(source); + + return await result.RenderAsync(context, encoder); + } + + private FluidTemplate GetCachedTemplate(string source) + { + IEnumerable errors; + + var result = memoryCache.GetOrCreate( + source, + e => + { + if (!TryParse(source, out var parsed, out errors)) + TryParse(string.Join(Environment.NewLine, errors), out parsed, out errors); + + e.SetSlidingExpiration(TimeSpan.FromSeconds(30)); + return parsed; + }); + return result; + } + + public bool Validate(string template, out IEnumerable errors) => TryParse(template, out _, out errors); + private bool TryParse(string template, out FluidTemplate result, out IEnumerable errors) => FluidTemplate.TryParse(template, true, out result, out errors); + } +} \ No newline at end of file diff --git a/src/core/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj b/src/utils/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj similarity index 97% rename from src/core/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj rename to src/utils/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj index 6105fc850..b5256fec1 100644 --- a/src/core/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj +++ b/src/utils/Elsa.AutoMapper.Extensions/Elsa.AutoMapper.Extensions.csproj @@ -1,22 +1,22 @@ - - - - netstandard2.0 - latest - 1.0.0 - Sipke Schoorstra - Elsa is a set of workflowing libraries and tools to enable super-fast workflowing capabilities in any .NET Core application. - 2019 - https://github.com/elsa-workflows/elsa-core - https://github.com/elsa-workflows/elsa-core - GitHub - elsa, workflows - - - - - - - - - + + + + netstandard2.0 + latest + 1.0.0 + Sipke Schoorstra + Elsa is a set of workflowing libraries and tools to enable super-fast workflowing capabilities in any .NET Core application. + 2019 + https://github.com/elsa-workflows/elsa-core + https://github.com/elsa-workflows/elsa-core + GitHub + elsa, workflows + + + + + + + + + diff --git a/src/core/Elsa.AutoMapper.Extensions/NodaTime/InstantProfile.cs b/src/utils/Elsa.AutoMapper.Extensions/NodaTime/InstantProfile.cs similarity index 100% rename from src/core/Elsa.AutoMapper.Extensions/NodaTime/InstantProfile.cs rename to src/utils/Elsa.AutoMapper.Extensions/NodaTime/InstantProfile.cs diff --git a/src/core/Elsa.AutoMapper.Extensions/ServiceCollectionExtensions.cs b/src/utils/Elsa.AutoMapper.Extensions/ServiceCollectionExtensions.cs similarity index 97% rename from src/core/Elsa.AutoMapper.Extensions/ServiceCollectionExtensions.cs rename to src/utils/Elsa.AutoMapper.Extensions/ServiceCollectionExtensions.cs index 79e2ff153..e078b36f2 100644 --- a/src/core/Elsa.AutoMapper.Extensions/ServiceCollectionExtensions.cs +++ b/src/utils/Elsa.AutoMapper.Extensions/ServiceCollectionExtensions.cs @@ -1,39 +1,39 @@ -using System; -using AutoMapper; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.DependencyInjection.Extensions; - -namespace Elsa.AutoMapper.Extensions -{ - public static class ServiceCollectionExtensions - { - public static IServiceCollection AddAutoMapper(this IServiceCollection services, ServiceLifetime lifetime) - { - services.TryAddSingleton(CreateConfigurationProvider); - services.TryAdd(new ServiceDescriptor(typeof(IMapper), sp => new Mapper(sp.GetRequiredService(), sp.GetService), lifetime)); - - return services; - } - - public static IServiceCollection AddAutoMapperProfile(this IServiceCollection services, ServiceLifetime lifetime) where TProfile : Profile - { - services.AddAutoMapper(lifetime); - return services.AddTransient(); - } - - private static IConfigurationProvider CreateConfigurationProvider(IServiceProvider serviceProvider) - { - var profiles = serviceProvider.GetServices(); - - var configuration = new MapperConfiguration( - x => - { - foreach (var profile in profiles) - x.AddProfile(profile); - } - ); - - return configuration; - } - } +using System; +using AutoMapper; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; + +namespace Elsa.AutoMapper.Extensions +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddAutoMapper(this IServiceCollection services, ServiceLifetime lifetime) + { + services.TryAddSingleton(CreateConfigurationProvider); + services.TryAdd(new ServiceDescriptor(typeof(IMapper), sp => new Mapper(sp.GetRequiredService(), sp.GetService), lifetime)); + + return services; + } + + public static IServiceCollection AddAutoMapperProfile(this IServiceCollection services, ServiceLifetime lifetime) where TProfile : Profile + { + services.AddAutoMapper(lifetime); + return services.AddTransient(); + } + + private static IConfigurationProvider CreateConfigurationProvider(IServiceProvider serviceProvider) + { + var profiles = serviceProvider.GetServices(); + + var configuration = new MapperConfiguration( + x => + { + foreach (var profile in profiles) + x.AddProfile(profile); + } + ); + + return configuration; + } + } } \ No newline at end of file From cd82577511ddf5335bf1d6773e8cc75e9e09baf3 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 27 Oct 2019 15:54:41 +0100 Subject: [PATCH 11/13] Add Roadmap to README --- README.md | 134 ++++++++++++++---------------------------------------- 1 file changed, 33 insertions(+), 101 deletions(-) diff --git a/README.md b/README.md index 562ba2cf4..815bb3650 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,39 @@ Workflows can be defined not only using code but also as JSON, YAML or XML. Follow the [Getting Started](https://elsa-workflows.github.io/elsa-core/docs/installing-elsa-core) instructions on the [Elsa Workflows documentation site](https://elsa-workflows.github.io/elsa-core). +## Roadmap + +Version 1.0 + +- [x] Workflow Invoker +- [x] Long-running Workflows +- [x] Workflows as code +- [x] Workflows as data +- [x] HTML5 Workflow Designer Web Component +- [x] ASP.NET Core Workflow Dashboard +- [x] JavaScript Expressions +- [x] Liquid Expressions +- [x] Primitive Activities +- [X] Control Flow Activities +- [x] Workflow Activities +- [x] Timer Activities +- [x] HTTP Activities +- [x] Email Activities + +Version 2.0 + +- [ ] Service Bus Messaging +- [ ] Generic Command & Event Activities +- [ ] Workflow Host REST API +- [ ] Workflow Host gRPC API +- [ ] Distributed Hosting Support (support for multi-node environments) +- [ ] Localization Support + +Version 3.0 + +- [ ] State Machines +- [ ] Container Activities + ## Workflow Designer Workflows can be visually designed using [Elsa Designer](https://github.com/elsa-workflows/elsa-designer-html), a reusable & extensible HTML5 web component built with [StencilJS](https://stenciljs.com/). @@ -69,107 +102,6 @@ The following providers will be supported: Currently, workflows can be stored in YAML or JSON format. The following demonstrates a simple workflow expressed in YAML and JSON, respectively: -**YAML** -```yaml -activities: -- name: WriteLine - id: activity-1 - textExpression: - syntax: PlainText - expression: Hi! What's your name? -- name: ReadLine - id: activity-2 - argumentName: name -- name: WriteLine - id: activity-3 - textExpression: - syntax: JavaScript - expression: '`Nice to meet you, ${name}!`' -connections: -- source: - activityId: activity-1 - name: Done - target: - activityId: activity-2 -- source: - activityId: activity-2 - name: Done - target: - activityId: activity-3 -``` - -**JSON** -```json -{ - "activities": [ - { - "name": "WriteLine", - "id": "activity-1", - "textExpression": { - "syntax": "PlainText", - "expression": "Hi! What's your name?" - } - }, - { - "id": "activity-2", - "name": "ReadLine", - "argumentName": "name" - }, - { - "name": "WriteLine", - "id": "activity-3", - "textExpression": { - "syntax": "JavaScript", - "expression": "`Nice to meet you, ${name}!`" - } - } - ], - "connections": [ - { - "source": { - "activityId": "activity-1", - "name": "Done" - }, - "target": { - "activityId": "activity-2" - } - }, - { - "source": { - "activityId": "activity-2", - "name": "Done" - }, - "target": { - "activityId": "activity-3" - } - } - ] -} -``` - -The following demonstrates loading a workflow from a YAML string: - -```c# -// Setup a service collection and use the FileSystemProvider for both workflow definitions and workflow instances. -var services = new ServiceCollection() - .AddWorkflowsInvoker() - .AddConsoleActivities() - .AddSingleton(Console.In) - .BuildServiceProvider(); - -// Load the data and specify data format. -var data = Resources.SampleWorkflowDefinition; -var format = YamlTokenFormatter.FormatName; // "YAML" - -// Deserialize the workflow from data. -var serializer = services.GetService(); -var workflowDefinition = await serializer.DeserializeAsync(data, format, CancellationToken.None); - -// Invoke the workflow. -var invoker = services.GetService(); -await invoker.InvokeAsync(workflowDefinition); -``` - ## Long Running Workflows Elsa has native support for long-running workflows. As soon as a workflow is halted because of some blocking activity, the workflow is persisted. From d5d16325f4cc7ba671ef2a81f5121d5fac3c7991 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 27 Oct 2019 16:01:37 +0100 Subject: [PATCH 12/13] Update README --- README.md | 33 ++++++--------------------------- 1 file changed, 6 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index 815bb3650..ecddb6bea 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,8 @@ Version 1.0 - [x] Long-running Workflows - [x] Workflows as code - [x] Workflows as data +- [x] Correlation +- [x] Persistence: CosmosDB, Entity Framework Core, MongoDB, YesSQL - [x] HTML5 Workflow Designer Web Component - [x] ASP.NET Core Workflow Dashboard - [x] JavaScript Expressions @@ -172,35 +174,12 @@ TODO: describe all the steps to add packages and register services. TODO: describe all the steps to add packages and register services. -## Running Elsa Workflows Dashboard +### Building & Running Elsa Workflows Dashboard -In order to run Elsa on your local machine, follow these steps: +In order to build & run Elsa on your local machine, follow these steps: 1. Clone the repository. 2. Run NPM install on all folders containing packages.json (or run `node npm-install.js` - a script in the root that recursively installs the Node packages) 3. Execute gulp build from the directory src\dashboard\Elsa.Dashboard\Theme\argon-dashboard -4. Open a shell and navigate to `src/samples/SampleDashboard.Web` and run `dotnet run`. -5. Navigate to https://localhost:44397/elsa/home - -## Running Elsa Workflows Host - -(TODO) - -## Roadmap - -(TODO) - -- Describe all the features (core engine, runtime, webbased designer, YAML, scripting, separation of designer from invoker from engine). -- Describe various use cases. -- Describe how to use. -- Describe architecture. -- Describe how to implement (custom host, custom dashboard). -- Implement more activities -- Implement integration with Orchard Core (separate repo) -- Detailed documentation -- Open API Activity Harvester -- MassTransit Activity Harvester -- RabbitMQ Activities -- Azure Service Bus Activities -- Automatic UI for Activity Editor - +4. Open a shell and navigate to `src/samples/Sample16` and run `dotnet run`. +5. Navigate to https://localhost:44397/elsa/home \ No newline at end of file From 14f1a06fe53261203bc61451fa024d36f79e118a Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sun, 27 Oct 2019 16:02:21 +0100 Subject: [PATCH 13/13] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index ecddb6bea..a55feea24 100644 --- a/README.md +++ b/README.md @@ -182,4 +182,4 @@ In order to build & run Elsa on your local machine, follow these steps: 2. Run NPM install on all folders containing packages.json (or run `node npm-install.js` - a script in the root that recursively installs the Node packages) 3. Execute gulp build from the directory src\dashboard\Elsa.Dashboard\Theme\argon-dashboard 4. Open a shell and navigate to `src/samples/Sample16` and run `dotnet run`. -5. Navigate to https://localhost:44397/elsa/home \ No newline at end of file +5. Navigate to https://localhost:8632/elsa/home \ No newline at end of file