diff --git a/Samples.sln b/Samples.sln index 3aae029e0..f74bd12bf 100644 --- a/Samples.sln +++ b/Samples.sln @@ -65,6 +65,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.UserTask", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample12", "samples\Sample12\Sample12.csproj", "{D0F68FBB-17C6-421E-B8AE-AAAFE111BF0B}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample13", "samples\Sample13\Sample13.csproj", "{006BDFC0-0203-4B09-8FBC-52642E22CA79}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -159,6 +161,10 @@ Global {D0F68FBB-17C6-421E-B8AE-AAAFE111BF0B}.Debug|Any CPU.Build.0 = Debug|Any CPU {D0F68FBB-17C6-421E-B8AE-AAAFE111BF0B}.Release|Any CPU.ActiveCfg = Release|Any CPU {D0F68FBB-17C6-421E-B8AE-AAAFE111BF0B}.Release|Any CPU.Build.0 = Release|Any CPU + {006BDFC0-0203-4B09-8FBC-52642E22CA79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {006BDFC0-0203-4B09-8FBC-52642E22CA79}.Debug|Any CPU.Build.0 = Debug|Any CPU + {006BDFC0-0203-4B09-8FBC-52642E22CA79}.Release|Any CPU.ActiveCfg = Release|Any CPU + {006BDFC0-0203-4B09-8FBC-52642E22CA79}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -189,6 +195,7 @@ Global {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} + {006BDFC0-0203-4B09-8FBC-52642E22CA79} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158} diff --git a/samples/Sample13/ActivityOutputWorkflow.cs b/samples/Sample13/ActivityOutputWorkflow.cs new file mode 100644 index 000000000..c44f9ef6c --- /dev/null +++ b/samples/Sample13/ActivityOutputWorkflow.cs @@ -0,0 +1,20 @@ +using Elsa.Activities.Console.Activities; +using Elsa.Expressions; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Sample13 +{ + public class ActivityOutputWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(x => x.TextExpression = new PlainTextExpression("Hi! What's your name?")) + .Then(id: "name") + .Then(x => x.TextExpression = new JavaScriptExpression("`${name.output.text}, 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.output.text))}. What a year to be alive!`")); + } + } +} \ No newline at end of file diff --git a/samples/Sample13/Program.cs b/samples/Sample13/Program.cs new file mode 100644 index 000000000..27a83d392 --- /dev/null +++ b/samples/Sample13/Program.cs @@ -0,0 +1,38 @@ +using System; +using System.Threading.Tasks; +using Elsa.Activities.Console.Extensions; +using Elsa.Extensions; +using Elsa.Persistence.Memory; +using Elsa.Scripting; +using Elsa.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Sample13 +{ + /// + /// A strongly-typed workflows program demonstrating scripting, and branching. + /// + class Program + { + static async Task Main() + { + // Setup a service collection. + var services = new ServiceCollection() + .AddWorkflows() + .AddMemoryWorkflowInstanceStore() + .AddConsoleActivities() + .AddSingleton() + .BuildServiceProvider(); + + // Create a workflow. + var workflowFactory = services.GetRequiredService(); + var workflow = workflowFactory.CreateWorkflow(); + + // Invoke the workflow. + var invoker = services.GetService(); + await invoker.InvokeAsync(workflow); + + Console.ReadLine(); + } + } +} \ No newline at end of file diff --git a/samples/Sample13/Sample13.csproj b/samples/Sample13/Sample13.csproj new file mode 100644 index 000000000..0ffa1e12e --- /dev/null +++ b/samples/Sample13/Sample13.csproj @@ -0,0 +1,13 @@ + + + + Exe + netcoreapp2.2 + + + + + + + + diff --git a/samples/Sample13/ScriptEngineConfigurator.cs b/samples/Sample13/ScriptEngineConfigurator.cs new file mode 100644 index 000000000..0b7cbfbb1 --- /dev/null +++ b/samples/Sample13/ScriptEngineConfigurator.cs @@ -0,0 +1,26 @@ +using System; +using Elsa.Scripting; +using Elsa.Services.Models; +using Jint; +using NodaTime; + +namespace Sample13 +{ + /// + /// Add custom JavaScript functions that are easy to use in workflow expressions. + /// + public class ScriptEngineConfigurator : IScriptEngineConfigurator + { + private readonly IClock clock; + + public ScriptEngineConfigurator(IClock clock) + { + this.clock = clock; + } + + public void Configure(Engine engine, WorkflowExecutionContext workflowExecutionContext) + { + engine.SetValue("getDateOfBirth", (Func) (age => clock.GetCurrentInstant().Minus(Duration.FromDays(int.Parse(age) * 365)).ToDateTimeUtc().Year)); + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs b/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs index acc1d44a8..e3510877c 100644 --- a/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs +++ b/src/activities/Elsa.Activities.Console/Activities/ReadLine.cs @@ -4,6 +4,7 @@ using System.Threading.Tasks; using Elsa.Results; using Elsa.Services; using Elsa.Services.Models; +using Newtonsoft.Json.Linq; namespace Elsa.Activities.Console.Activities { @@ -22,13 +23,13 @@ namespace Elsa.Activities.Console.Activities { this.input = input; } - - public string VariableName + + public string VariableName { get => GetState(); set => SetState(value); } - + protected override async Task OnExecuteAsync(WorkflowExecutionContext context, CancellationToken cancellationToken) { if (input == null) @@ -40,14 +41,18 @@ namespace Elsa.Activities.Console.Activities protected override ActivityExecutionResult OnResume(WorkflowExecutionContext context) { - var receivedInput = (string)context.Workflow.Input[VariableName]; + var receivedInput = (string) context.Workflow.Input[VariableName]; return Execute(context, receivedInput); } private ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, string receivedInput) { - workflowContext.CurrentScope.SetVariable(VariableName, receivedInput); + if (!string.IsNullOrWhiteSpace(VariableName)) + workflowContext.CurrentScope.SetVariable(VariableName, receivedInput); + workflowContext.SetLastResult(receivedInput); + Output = JObject.FromObject(new { text = receivedInput }); + return Outcome(OutcomeNames.Done); } } diff --git a/src/activities/Elsa.Activities.Console/Elsa.Activities.Console.csproj b/src/activities/Elsa.Activities.Console/Elsa.Activities.Console.csproj index 481f8e48b..125881071 100644 --- a/src/activities/Elsa.Activities.Console/Elsa.Activities.Console.csproj +++ b/src/activities/Elsa.Activities.Console/Elsa.Activities.Console.csproj @@ -17,4 +17,8 @@ + + + + diff --git a/src/core/Elsa.Abstractions/Services/ActivityBase.cs b/src/core/Elsa.Abstractions/Services/ActivityBase.cs index 17c190d60..b0522ff6b 100644 --- a/src/core/Elsa.Abstractions/Services/ActivityBase.cs +++ b/src/core/Elsa.Abstractions/Services/ActivityBase.cs @@ -14,15 +14,9 @@ using NodaTime.Serialization.JsonNet; namespace Elsa.Services { public abstract class ActivityBase : IActivity - { - private readonly JsonSerializer serializer; - - protected ActivityBase() - { - serializer = new JsonSerializer().ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); - } - + { public JObject State { get; set; } = new JObject(); + public JObject Output { get; set; } = new JObject(); public virtual string Type => GetType().Name; public string Id { get; set; } diff --git a/src/core/Elsa.Abstractions/Services/Models/IActivity.cs b/src/core/Elsa.Abstractions/Services/Models/IActivity.cs index fdfee5056..c0dbdcb99 100644 --- a/src/core/Elsa.Abstractions/Services/Models/IActivity.cs +++ b/src/core/Elsa.Abstractions/Services/Models/IActivity.cs @@ -13,6 +13,11 @@ namespace Elsa.Services.Models /// JObject State { get; set; } + /// + /// Holds activity output. + /// + JObject Output { get; set; } + /// /// The type name of this activity. /// diff --git a/src/core/Elsa.Core/Elsa.Core.csproj b/src/core/Elsa.Core/Elsa.Core.csproj index 6c94c2cf2..6f8ee17bc 100644 --- a/src/core/Elsa.Core/Elsa.Core.csproj +++ b/src/core/Elsa.Core/Elsa.Core.csproj @@ -16,6 +16,7 @@ + diff --git a/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs b/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs index ce95fca74..8df8a500d 100644 --- a/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs +++ b/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs @@ -1,6 +1,8 @@ using System; +using System.Dynamic; using Elsa.Services.Models; using Jint; +using Newtonsoft.Json; namespace Elsa.Scripting { @@ -10,16 +12,28 @@ namespace Elsa.Scripting { 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) (name => context.Workflow.CorrelationId)); - + foreach (var variable in workflowExecutionContext.CurrentScope.Variables) { engine.SetValue(variable.Key, variable.Value); } + + foreach (var activity in workflowExecutionContext.Workflow.Activities) + { + engine.SetValue( + activity.Id, + new + { + state = activity.State, + output = JsonConvert.DeserializeObject(activity.Output.ToString()) + } + ); + } } } } \ No newline at end of file