diff --git a/Samples.sln b/Samples.sln index fb2e2f6e2..6854d488d 100644 --- a/Samples.sln +++ b/Samples.sln @@ -160,6 +160,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.Timers", "src\ EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.WhileLoopWorker", "src\samples\worker\Elsa.Samples.WhileLoopWorker\Elsa.Samples.WhileLoopWorker.csproj", "{EA3832C4-8079-4E84-AF8C-12744E4EFD44}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.CustomAttributesChildWorker", "src\samples\worker\Elsa.Samples.CustomAttributesChildWorker\Elsa.Samples.CustomAttributesChildWorker.csproj", "{AEA00027-F280-43A4-AC60-E2AF3014CF34}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -387,6 +389,10 @@ Global {EA3832C4-8079-4E84-AF8C-12744E4EFD44}.Debug|Any CPU.Build.0 = Debug|Any CPU {EA3832C4-8079-4E84-AF8C-12744E4EFD44}.Release|Any CPU.ActiveCfg = Release|Any CPU {EA3832C4-8079-4E84-AF8C-12744E4EFD44}.Release|Any CPU.Build.0 = Release|Any CPU + {AEA00027-F280-43A4-AC60-E2AF3014CF34}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEA00027-F280-43A4-AC60-E2AF3014CF34}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEA00027-F280-43A4-AC60-E2AF3014CF34}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEA00027-F280-43A4-AC60-E2AF3014CF34}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -463,6 +469,7 @@ Global {A4DF29BD-95CC-4A86-96EC-FB6692270E68} = {E42743A0-FBDD-4150-9D53-6000496D9B87} {FD2D1BD0-1229-4DCF-BE70-6BFD396DD489} = {E42743A0-FBDD-4150-9D53-6000496D9B87} {EA3832C4-8079-4E84-AF8C-12744E4EFD44} = {E42743A0-FBDD-4150-9D53-6000496D9B87} + {AEA00027-F280-43A4-AC60-E2AF3014CF34} = {E42743A0-FBDD-4150-9D53-6000496D9B87} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158} diff --git a/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Activities/RunWorkflow.cs b/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Activities/RunWorkflow.cs index 7c1dc0962..82438a713 100644 --- a/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Activities/RunWorkflow.cs +++ b/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Activities/RunWorkflow.cs @@ -1,8 +1,9 @@ -using System.Threading; +using System.Collections.Generic; +using System.Linq; +using System.Threading; using System.Threading.Tasks; using Elsa.ActivityResults; using Elsa.Attributes; -using Elsa.Extensions; using Elsa.Models; using Elsa.Services; using Elsa.Services.Models; @@ -26,11 +27,12 @@ namespace Elsa.Activities.Workflows _workflowRegistry = workflowRegistry; } - [ActivityProperty] public string WorkflowDefinitionId { get; set; } = default!; + [ActivityProperty] public string? WorkflowDefinitionId { get; set; } = default!; [ActivityProperty] public string? TenantId { get; set; } = default!; [ActivityProperty] public object? Input { get; set; } [ActivityProperty] public string? CorrelationId { get; set; } [ActivityProperty] public string? ContextId { get; set; } + [ActivityProperty] public Variables? CustomAttributes { get; set; } = default!; [ActivityProperty] public RunWorkflowMode Mode { get; set; } public string ChildWorkflowInstanceId @@ -41,8 +43,8 @@ namespace Elsa.Activities.Workflows protected override async ValueTask OnExecuteAsync(ActivityExecutionContext context, CancellationToken cancellationToken) { - var workflowBlueprint = (await _workflowRegistry.GetWorkflowAsync(WorkflowDefinitionId, TenantId, VersionOptions.Published, cancellationToken))!; - var workflowInstance = await _workflowScheduler.RunWorkflowAsync(workflowBlueprint, TenantId, Input, CorrelationId, ContextId, cancellationToken); + var workflowBlueprint = await FindWorkflowBlueprintAsync(cancellationToken); + var workflowInstance = await _workflowScheduler.RunWorkflowAsync(workflowBlueprint!, TenantId, Input, CorrelationId, ContextId, cancellationToken); ChildWorkflowInstanceId = workflowInstance.WorkflowInstanceId; return Mode switch @@ -59,6 +61,25 @@ namespace Elsa.Activities.Workflows return Done(input); } + private async Task FindWorkflowBlueprintAsync(CancellationToken cancellationToken) + { + var query = (IEnumerable)(await _workflowRegistry.GetWorkflowsAsync(cancellationToken).ToListAsync(cancellationToken)); + + query = query.Where(x => x.WithVersion(VersionOptions.Published)); + + if (WorkflowDefinitionId != null) + query = query.Where(x => x.Id == WorkflowDefinitionId); + + if (TenantId != null) + query = query.Where(x => x.TenantId == TenantId); + + if (CustomAttributes != null) + foreach (var customAttribute in CustomAttributes.Data) + query = query.Where(x => Equals(x.CustomAttributes.Get(customAttribute.Key), customAttribute.Value)); + + return query.FirstOrDefault(); + } + public enum RunWorkflowMode { /// diff --git a/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Extensions/RunWorkflowBuilderExtensions.cs b/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Extensions/RunWorkflowBuilderExtensions.cs index 2229a1537..1b3ac33eb 100644 --- a/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Extensions/RunWorkflowBuilderExtensions.cs +++ b/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Extensions/RunWorkflowBuilderExtensions.cs @@ -1,5 +1,6 @@ using System; using Elsa.Builders; +using Elsa.Models; using Elsa.Services.Models; // ReSharper disable once CheckNamespace @@ -37,5 +38,14 @@ namespace Elsa.Activities.Workflows public static IActivityBuilder RunWorkflow(this IBuilder builder, string workflowDefinitionId, string tenantId, RunWorkflow.RunWorkflowMode mode, string correlationId) => builder.RunWorkflow(activity => activity.WithWorkflow(workflowDefinitionId).WithMode(mode).WithCorrelationId(correlationId).WithTenantId(tenantId)); + + public static IActivityBuilder RunWorkflow(this IBuilder builder, string workflowDefinitionId, Variables customAttributes, RunWorkflow.RunWorkflowMode mode, string correlationId) => + builder.RunWorkflow(activity => activity.WithWorkflow(workflowDefinitionId).WithMode(mode).WithCorrelationId(correlationId).WithCustomAttributes(customAttributes)); + + public static IActivityBuilder RunWorkflow(this IBuilder builder, Variables customAttributes, RunWorkflow.RunWorkflowMode mode, string correlationId) => + builder.RunWorkflow(activity => activity.WithMode(mode).WithCorrelationId(correlationId).WithCustomAttributes(customAttributes)); + + public static IActivityBuilder RunWorkflow(this IBuilder builder, Variables customAttributes, RunWorkflow.RunWorkflowMode mode) => + builder.RunWorkflow(activity => activity.WithMode(mode).WithCustomAttributes(customAttributes)); } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Extensions/RunWorkflowExtensions.cs b/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Extensions/RunWorkflowExtensions.cs index d677a236e..5b5b3feb3 100644 --- a/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Extensions/RunWorkflowExtensions.cs +++ b/src/core/Elsa.Core/Activities/Workflows/RunWorkflow/Extensions/RunWorkflowExtensions.cs @@ -2,6 +2,7 @@ using System; using System.Threading.Tasks; using Elsa.Builders; using Elsa.Extensions; +using Elsa.Models; using Elsa.Services; using Elsa.Services.Models; @@ -54,5 +55,11 @@ namespace Elsa.Activities.Workflows public static ISetupActivity WithTenantId(this ISetupActivity activity, Func> value) => activity.Set(x => x.TenantId, value); public static ISetupActivity WithTenantId(this ISetupActivity activity, Func value) => activity.Set(x => x.TenantId, value); public static ISetupActivity WithTenantId(this ISetupActivity activity, string? value) => activity.Set(x => x.TenantId, value); + + public static ISetupActivity WithCustomAttributes(this ISetupActivity activity, Func> value) => activity.Set(x => x.CustomAttributes, value); + public static ISetupActivity WithCustomAttributes(this ISetupActivity activity, Func value) => activity.Set(x => x.CustomAttributes, value); + public static ISetupActivity WithCustomAttributes(this ISetupActivity activity, Func> value) => activity.Set(x => x.CustomAttributes, value); + public static ISetupActivity WithCustomAttributes(this ISetupActivity activity, Func value) => activity.Set(x => x.CustomAttributes, value); + public static ISetupActivity WithCustomAttributes(this ISetupActivity activity, Variables? value) => activity.Set(x => x.CustomAttributes, value); } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Services/WorkflowRegistry.cs b/src/core/Elsa.Core/Services/WorkflowRegistry.cs index b2b1b6046..0af881fa0 100644 --- a/src/core/Elsa.Core/Services/WorkflowRegistry.cs +++ b/src/core/Elsa.Core/Services/WorkflowRegistry.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; @@ -42,5 +43,11 @@ namespace Elsa.Services .OrderByDescending(x => x.Version) .FirstOrDefault(); } + + public async Task> FindWorkflowsAsync(Func predicate, CancellationToken cancellationToken) => + await GetWorkflowsAsync(cancellationToken).Where(predicate).OrderByDescending(x => x.Version).ToListAsync(cancellationToken); + + public async Task FindWorkflowAsync(Func predicate, CancellationToken cancellationToken) => + await GetWorkflowsAsync(cancellationToken).Where(predicate).OrderByDescending(x => x.Version).FirstOrDefaultAsync(cancellationToken); } } \ No newline at end of file diff --git a/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Elsa.Samples.CustomAttributesChildWorker.csproj b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Elsa.Samples.CustomAttributesChildWorker.csproj new file mode 100644 index 000000000..9b10d12ff --- /dev/null +++ b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Elsa.Samples.CustomAttributesChildWorker.csproj @@ -0,0 +1,16 @@ + + + + net5.0 + enable + + + + + + + + + + + diff --git a/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Messages/OrderReceived.cs b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Messages/OrderReceived.cs new file mode 100644 index 000000000..9e4bf98e7 --- /dev/null +++ b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Messages/OrderReceived.cs @@ -0,0 +1,7 @@ +namespace Elsa.Samples.CustomAttributesChildWorker.Messages +{ + public class OrderReceived + { + public string CustomerId { get; set; } + } +} \ No newline at end of file diff --git a/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Program.cs b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Program.cs new file mode 100644 index 000000000..45feacada --- /dev/null +++ b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Program.cs @@ -0,0 +1,32 @@ +using Elsa.Samples.CustomAttributesChildWorker.Messages; +using Elsa.Samples.CustomAttributesChildWorker.Workflows; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; + +namespace Elsa.Samples.CustomAttributesChildWorker +{ + public class Program + { + public static void Main(string[] args) + { + CreateHostBuilder(args).Build().Run(); + } + + public static IHostBuilder CreateHostBuilder(string[] args) => + Host.CreateDefaultBuilder(args) + .ConfigureServices( + (hostContext, services) => + { + services + .AddElsa() + .AddTimerActivities() + .AddConsoleActivities() + .AddRebusActivities() + .AddWorkflow() + .AddWorkflow() + .AddWorkflow() + .AddWorkflow() + ; + }); + } +} \ No newline at end of file diff --git a/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Properties/launchSettings.json b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Properties/launchSettings.json new file mode 100644 index 000000000..505149918 --- /dev/null +++ b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Properties/launchSettings.json @@ -0,0 +1,11 @@ +{ + "profiles": { + "Elsa.Samples.WhileLoopWorker": { + "commandName": "Project", + "dotnetRunMessages": "true", + "environmentVariables": { + "DOTNET_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/Customer1Workflow.cs b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/Customer1Workflow.cs new file mode 100644 index 000000000..77a48f249 --- /dev/null +++ b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/Customer1Workflow.cs @@ -0,0 +1,15 @@ +using Elsa.Activities.Console; +using Elsa.Builders; + +namespace Elsa.Samples.CustomAttributesChildWorker.Workflows +{ + public class Customer1Workflow : IWorkflow + { + public void Build(IWorkflowBuilder workflow) + { + workflow + .WithCustomAttribute("Customer","Customer1") + .WriteLine("Specialized workflow for Customer 1"); + } + } +} \ No newline at end of file diff --git a/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/Customer2Workflow.cs b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/Customer2Workflow.cs new file mode 100644 index 000000000..02b094bca --- /dev/null +++ b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/Customer2Workflow.cs @@ -0,0 +1,15 @@ +using Elsa.Activities.Console; +using Elsa.Builders; + +namespace Elsa.Samples.CustomAttributesChildWorker.Workflows +{ + public class Customer2Workflow : IWorkflow + { + public void Build(IWorkflowBuilder workflow) + { + workflow + .WithCustomAttribute("Customer","Customer2") + .WriteLine("Specialized workflow for Customer 2"); + } + } +} \ No newline at end of file diff --git a/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/GenerateOrdersWorkflow.cs b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/GenerateOrdersWorkflow.cs new file mode 100644 index 000000000..fa4ad4483 --- /dev/null +++ b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/GenerateOrdersWorkflow.cs @@ -0,0 +1,36 @@ +using System; +using Elsa.Activities.Console; +using Elsa.Activities.ControlFlow; +using Elsa.Activities.Rebus; +using Elsa.Activities.Timers; +using Elsa.Builders; +using Elsa.Samples.CustomAttributesChildWorker.Messages; +using NodaTime; + +namespace Elsa.Samples.CustomAttributesChildWorker.Workflows +{ + /// + /// Generate a new order for a random customer every 5 seconds. + /// + public class GenerateOrdersWorkflow : IWorkflow + { + private readonly Random _random; + public GenerateOrdersWorkflow() => _random = new Random(); + + public void Build(IWorkflowBuilder workflow) + { + workflow + .TimerEvent(Duration.FromSeconds(5)) + .SetVariable("CustomerId", SelectRandomCustomerId) + .WriteLine(context => $"Creating a new order for customer {context.GetVariable("CustomerId")}.") + .Then(message => message.Set(x => x.Message, context => new OrderReceived { CustomerId = context.GetVariable("CustomerId") })); + } + + private string SelectRandomCustomerId() + { + var customers = new[] { "Customer1", "Customer2" }; + var index = _random.Next(customers.Length); + return customers[index]; + } + } +} \ No newline at end of file diff --git a/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/OrderReceivedWorkflow.cs b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/OrderReceivedWorkflow.cs new file mode 100644 index 000000000..d5c755867 --- /dev/null +++ b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/Workflows/OrderReceivedWorkflow.cs @@ -0,0 +1,26 @@ +using Elsa.Activities.Console; +using Elsa.Activities.ControlFlow; +using Elsa.Activities.Rebus; +using Elsa.Activities.Workflows; +using Elsa.Builders; +using Elsa.Models; +using Elsa.Samples.CustomAttributesChildWorker.Messages; + +namespace Elsa.Samples.CustomAttributesChildWorker.Workflows +{ + /// + /// Listen for new OrderReceived messages and kick off customer-specific child workflows. + /// + public class OrderReceivedWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder workflow) + { + workflow + .StartWith(activity => activity.Set(x => x.MessageType, typeof(OrderReceived))) + .SetVariable(context => context.GetInput()) + .WriteLine(context => $"Received a new order for {context.GetVariable().CustomerId}.") + .RunWorkflow(activity => activity.WithCustomAttributes(context => new Variables().Set("Customer", context.GetVariable().CustomerId))) + .WriteLine("Returned back from child workflow."); + } + } +} \ No newline at end of file diff --git a/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/appsettings.Development.json b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/appsettings.Development.json new file mode 100644 index 000000000..8983e0fc1 --- /dev/null +++ b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +} diff --git a/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/appsettings.json b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/appsettings.json new file mode 100644 index 000000000..8983e0fc1 --- /dev/null +++ b/src/samples/worker/Elsa.Samples.CustomAttributesChildWorker/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning", + "Microsoft.Hosting.Lifetime": "Information" + } + } +}