diff --git a/Elsa.sln b/Elsa.sln index 6eaabc87a..20922d28e 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -27,7 +27,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Activities", "Activities", EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.Email", "src\activities\Elsa.Activities.Email\Elsa.Activities.Email.csproj", "{D20FCB88-9DCA-49EA-9CC2-5B94DD935D30}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.Cron", "src\activities\Elsa.Activities.Cron\Elsa.Activities.Cron.csproj", "{E4B71DC4-3E73-49C3-9B4E-CA13909F222D}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.Timers", "src\activities\Elsa.Activities.Timers\Elsa.Activities.Timers.csproj", "{E4B71DC4-3E73-49C3-9B4E-CA13909F222D}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample01", "samples\Sample01\Sample01.csproj", "{92750B10-8DAF-4E4F-8999-33304034EDF4}" EndProject @@ -45,6 +45,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{2269 EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample07", "samples\Sample07\Sample07.csproj", "{CC1012DE-511E-41A1-8714-3F1DFCA70BC6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Activities.MassTransit", "src\activities\Elsa.Activities.MassTransit\Elsa.Activities.MassTransit.csproj", "{C4939482-9447-47D1-B6AF-E9F8C6323841}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample08", "samples\Sample08\Sample08.csproj", "{0E58E5FA-BF2F-44CA-9AC8-E8B33CF6AAAA}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -103,6 +107,14 @@ Global {CC1012DE-511E-41A1-8714-3F1DFCA70BC6}.Debug|Any CPU.Build.0 = Debug|Any CPU {CC1012DE-511E-41A1-8714-3F1DFCA70BC6}.Release|Any CPU.ActiveCfg = Release|Any CPU {CC1012DE-511E-41A1-8714-3F1DFCA70BC6}.Release|Any CPU.Build.0 = Release|Any CPU + {C4939482-9447-47D1-B6AF-E9F8C6323841}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4939482-9447-47D1-B6AF-E9F8C6323841}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4939482-9447-47D1-B6AF-E9F8C6323841}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4939482-9447-47D1-B6AF-E9F8C6323841}.Release|Any CPU.Build.0 = Release|Any CPU + {0E58E5FA-BF2F-44CA-9AC8-E8B33CF6AAAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0E58E5FA-BF2F-44CA-9AC8-E8B33CF6AAAA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0E58E5FA-BF2F-44CA-9AC8-E8B33CF6AAAA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0E58E5FA-BF2F-44CA-9AC8-E8B33CF6AAAA}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -123,6 +135,8 @@ Global {F48E32ED-4E85-4D03-8166-7E63E7AB1D6F} = {2269E8BE-894F-46BF-A767-1E0FD046F681} {484E698C-6CC1-4D30-879C-F333AD0D44DD} = {2269E8BE-894F-46BF-A767-1E0FD046F681} {CC1012DE-511E-41A1-8714-3F1DFCA70BC6} = {2269E8BE-894F-46BF-A767-1E0FD046F681} + {C4939482-9447-47D1-B6AF-E9F8C6323841} = {B43B546E-23F3-46E8-ACB7-D04F05CDA180} + {0E58E5FA-BF2F-44CA-9AC8-E8B33CF6AAAA} = {2269E8BE-894F-46BF-A767-1E0FD046F681} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158} diff --git a/samples/Sample01/Program.cs b/samples/Sample01/Program.cs index 20157eaee..c8c0b5544 100644 --- a/samples/Sample01/Program.cs +++ b/samples/Sample01/Program.cs @@ -1,9 +1,7 @@ using System; using System.Threading.Tasks; using Elsa.Core.Extensions; -using Elsa.Core.Services; using Elsa.Services; -using Elsa.Services.Extensions; using Microsoft.Extensions.DependencyInjection; using Sample01.Activities; diff --git a/samples/Sample02/Program.cs b/samples/Sample02/Program.cs index 4162c866a..0b7c7d93c 100644 --- a/samples/Sample02/Program.cs +++ b/samples/Sample02/Program.cs @@ -4,9 +4,7 @@ using Elsa.Activities.Console.Activities; using Elsa.Activities.Console.Extensions; using Elsa.Core.Expressions; using Elsa.Core.Extensions; -using Elsa.Expressions; using Elsa.Services; -using Elsa.Services.Extensions; using Microsoft.Extensions.DependencyInjection; namespace Sample02 diff --git a/samples/Sample03/Program.cs b/samples/Sample03/Program.cs index c93953929..dee9f7134 100644 --- a/samples/Sample03/Program.cs +++ b/samples/Sample03/Program.cs @@ -5,9 +5,7 @@ using Elsa.Activities.Console.Extensions; using Elsa.Core.Expressions; using Elsa.Core.Extensions; using Elsa.Models; -using Elsa.Serialization.Models; using Elsa.Services; -using Elsa.Services.Models; using Microsoft.Extensions.DependencyInjection; namespace Sample03 diff --git a/samples/Sample04/Activities/ArithmeticOperation.cs b/samples/Sample04/Activities/ArithmeticOperation.cs index 9852253ae..b803157b4 100644 --- a/samples/Sample04/Activities/ArithmeticOperation.cs +++ b/samples/Sample04/Activities/ArithmeticOperation.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using Elsa.Core.Expressions; +using Elsa.Core.Extensions; using Elsa.Core.Services; using Elsa.Results; using Elsa.Services; diff --git a/samples/Sample04/Program.cs b/samples/Sample04/Program.cs index 9719a2d20..a49fdf20f 100644 --- a/samples/Sample04/Program.cs +++ b/samples/Sample04/Program.cs @@ -3,7 +3,6 @@ using System.Threading.Tasks; using Elsa.Activities.Console.Extensions; using Elsa.Core.Extensions; using Elsa.Services; -using Elsa.Services.Extensions; using Microsoft.Extensions.DependencyInjection; using Sample04.Activities; diff --git a/samples/Sample05/Program.cs b/samples/Sample05/Program.cs index 4b417506e..349cf1bff 100644 --- a/samples/Sample05/Program.cs +++ b/samples/Sample05/Program.cs @@ -1,11 +1,10 @@ using System; using System.Threading.Tasks; using Elsa.Activities.Console.Extensions; -using Elsa.Activities.Cron.Extensions; +using Elsa.Activities.Timers.Extensions; using Elsa.Core.Extensions; using Elsa.Core.Persistence.Extensions; using Elsa.Services; -using Elsa.Services.Extensions; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; @@ -46,7 +45,7 @@ namespace Sample05 services .AddWorkflows() .AddConsoleActivities() - .AddCronActivities(options => options.Configure(x => x.SweepInterval = Period.FromSeconds(10))) + .AddTimerActivities(options => options.Configure(x => x.SweepInterval = Period.FromSeconds(10))) .AddMemoryWorkflowDefinitionStore() .AddMemoryWorkflowInstanceStore(); } diff --git a/samples/Sample05/RecurringWorkflow.cs b/samples/Sample05/RecurringWorkflow.cs index ab59e3773..9504b5895 100644 --- a/samples/Sample05/RecurringWorkflow.cs +++ b/samples/Sample05/RecurringWorkflow.cs @@ -1,5 +1,5 @@ using Elsa.Activities.Console.Activities; -using Elsa.Activities.Cron.Activities; +using Elsa.Activities.Timers.Activities; using Elsa.Core.Expressions; using Elsa.Services; using Elsa.Services.Models; @@ -13,7 +13,7 @@ namespace Sample05 builder .WithId("RecurringWorkflow") .AsSingleton() - .StartWith(x => x.CronExpression = new PlainTextExpression("* * * * *")) + .StartWith(x => x.CronExpression = new PlainTextExpression("* * * * *")) .Then(x => x.TextExpression = new JavaScriptExpression("`Trigger received. The time is: ${new Date().toISOString()}`")); } } diff --git a/samples/Sample05/Sample05.csproj b/samples/Sample05/Sample05.csproj index 5b08e65aa..2b4544002 100644 --- a/samples/Sample05/Sample05.csproj +++ b/samples/Sample05/Sample05.csproj @@ -7,7 +7,7 @@ - + diff --git a/samples/Sample06/Sample06.csproj b/samples/Sample06/Sample06.csproj index c9cd57380..7081f6b87 100644 --- a/samples/Sample06/Sample06.csproj +++ b/samples/Sample06/Sample06.csproj @@ -10,9 +10,9 @@ - + diff --git a/samples/Sample07/DocumentApprovalWorkflow.cs b/samples/Sample07/DocumentApprovalWorkflow.cs index 62c360a68..c8ccf0cb8 100644 --- a/samples/Sample07/DocumentApprovalWorkflow.cs +++ b/samples/Sample07/DocumentApprovalWorkflow.cs @@ -6,7 +6,6 @@ using Elsa.Activities.Email.Activities; using Elsa.Activities.Http.Activities; using Elsa.Core.Activities.Primitives; using Elsa.Core.Expressions; -using Elsa.Core.Services; using Elsa.Services; using Elsa.Services.Models; @@ -29,7 +28,7 @@ namespace Sample07 activity => { activity.VariableName = "document"; - activity.ValueExpression = new JavaScriptExpression("lastResult().FormattedContent"); + activity.ValueExpression = new JavaScriptExpression("lastResult().ParsedContent"); } ) .Then( diff --git a/samples/Sample07/Program.cs b/samples/Sample07/Program.cs index a3e3709c3..622bef477 100644 --- a/samples/Sample07/Program.cs +++ b/samples/Sample07/Program.cs @@ -1,12 +1,5 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; -using Microsoft.AspNetCore; +using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; namespace Sample07 { diff --git a/samples/Sample07/Sample07.csproj b/samples/Sample07/Sample07.csproj index 4be7b9a69..072fa7e4b 100644 --- a/samples/Sample07/Sample07.csproj +++ b/samples/Sample07/Sample07.csproj @@ -10,9 +10,9 @@ - + diff --git a/samples/Sample08/Messages/CreateOrder.cs b/samples/Sample08/Messages/CreateOrder.cs new file mode 100644 index 000000000..b50e2a047 --- /dev/null +++ b/samples/Sample08/Messages/CreateOrder.cs @@ -0,0 +1,9 @@ +using Sample08.Models; + +namespace Sample08.Messages +{ + public class CreateOrder + { + public Order Order { get; set; } + } +} \ No newline at end of file diff --git a/samples/Sample08/Messages/OrderShipped.cs b/samples/Sample08/Messages/OrderShipped.cs new file mode 100644 index 000000000..6d7b9f922 --- /dev/null +++ b/samples/Sample08/Messages/OrderShipped.cs @@ -0,0 +1,9 @@ +using Sample08.Models; + +namespace Sample08.Messages +{ + public class OrderShipped + { + public Order Order { get; set; } + } +} \ No newline at end of file diff --git a/samples/Sample08/Models/Customer.cs b/samples/Sample08/Models/Customer.cs new file mode 100644 index 000000000..61bcc9539 --- /dev/null +++ b/samples/Sample08/Models/Customer.cs @@ -0,0 +1,8 @@ +namespace Sample08.Models +{ + public class Customer + { + public string Name { get; set; } + public string Email { get; set; } + } +} \ No newline at end of file diff --git a/samples/Sample08/Models/Order.cs b/samples/Sample08/Models/Order.cs new file mode 100644 index 000000000..f46742967 --- /dev/null +++ b/samples/Sample08/Models/Order.cs @@ -0,0 +1,10 @@ +namespace Sample08.Models +{ + public class Order + { + public string Id { get; set; } + public Customer Customer { get; set; } + public string Product { get; set; } + public decimal Amount { get; set; } + } +} \ No newline at end of file diff --git a/samples/Sample08/Program.cs b/samples/Sample08/Program.cs new file mode 100644 index 000000000..21140c195 --- /dev/null +++ b/samples/Sample08/Program.cs @@ -0,0 +1,17 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; + +namespace Sample08 +{ + public class Program + { + public static void Main(string[] args) + { + CreateWebHostBuilder(args).Build().Run(); + } + + public static IWebHostBuilder CreateWebHostBuilder(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup(); + } +} \ No newline at end of file diff --git a/samples/Sample08/Properties/launchSettings.json b/samples/Sample08/Properties/launchSettings.json new file mode 100644 index 000000000..f67974bbb --- /dev/null +++ b/samples/Sample08/Properties/launchSettings.json @@ -0,0 +1,30 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:32051", + "sslPort": 44347 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "api/values", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "Sample07": { + "commandName": "Project", + "launchBrowser": true, + "launchUrl": "api/values", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/samples/Sample08/Sample08.csproj b/samples/Sample08/Sample08.csproj new file mode 100644 index 000000000..f49c1ffa0 --- /dev/null +++ b/samples/Sample08/Sample08.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp2.2 + + + + + + + + + + + + + + + + + + diff --git a/samples/Sample08/Startup.cs b/samples/Sample08/Startup.cs new file mode 100644 index 000000000..39db340cb --- /dev/null +++ b/samples/Sample08/Startup.cs @@ -0,0 +1,56 @@ +using Elsa.Activities.Email.Extensions; +using Elsa.Activities.Http.Extensions; +using Elsa.Activities.MassTransit.Extensions; +using Elsa.Activities.Timers.Extensions; +using Elsa.Core.Extensions; +using Elsa.Core.Persistence.Extensions; +using Elsa.Services; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using NodaTime; +using Sample08.Messages; +using Sample08.Workflows; +using IHostingEnvironment = Microsoft.AspNetCore.Hosting.IHostingEnvironment; + +namespace Sample08 +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + public void ConfigureServices(IServiceCollection services) + { + services + .AddWorkflows() + .AddHttpActivities() + .AddTimerActivities(options => options.Configure(x => x.SweepInterval = Period.FromSeconds(10))) + .AddEmailActivities(options => options.Bind(Configuration.GetSection("Smtp"))) + .AddRabbitMqActivities( + options => options.Bind(Configuration.GetSection("MassTransit:RabbitMq")), + typeof(CreateOrder), + typeof(OrderShipped)) + .AddMemoryWorkflowDefinitionStore() + .AddMemoryWorkflowInstanceStore(); + } + + public void Configure(IApplicationBuilder app, IHostingEnvironment env, IWorkflowRegistry workflowRegistry) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseHttpActivities(); + + workflowRegistry.RegisterWorkflow(); + workflowRegistry.RegisterWorkflow(); + } + } +} \ No newline at end of file diff --git a/samples/Sample08/Workflows/CreateOrderWorkflow.cs b/samples/Sample08/Workflows/CreateOrderWorkflow.cs new file mode 100644 index 000000000..9468de76e --- /dev/null +++ b/samples/Sample08/Workflows/CreateOrderWorkflow.cs @@ -0,0 +1,74 @@ +using System; +using System.Net; +using System.Net.Http; +using Elsa.Activities.Email.Activities; +using Elsa.Activities.Http.Activities; +using Elsa.Activities.MassTransit.Activities; +using Elsa.Core.Activities.Primitives; +using Elsa.Core.Expressions; +using Elsa.Services; +using Elsa.Services.Models; +using Sample08.Messages; + +namespace Sample08.Workflows +{ + public class CreateOrderWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith( + activity => + { + activity.Method = HttpMethod.Post.Method; + activity.Path = new Uri("/orders", UriKind.RelativeOrAbsolute); + activity.ReadContent = true; + } + ) + .Then( + activity => + { + activity.VariableName = "order"; + activity.ValueExpression = new JavaScriptExpression("lastResult().ParsedContent"); + } + ) + .Then(activity => + { + activity.Message = new JavaScriptExpression("return {order: order};"); + activity.MessageType = typeof(CreateOrder); + } + ) + .Then( + activity => activity.Forks = new[] { "Write-Response", "Await-Shipment" }, + fork => + { + fork + .When("Write-Response") + .Then( + activity => + { + activity.Body = new PlainTextExpression("

Order Received

Your order has been received. Waiting for shipment.

"); + activity.ContentType = new PlainTextExpression("text/html"); + activity.StatusCode = HttpStatusCode.Accepted; + } + ); + + fork + .When("Await-Shipment") + .Then(activity => activity.MessageType = typeof(OrderShipped)) + .Then( + activity => + { + activity.From = new PlainTextExpression("shipment@acme.com"); + activity.To = new JavaScriptExpression("order.customer.email"); + activity.Subject = new JavaScriptExpression("`Your order with ID #${order.id} has been shipped!`"); + activity.Body = new JavaScriptExpression( + "`Dear ${order.customer.name}, your order has shipped!`" + ); + } + ); + } + ); + } + } +} \ No newline at end of file diff --git a/samples/Sample08/Workflows/HandleOrderWorkflow.cs b/samples/Sample08/Workflows/HandleOrderWorkflow.cs new file mode 100644 index 000000000..ee40c02ed --- /dev/null +++ b/samples/Sample08/Workflows/HandleOrderWorkflow.cs @@ -0,0 +1,34 @@ +using System; +using Elsa.Activities.MassTransit.Activities; +using Elsa.Activities.Timers.Activities; +using Elsa.Core.Activities.Primitives; +using Elsa.Core.Expressions; +using Elsa.Services; +using Elsa.Services.Models; +using Sample08.Messages; + +namespace Sample08.Workflows +{ + public class HandleOrderWorkflow : IWorkflow + { + public void Build(IWorkflowBuilder builder) + { + builder + .StartWith(activity => activity.MessageType = typeof(CreateOrder)) + .Then( + activity => + { + activity.VariableName = "order"; + activity.ValueExpression = new JavaScriptExpression("lastResult().Order"); + } + ) + .Then(activity => activity.TimeoutExpression = new PlainTextExpression("00:00:05")) + .Then(activity => + { + activity.Message = new JavaScriptExpression("return {order: order}"); + activity.MessageType = typeof(OrderShipped); + } + ); + } + } +} \ No newline at end of file diff --git a/samples/Sample08/app.config b/samples/Sample08/app.config new file mode 100644 index 000000000..a6ad82831 --- /dev/null +++ b/samples/Sample08/app.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/samples/Sample08/appsettings.Development.json b/samples/Sample08/appsettings.Development.json new file mode 100644 index 000000000..e203e9407 --- /dev/null +++ b/samples/Sample08/appsettings.Development.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Debug", + "System": "Information", + "Microsoft": "Information" + } + } +} diff --git a/samples/Sample08/appsettings.json b/samples/Sample08/appsettings.json new file mode 100644 index 000000000..d10c4eb0d --- /dev/null +++ b/samples/Sample08/appsettings.json @@ -0,0 +1,17 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Warning" + } + }, + "AllowedHosts": "*", + "Smtp": { + "Host": "localhost", + "Port": "2525" + }, + "MassTransit": { + "RabbitMq": { + "Host": "rabbitmq://localhost:5672" + } + } +} diff --git a/samples/Sample08/docker-compose.yaml b/samples/Sample08/docker-compose.yaml new file mode 100644 index 000000000..329edc2e1 --- /dev/null +++ b/samples/Sample08/docker-compose.yaml @@ -0,0 +1,10 @@ +version: '3.7' + +services: + rabbitmq: + image: "rabbitmq:3-management" + ports: + - "15672:15672" + - "5672:5672" + labels: + name: "rabbitmq" \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Console/Activities/WriteLine.cs b/src/activities/Elsa.Activities.Console/Activities/WriteLine.cs index fd694a5a9..263a45d51 100644 --- a/src/activities/Elsa.Activities.Console/Activities/WriteLine.cs +++ b/src/activities/Elsa.Activities.Console/Activities/WriteLine.cs @@ -2,8 +2,8 @@ using System.Threading; using System.Threading.Tasks; using Elsa.Core.Expressions; +using Elsa.Core.Extensions; using Elsa.Core.Services; -using Elsa.Expressions; using Elsa.Results; using Elsa.Services; using Elsa.Services.Models; diff --git a/src/activities/Elsa.Activities.Cron/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Cron/Extensions/ServiceCollectionExtensions.cs deleted file mode 100644 index a60a244f1..000000000 --- a/src/activities/Elsa.Activities.Cron/Extensions/ServiceCollectionExtensions.cs +++ /dev/null @@ -1,24 +0,0 @@ -using System; -using Elsa.Activities.Cron.Activities; -using Elsa.Activities.Cron.HostedServices; -using Elsa.Activities.Cron.Options; -using Elsa.Core.Extensions; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Elsa.Activities.Cron.Extensions -{ - public static class ServiceCollectionExtensions - { - public static IServiceCollection AddCronActivities(this IServiceCollection services, Action> options = null) - { - var optionsBuilder = services.AddOptions(); - options?.Invoke(optionsBuilder); - - return services - .AddOptions() - .AddHostedService() - .AddActivity(); - } - } -} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Email/Activities/SendEmail.cs b/src/activities/Elsa.Activities.Email/Activities/SendEmail.cs index ecff02133..824438623 100644 --- a/src/activities/Elsa.Activities.Email/Activities/SendEmail.cs +++ b/src/activities/Elsa.Activities.Email/Activities/SendEmail.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Elsa.Core.Expressions; +using Elsa.Core.Extensions; using Elsa.Core.Services; using Elsa.Results; using Elsa.Services; diff --git a/src/activities/Elsa.Activities.Email/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Email/Extensions/ServiceCollectionExtensions.cs index 187d7979f..cd12f2a19 100644 --- a/src/activities/Elsa.Activities.Email/Extensions/ServiceCollectionExtensions.cs +++ b/src/activities/Elsa.Activities.Email/Extensions/ServiceCollectionExtensions.cs @@ -3,7 +3,6 @@ using System.Net.Mail; using Elsa.Activities.Email.Activities; using Elsa.Activities.Email.Options; using Elsa.Core.Extensions; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; diff --git a/src/activities/Elsa.Activities.Http/Activities/HttpRequestAction.cs b/src/activities/Elsa.Activities.Http/Activities/HttpRequestAction.cs index 2eccf92c4..c4aae95c9 100644 --- a/src/activities/Elsa.Activities.Http/Activities/HttpRequestAction.cs +++ b/src/activities/Elsa.Activities.Http/Activities/HttpRequestAction.cs @@ -9,6 +9,7 @@ using System.Threading.Tasks; using Elsa.Activities.Http.Models; using Elsa.Activities.Http.Services; using Elsa.Core.Expressions; +using Elsa.Core.Extensions; using Elsa.Core.Services; using Elsa.Results; using Elsa.Services; @@ -111,7 +112,7 @@ namespace Elsa.Activities.Http.Activities StatusCode = response.StatusCode, Headers = new HeaderDictionary(response.Headers.ToDictionary(x => x.Key, x => new StringValues(x.Value.ToArray()))), Content = content, - FormattedContent = await formatter.FormatAsync(content, contentType) + FormattedContent = await formatter.ParseAsync(content, contentType) }; workflowContext.SetLastResult(responseModel); diff --git a/src/activities/Elsa.Activities.Http/Activities/HttpRequestTrigger.cs b/src/activities/Elsa.Activities.Http/Activities/HttpRequestTrigger.cs index 237e931c9..d706478d9 100644 --- a/src/activities/Elsa.Activities.Http/Activities/HttpRequestTrigger.cs +++ b/src/activities/Elsa.Activities.Http/Activities/HttpRequestTrigger.cs @@ -9,9 +9,7 @@ using Elsa.Activities.Http.Models; using Elsa.Activities.Http.Services; using Elsa.Core.Services; using Elsa.Extensions; -using Elsa.Models; using Elsa.Results; -using Elsa.Services; using Elsa.Services.Models; using Microsoft.AspNetCore.Http; using Newtonsoft.Json.Linq; @@ -98,10 +96,10 @@ namespace Elsa.Activities.Http.Activities model.Form = (await request.ReadFormAsync(cancellationToken)).ToDictionary(x => x.Key, x => x.Value); } - var formatter = SelectContentFormatter(request.ContentType); + var parser = SelectContentParser(request.ContentType); var content = await request.ReadBodyAsync(); model.Content = content; - model.FormattedContent = await formatter.FormatAsync(content, request.ContentType); + model.ParsedContent = await parser.ParseAsync(content, request.ContentType); } workflowContext.CurrentScope.LastResult = model; @@ -109,7 +107,7 @@ namespace Elsa.Activities.Http.Activities return Done(); } - private IContentFormatter SelectContentFormatter(string contentType) + private IContentFormatter SelectContentParser(string contentType) { var formatters = contentFormatters.OrderByDescending(x => x.Priority).ToList(); return formatters.FirstOrDefault(x => x.SupportedContentTypes.Contains(contentType, StringComparer.OrdinalIgnoreCase)) ?? formatters.Last(); diff --git a/src/activities/Elsa.Activities.Http/Activities/HttpResponseAction.cs b/src/activities/Elsa.Activities.Http/Activities/HttpResponseAction.cs index d97112246..9e8b180c0 100644 --- a/src/activities/Elsa.Activities.Http/Activities/HttpResponseAction.cs +++ b/src/activities/Elsa.Activities.Http/Activities/HttpResponseAction.cs @@ -5,6 +5,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Threading.Tasks; using Elsa.Core.Expressions; +using Elsa.Core.Extensions; using Elsa.Core.Services; using Elsa.Results; using Elsa.Services; diff --git a/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj b/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj index ee04f05ea..15587447e 100644 --- a/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj +++ b/src/activities/Elsa.Activities.Http/Elsa.Activities.Http.csproj @@ -17,7 +17,6 @@ - diff --git a/src/activities/Elsa.Activities.Http/Extensions/ApplicationBuilderExtensions.cs b/src/activities/Elsa.Activities.Http/Extensions/ApplicationBuilderExtensions.cs index 95a500f04..722f00e59 100644 --- a/src/activities/Elsa.Activities.Http/Extensions/ApplicationBuilderExtensions.cs +++ b/src/activities/Elsa.Activities.Http/Extensions/ApplicationBuilderExtensions.cs @@ -1,5 +1,4 @@ using Elsa.Activities.Http.Middleware; -using Elsa.Activities.Http.RequestHandlers; using Elsa.Activities.Http.RequestHandlers.Handlers; using Elsa.Activities.Http.Services; using Microsoft.AspNetCore.Builder; diff --git a/src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs index f047b4f62..140c5551c 100644 --- a/src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs +++ b/src/activities/Elsa.Activities.Http/Extensions/ServiceCollectionExtensions.cs @@ -1,18 +1,12 @@ using Elsa.Activities.Http.Activities; using Elsa.Activities.Http.Formatters; -using Elsa.Activities.Http.Middleware; -using Elsa.Activities.Http.Models; -using Elsa.Activities.Http.RequestHandlers; using Elsa.Activities.Http.RequestHandlers.Handlers; using Elsa.Activities.Http.Scripting; using Elsa.Activities.Http.Services; using Elsa.Core.Extensions; using Elsa.Scripting; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; -using Microsoft.AspNetCore.Mvc.Routing; -using Microsoft.AspNetCore.Routing; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; diff --git a/src/activities/Elsa.Activities.Http/Formatters/JsonContentFormatter.cs b/src/activities/Elsa.Activities.Http/Formatters/JsonContentFormatter.cs index e2b3007d9..1f71ce012 100644 --- a/src/activities/Elsa.Activities.Http/Formatters/JsonContentFormatter.cs +++ b/src/activities/Elsa.Activities.Http/Formatters/JsonContentFormatter.cs @@ -3,7 +3,6 @@ using System.Dynamic; using System.Threading.Tasks; using Elsa.Activities.Http.Services; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; namespace Elsa.Activities.Http.Formatters { @@ -12,7 +11,7 @@ namespace Elsa.Activities.Http.Formatters public int Priority => 0; public IEnumerable SupportedContentTypes => new[] { "application/json", "text/json" }; - public Task FormatAsync(string content, string contentType) + public Task ParseAsync(string content, string contentType) { return Task.FromResult(JsonConvert.DeserializeObject(content)); } diff --git a/src/activities/Elsa.Activities.Http/Formatters/NullContentFormatter.cs b/src/activities/Elsa.Activities.Http/Formatters/NullContentFormatter.cs index 288186bf0..a9c78b6c3 100644 --- a/src/activities/Elsa.Activities.Http/Formatters/NullContentFormatter.cs +++ b/src/activities/Elsa.Activities.Http/Formatters/NullContentFormatter.cs @@ -9,7 +9,7 @@ namespace Elsa.Activities.Http.Formatters public int Priority => -1; public IEnumerable SupportedContentTypes => new[] { "", default }; - public Task FormatAsync(string content, string contentType) + public Task ParseAsync(string content, string contentType) { return Task.FromResult(content); } diff --git a/src/activities/Elsa.Activities.Http/Middleware/RequestHandlerMiddleware.cs b/src/activities/Elsa.Activities.Http/Middleware/RequestHandlerMiddleware.cs index 097cd9ad0..396f31595 100644 --- a/src/activities/Elsa.Activities.Http/Middleware/RequestHandlerMiddleware.cs +++ b/src/activities/Elsa.Activities.Http/Middleware/RequestHandlerMiddleware.cs @@ -1,8 +1,5 @@ using System.Threading.Tasks; -using Elsa.Activities.Http.RequestHandlers; using Elsa.Activities.Http.Services; -using Elsa.Persistence; -using Elsa.Services; using Microsoft.AspNetCore.Http; namespace Elsa.Activities.Http.Middleware diff --git a/src/activities/Elsa.Activities.Http/Models/HttpRequestModel.cs b/src/activities/Elsa.Activities.Http/Models/HttpRequestModel.cs index 273770a7a..522c2a3ac 100644 --- a/src/activities/Elsa.Activities.Http/Models/HttpRequestModel.cs +++ b/src/activities/Elsa.Activities.Http/Models/HttpRequestModel.cs @@ -11,7 +11,7 @@ namespace Elsa.Activities.Http.Models public IDictionary QueryString { get; set; } public IDictionary Headers { get; set; } public string Content { get; set; } - public object FormattedContent { get; set; } + public object ParsedContent { get; set; } public IDictionary Form { get; set; } } } \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Http/Services/IContentFormatter.cs b/src/activities/Elsa.Activities.Http/Services/IContentFormatter.cs index 2649c2643..cf27bdaf3 100644 --- a/src/activities/Elsa.Activities.Http/Services/IContentFormatter.cs +++ b/src/activities/Elsa.Activities.Http/Services/IContentFormatter.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Threading.Tasks; @@ -8,6 +7,6 @@ namespace Elsa.Activities.Http.Services { int Priority { get; } IEnumerable SupportedContentTypes { get; } - Task FormatAsync(string content, string contentType); + Task ParseAsync(string content, string contentType); } } \ No newline at end of file diff --git a/src/activities/Elsa.Activities.MassTransit/Activities/ReceiveMassTransitMessage.cs b/src/activities/Elsa.Activities.MassTransit/Activities/ReceiveMassTransitMessage.cs new file mode 100644 index 000000000..9e59e37ee --- /dev/null +++ b/src/activities/Elsa.Activities.MassTransit/Activities/ReceiveMassTransitMessage.cs @@ -0,0 +1,47 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Core.Services; +using Elsa.Extensions; +using Elsa.Results; +using Elsa.Services.Models; +using Newtonsoft.Json.Linq; + +namespace Elsa.Activities.MassTransit.Activities +{ + public class ReceiveMassTransitMessage : Activity + { + public static Type GetMessageType(JObject state) + { + var typeName = state.GetState(nameof(MessageType)); + return string.IsNullOrWhiteSpace(typeName) ? null : Type.GetType(typeName); + } + + public Type MessageType + { + get => GetMessageType(State); + set => SetState(value.AssemblyQualifiedName); + } + + protected override bool OnCanExecute(WorkflowExecutionContext context) + { + var message = context.Workflow.Input["message"]; + var messageType = MessageType; + + return message != null && messageType != null && message.GetType() == messageType; + } + + protected override ActivityExecutionResult OnExecute(WorkflowExecutionContext context) + { + return Halt(true); + } + + protected override Task OnResumeAsync(WorkflowExecutionContext context, CancellationToken cancellationToken) + { + var message = context.Workflow.Input["message"]; + context.SetLastResult(message); + + return Task.FromResult(Done()); + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.MassTransit/Activities/SendMassTransitMessage.cs b/src/activities/Elsa.Activities.MassTransit/Activities/SendMassTransitMessage.cs new file mode 100644 index 000000000..046aeed9b --- /dev/null +++ b/src/activities/Elsa.Activities.MassTransit/Activities/SendMassTransitMessage.cs @@ -0,0 +1,53 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Core.Expressions; +using Elsa.Core.Services; +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Models; +using MassTransit; + +namespace Elsa.Activities.MassTransit.Activities +{ + public class SendMassTransitMessage : Activity + { + private readonly ISendEndpointProvider sender; + private readonly IWorkflowExpressionEvaluator evaluator; + + public SendMassTransitMessage(ISendEndpointProvider sender, IWorkflowExpressionEvaluator evaluator) + { + this.sender = sender; + this.evaluator = evaluator; + } + + public Type MessageType + { + get + { + var typeName = GetState(); + return string.IsNullOrWhiteSpace(typeName) ? null : Type.GetType(typeName); + } + set => SetState(value.AssemblyQualifiedName); + } + + public WorkflowExpression Message + { + get => GetState(() => new WorkflowExpression(JavaScriptEvaluator.SyntaxName, string.Empty)); + set => SetState(value); + } + + protected override bool OnCanExecute(WorkflowExecutionContext context) + { + return MessageType != null; + } + + protected override async Task OnExecuteAsync(WorkflowExecutionContext context, CancellationToken cancellationToken) + { + var message = await evaluator.EvaluateAsync(Message, MessageType, context, cancellationToken); + await sender.Send(message, cancellationToken); + + return Done(); + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.MassTransit/Consumers/WorkflowConsumer.cs b/src/activities/Elsa.Activities.MassTransit/Consumers/WorkflowConsumer.cs new file mode 100644 index 000000000..799a455db --- /dev/null +++ b/src/activities/Elsa.Activities.MassTransit/Consumers/WorkflowConsumer.cs @@ -0,0 +1,31 @@ +using System.Threading.Tasks; +using Elsa.Activities.MassTransit.Activities; +using Elsa.Models; +using Elsa.Services; +using MassTransit; + +namespace Elsa.Activities.MassTransit.Consumers +{ + public class WorkflowConsumer : IConsumer where T : class + { + private readonly IWorkflowInvoker workflowInvoker; + + public WorkflowConsumer(IWorkflowInvoker workflowInvoker) + { + this.workflowInvoker = workflowInvoker; + } + + public async Task Consume(ConsumeContext context) + { + var message = context.Message; + var activityType = nameof(ReceiveMassTransitMessage); + var input = new Variables { ["message"] = message }; + + await workflowInvoker.TriggerAsync( + activityType, + input, + x => ReceiveMassTransitMessage.GetMessageType(x) == message.GetType(), + context.CancellationToken); + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.MassTransit/Elsa.Activities.MassTransit.csproj b/src/activities/Elsa.Activities.MassTransit/Elsa.Activities.MassTransit.csproj new file mode 100644 index 000000000..1fe85e3ae --- /dev/null +++ b/src/activities/Elsa.Activities.MassTransit/Elsa.Activities.MassTransit.csproj @@ -0,0 +1,17 @@ + + + + netstandard2.0 + + + + + + + + + + + + + diff --git a/src/activities/Elsa.Activities.MassTransit/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.MassTransit/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..c1088218d --- /dev/null +++ b/src/activities/Elsa.Activities.MassTransit/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using Elsa.Activities.MassTransit.Activities; +using Elsa.Activities.MassTransit.Consumers; +using Elsa.Activities.MassTransit.Options; +using Elsa.Core.Extensions; +using MassTransit; +using MassTransit.AspNetCoreIntegration; +using MassTransit.AspNetCoreIntegration.HealthChecks; +using MassTransit.ExtensionsDependencyInjectionIntegration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Options; + +namespace Elsa.Activities.MassTransit.Extensions +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddRabbitMqActivities(this IServiceCollection services, Action> options = null, params Type[] messageTypes) + { + var optionsBuilder = services.AddOptions(); + options?.Invoke(optionsBuilder); + + services + .AddActivity() + .AddActivity(); + + services.AddSingleton(); + services.AddSingleton(); + + services.AddMassTransit( + massTransit => + { + massTransit.AddBus(sp => CreateUsingRabbitMq(massTransit, sp, messageTypes)); + }); + + services.AddSingleton(); + + foreach (var messageType in messageTypes) + { + var consumerType = CreateConsumerType(messageType); + services.AddSingleton(consumerType); + } + + return services; + } + + private static IBusControl CreateUsingRabbitMq(IServiceCollectionConfigurator massTransit, IServiceProvider sp, IEnumerable messageTypes) + { + return Bus.Factory.CreateUsingRabbitMq( + bus => + { + var options = sp.GetRequiredService>(); + var host = bus.Host(new Uri(options.Value.Host), _ => { }); + + foreach (var messageType in messageTypes) + { + var queueName = messageType.Name; + var consumerType = CreateConsumerType(messageType); + + bus.ReceiveEndpoint( + host, + queueName, + endpoint => + { + endpoint.PrefetchCount = 16; + endpoint.Consumer(consumerType, sp.GetRequiredService); + MapEndpointConvention(messageType, endpoint.InputAddress); + }); + } + }); + } + + private static void MapEndpointConvention(Type messageType, Uri destinationAddress) + { + var method = typeof(EndpointConvention).GetMethod("Map", new[]{ typeof(Uri) }); + var generic = method.MakeGenericMethod(messageType); + generic.Invoke(null, new object[]{destinationAddress}); + } + + private static Type CreateConsumerType(Type messageType) + { + return typeof(WorkflowConsumer<>).MakeGenericType(messageType); + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.MassTransit/Options/RabbitMqOptions.cs b/src/activities/Elsa.Activities.MassTransit/Options/RabbitMqOptions.cs new file mode 100644 index 000000000..c80c5519b --- /dev/null +++ b/src/activities/Elsa.Activities.MassTransit/Options/RabbitMqOptions.cs @@ -0,0 +1,7 @@ +namespace Elsa.Activities.MassTransit.Options +{ + public class RabbitMqOptions + { + public string Host { get; set; } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Cron/Activities/CronTrigger.cs b/src/activities/Elsa.Activities.Timers/Activities/CronEvent.cs similarity index 81% rename from src/activities/Elsa.Activities.Cron/Activities/CronTrigger.cs rename to src/activities/Elsa.Activities.Timers/Activities/CronEvent.cs index e95055838..9f36314b6 100644 --- a/src/activities/Elsa.Activities.Cron/Activities/CronTrigger.cs +++ b/src/activities/Elsa.Activities.Timers/Activities/CronEvent.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using Elsa.Core.Expressions; +using Elsa.Core.Extensions; using Elsa.Core.Services; using Elsa.Results; using Elsa.Services; @@ -8,14 +9,14 @@ using Elsa.Services.Models; using NCrontab; using NodaTime; -namespace Elsa.Activities.Cron.Activities +namespace Elsa.Activities.Timers.Activities { - public class CronTrigger : Activity + public class CronEvent : Activity { private readonly IWorkflowExpressionEvaluator expressionEvaluator; private readonly IClock clock; - public CronTrigger(IWorkflowExpressionEvaluator expressionEvaluator, IClock clock) + public CronEvent(IWorkflowExpressionEvaluator expressionEvaluator, IClock clock) { this.expressionEvaluator = expressionEvaluator; this.clock = clock; @@ -27,7 +28,7 @@ namespace Elsa.Activities.Cron.Activities set => SetState(value); } - public Instant? StartTimestamp + public Instant? StartTime { get => GetState(); set => SetState(value); @@ -35,9 +36,9 @@ namespace Elsa.Activities.Cron.Activities protected override ActivityExecutionResult OnExecute(WorkflowExecutionContext workflowContext) { - if (StartTimestamp == null) + if (StartTime == null) { - StartTimestamp = clock.GetCurrentInstant(); + StartTime = clock.GetCurrentInstant(); } return Halt(); @@ -55,8 +56,8 @@ namespace Elsa.Activities.Cron.Activities var cronExpression = await expressionEvaluator.EvaluateAsync(CronExpression, workflowContext, cancellationToken); var schedule = CrontabSchedule.Parse(cronExpression); var now = clock.GetCurrentInstant(); - var startTimestamp = StartTimestamp ?? now; - var nextOccurrence = schedule.GetNextOccurrence(startTimestamp.ToDateTimeUtc()); + var startTime = StartTime ?? now; + var nextOccurrence = schedule.GetNextOccurrence(startTime.ToDateTimeUtc()); return now.ToDateTimeUtc() >= nextOccurrence; } diff --git a/src/activities/Elsa.Activities.Timers/Activities/TimerEvent.cs b/src/activities/Elsa.Activities.Timers/Activities/TimerEvent.cs new file mode 100644 index 000000000..c7d44deec --- /dev/null +++ b/src/activities/Elsa.Activities.Timers/Activities/TimerEvent.cs @@ -0,0 +1,64 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Core.Expressions; +using Elsa.Core.Extensions; +using Elsa.Core.Services; +using Elsa.Results; +using Elsa.Services; +using Elsa.Services.Models; +using NodaTime; + +namespace Elsa.Activities.Timers.Activities +{ + public class TimerEvent : Activity + { + private readonly IWorkflowExpressionEvaluator expressionEvaluator; + private readonly IClock clock; + + public TimerEvent(IWorkflowExpressionEvaluator expressionEvaluator, IClock clock) + { + this.expressionEvaluator = expressionEvaluator; + this.clock = clock; + } + + public WorkflowExpression TimeoutExpression + { + get => GetState(() => new PlainTextExpression("00:01:00")); + set => SetState(value); + } + + public Instant? StartTime + { + get => GetState(); + set => SetState(value); + } + + protected override ActivityExecutionResult OnExecute(WorkflowExecutionContext workflowContext) + { + if (StartTime == null) + { + StartTime = clock.GetCurrentInstant(); + } + + return Halt(); + } + + protected override async Task OnResumeAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) + { + var isExpired = await IsExpiredAsync(workflowContext, cancellationToken); + + return isExpired ? Done() : Halt(); + } + + private async Task IsExpiredAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) + { + var timeSpan = await expressionEvaluator.EvaluateAsync(TimeoutExpression, workflowContext, cancellationToken); + var now = clock.GetCurrentInstant(); + var startTime = StartTime ?? now; + var expiresAt = startTime.ToDateTimeUtc() + timeSpan; + + return now.ToDateTimeUtc() >= expiresAt; + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Cron/Elsa.Activities.Cron.csproj b/src/activities/Elsa.Activities.Timers/Elsa.Activities.Timers.csproj similarity index 100% rename from src/activities/Elsa.Activities.Cron/Elsa.Activities.Cron.csproj rename to src/activities/Elsa.Activities.Timers/Elsa.Activities.Timers.csproj diff --git a/src/activities/Elsa.Activities.Timers/Extensions/ServiceCollectionExtensions.cs b/src/activities/Elsa.Activities.Timers/Extensions/ServiceCollectionExtensions.cs new file mode 100644 index 000000000..a020dd246 --- /dev/null +++ b/src/activities/Elsa.Activities.Timers/Extensions/ServiceCollectionExtensions.cs @@ -0,0 +1,25 @@ +using System; +using Elsa.Activities.Timers.Activities; +using Elsa.Activities.Timers.HostedServices; +using Elsa.Activities.Timers.Options; +using Elsa.Core.Extensions; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Elsa.Activities.Timers.Extensions +{ + public static class ServiceCollectionExtensions + { + public static IServiceCollection AddTimerActivities(this IServiceCollection services, Action> options = null) + { + var optionsBuilder = services.AddOptions(); + options?.Invoke(optionsBuilder); + + return services + .AddOptions() + .AddHostedService() + .AddActivity() + .AddActivity(); + } + } +} \ No newline at end of file diff --git a/src/activities/Elsa.Activities.Cron/HostedServices/CronHostedService.cs b/src/activities/Elsa.Activities.Timers/HostedServices/TimersHostedService.cs similarity index 61% rename from src/activities/Elsa.Activities.Cron/HostedServices/CronHostedService.cs rename to src/activities/Elsa.Activities.Timers/HostedServices/TimersHostedService.cs index ba52f33d4..52bf5f6cb 100644 --- a/src/activities/Elsa.Activities.Cron/HostedServices/CronHostedService.cs +++ b/src/activities/Elsa.Activities.Timers/HostedServices/TimersHostedService.cs @@ -1,23 +1,24 @@ using System; using System.Threading; using System.Threading.Tasks; -using Elsa.Activities.Cron.Activities; -using Elsa.Activities.Cron.Options; +using Elsa.Activities.Timers.Activities; +using Elsa.Activities.Timers.Options; using Elsa.Models; using Elsa.Services; +using Elsa.Services.Extensions; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace Elsa.Activities.Cron.HostedServices +namespace Elsa.Activities.Timers.HostedServices { - public class CronHostedService : BackgroundService + public class TimersHostedService : BackgroundService { private readonly IWorkflowInvoker workflowInvoker; - private readonly IOptions options; - private readonly ILogger logger; + private readonly IOptions options; + private readonly ILogger logger; - public CronHostedService(IWorkflowInvoker workflowInvoker, IOptions options, ILogger logger) + public TimersHostedService(IWorkflowInvoker workflowInvoker, IOptions options, ILogger logger) { this.workflowInvoker = workflowInvoker; this.options = options; @@ -30,7 +31,8 @@ namespace Elsa.Activities.Cron.HostedServices { try { - await workflowInvoker.TriggerAsync(nameof(CronTrigger), Variables.Empty, stoppingToken); + await workflowInvoker.TriggerAsync(nameof(TimerEvent), Variables.Empty, stoppingToken); + await workflowInvoker.TriggerAsync(nameof(CronEvent), Variables.Empty, stoppingToken); } catch (Exception ex) { diff --git a/src/activities/Elsa.Activities.Cron/Options/CronOptions.cs b/src/activities/Elsa.Activities.Timers/Options/TimersOptions.cs similarity index 61% rename from src/activities/Elsa.Activities.Cron/Options/CronOptions.cs rename to src/activities/Elsa.Activities.Timers/Options/TimersOptions.cs index 04094bdc8..c646f1974 100644 --- a/src/activities/Elsa.Activities.Cron/Options/CronOptions.cs +++ b/src/activities/Elsa.Activities.Timers/Options/TimersOptions.cs @@ -1,10 +1,10 @@ using NodaTime; -namespace Elsa.Activities.Cron.Options +namespace Elsa.Activities.Timers.Options { - public class CronOptions + public class TimersOptions { - public CronOptions() + public TimersOptions() { SweepInterval = Period.FromMinutes(1); } diff --git a/src/core/Elsa.Abstractions/Expressions/IWorkflowExpression.cs b/src/core/Elsa.Abstractions/Expressions/IWorkflowExpression.cs index 5b76029a5..3d2eb6a1f 100644 --- a/src/core/Elsa.Abstractions/Expressions/IWorkflowExpression.cs +++ b/src/core/Elsa.Abstractions/Expressions/IWorkflowExpression.cs @@ -1,8 +1,12 @@ namespace Elsa.Expressions { - public interface IWorkflowExpression + public interface IWorkflowExpression { string Syntax { get; } string Expression { get; } } + + public interface IWorkflowExpression : IWorkflowExpression + { + } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Persistence/IWorkflowDefinitionStore.cs b/src/core/Elsa.Abstractions/Persistence/IWorkflowDefinitionStore.cs index b4e86cfc0..21d1a2c21 100644 --- a/src/core/Elsa.Abstractions/Persistence/IWorkflowDefinitionStore.cs +++ b/src/core/Elsa.Abstractions/Persistence/IWorkflowDefinitionStore.cs @@ -1,7 +1,6 @@ using System.Threading; using System.Threading.Tasks; using Elsa.Models; -using Elsa.Serialization.Models; namespace Elsa.Persistence { diff --git a/src/core/Elsa.Abstractions/Persistence/IWorkflowInstanceStore.cs b/src/core/Elsa.Abstractions/Persistence/IWorkflowInstanceStore.cs index e2998e60e..2eb5af134 100644 --- a/src/core/Elsa.Abstractions/Persistence/IWorkflowInstanceStore.cs +++ b/src/core/Elsa.Abstractions/Persistence/IWorkflowInstanceStore.cs @@ -1,4 +1,3 @@ -using System; using System.Collections.Generic; using System.Linq; using System.Threading; diff --git a/src/core/Elsa.Abstractions/Serialization/Models/WorkflowInstance.cs b/src/core/Elsa.Abstractions/Serialization/Models/WorkflowInstance.cs index 2828eeb46..2405ae590 100644 --- a/src/core/Elsa.Abstractions/Serialization/Models/WorkflowInstance.cs +++ b/src/core/Elsa.Abstractions/Serialization/Models/WorkflowInstance.cs @@ -1,6 +1,5 @@ using System.Collections.Generic; using Elsa.Models; -using Newtonsoft.Json.Linq; using NodaTime; namespace Elsa.Serialization.Models diff --git a/src/core/Elsa.Abstractions/Services/Extensions/WorkflowBlueprintExtensions.cs b/src/core/Elsa.Abstractions/Services/Extensions/WorkflowBlueprintExtensions.cs index fe6635167..defc3f3e7 100644 --- a/src/core/Elsa.Abstractions/Services/Extensions/WorkflowBlueprintExtensions.cs +++ b/src/core/Elsa.Abstractions/Services/Extensions/WorkflowBlueprintExtensions.cs @@ -1,8 +1,6 @@ using System.Collections.Generic; using System.Linq; using Elsa.Models; -using Elsa.Services.Models; -using NodaTime; namespace Elsa.Services.Extensions { diff --git a/src/core/Elsa.Abstractions/Services/Extensions/WorkflowInvokerExtensions.cs b/src/core/Elsa.Abstractions/Services/Extensions/WorkflowInvokerExtensions.cs new file mode 100644 index 000000000..ba2b757a3 --- /dev/null +++ b/src/core/Elsa.Abstractions/Services/Extensions/WorkflowInvokerExtensions.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; +using Elsa.Models; + +namespace Elsa.Services.Extensions +{ + public static class WorkflowInvokerExtensions + { + /// + /// Starts new workflows that start with the specified activity name and resumes halted workflows that are blocked on activities with the specified activity name. + /// + public static Task TriggerAsync( + this IWorkflowInvoker workflowInvoker, + string activityType, + Variables input, + CancellationToken cancellationToken = default) + { + return workflowInvoker.TriggerAsync(activityType, input, cancellationToken: cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Services/IActivityResolver.cs b/src/core/Elsa.Abstractions/Services/IActivityResolver.cs index 90e451a41..7126c4d6d 100644 --- a/src/core/Elsa.Abstractions/Services/IActivityResolver.cs +++ b/src/core/Elsa.Abstractions/Services/IActivityResolver.cs @@ -5,7 +5,7 @@ namespace Elsa.Services { public interface IActivityResolver { - IActivity ResolveActivity(string activityTypeName, Action setup = null); - T ResolveActivity(Action configure = null) where T : class, IActivity; + IActivity ResolveActivity(string activityTypeName, Action setup = default); + T ResolveActivity(Action configure = default) where T : class, IActivity; } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Services/IConnectionBuilder.cs b/src/core/Elsa.Abstractions/Services/IConnectionBuilder.cs index 0d1c0ea0c..705b3c7a7 100644 --- a/src/core/Elsa.Abstractions/Services/IConnectionBuilder.cs +++ b/src/core/Elsa.Abstractions/Services/IConnectionBuilder.cs @@ -1,6 +1,5 @@ using System; using Elsa.Models; -using Elsa.Services.Models; namespace Elsa.Services { diff --git a/src/core/Elsa.Abstractions/Services/IExpressionEvaluator.cs b/src/core/Elsa.Abstractions/Services/IExpressionEvaluator.cs index 33dfbad55..032d900da 100644 --- a/src/core/Elsa.Abstractions/Services/IExpressionEvaluator.cs +++ b/src/core/Elsa.Abstractions/Services/IExpressionEvaluator.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using System.Threading.Tasks; using Elsa.Services.Models; @@ -7,6 +8,6 @@ namespace Elsa.Services public interface IExpressionEvaluator { string Syntax { get; } - Task EvaluateAsync(string expression, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken); + Task EvaluateAsync(string expression, Type type, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Services/IOutcomeBuilder.cs b/src/core/Elsa.Abstractions/Services/IOutcomeBuilder.cs index 22922feb3..36f975874 100644 --- a/src/core/Elsa.Abstractions/Services/IOutcomeBuilder.cs +++ b/src/core/Elsa.Abstractions/Services/IOutcomeBuilder.cs @@ -8,7 +8,7 @@ namespace Elsa.Services { IActivityBuilder Source { get; } string Outcome { get; } - IActivityBuilder Then(Action setup, string id = null) where T : class, IActivity; + IActivityBuilder Then(Action setup = default, string id = default) where T : class, IActivity; WorkflowDefinition Build(); IConnectionBuilder Then(string activityId); } diff --git a/src/core/Elsa.Abstractions/Services/IWorkflowBuilder.cs b/src/core/Elsa.Abstractions/Services/IWorkflowBuilder.cs index 7b94269a7..04b764b35 100644 --- a/src/core/Elsa.Abstractions/Services/IWorkflowBuilder.cs +++ b/src/core/Elsa.Abstractions/Services/IWorkflowBuilder.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using Elsa.Models; -using Elsa.Serialization.Models; using Elsa.Services.Models; namespace Elsa.Services @@ -12,10 +11,10 @@ namespace Elsa.Services IReadOnlyList Activities { get; } IWorkflowBuilder WithId(string id); IWorkflowBuilder AsSingleton(bool value = true); - IActivityBuilder Add(Action setupActivity, string id = null) where T : class, IActivity; - IActivityBuilder StartWith(Action setup = null, string id = null) where T: class, IActivity; - IConnectionBuilder Connect(IActivityBuilder source, IActivityBuilder target, string outcome = null); - IConnectionBuilder Connect(Func source, Func target, string outcome = null); + IActivityBuilder Add(Action setupActivity = default, string id = default) where T : class, IActivity; + IActivityBuilder StartWith(Action setup = default, string id = default) where T: class, IActivity; + IConnectionBuilder Connect(IActivityBuilder source, IActivityBuilder target, string outcome = default); + IConnectionBuilder Connect(Func source, Func target, string outcome = default); WorkflowDefinition Build(); } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Services/IWorkflowExpressionEvaluator.cs b/src/core/Elsa.Abstractions/Services/IWorkflowExpressionEvaluator.cs index cdce55549..0dc838366 100644 --- a/src/core/Elsa.Abstractions/Services/IWorkflowExpressionEvaluator.cs +++ b/src/core/Elsa.Abstractions/Services/IWorkflowExpressionEvaluator.cs @@ -1,3 +1,4 @@ +using System; using System.Threading; using System.Threading.Tasks; using Elsa.Expressions; @@ -7,6 +8,6 @@ namespace Elsa.Services { public interface IWorkflowExpressionEvaluator { - Task EvaluateAsync(IWorkflowExpression expression, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken); + Task EvaluateAsync(IWorkflowExpression expression, Type type, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken); } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Services/IWorkflowInvoker.cs b/src/core/Elsa.Abstractions/Services/IWorkflowInvoker.cs index 0969978b4..9bcaccad3 100644 --- a/src/core/Elsa.Abstractions/Services/IWorkflowInvoker.cs +++ b/src/core/Elsa.Abstractions/Services/IWorkflowInvoker.cs @@ -1,9 +1,11 @@ -using System.Collections.Generic; +using System; +using System.Collections.Generic; using System.Threading; using System.Threading.Tasks; using Elsa.Models; using Elsa.Serialization.Models; using Elsa.Services.Models; +using Newtonsoft.Json.Linq; namespace Elsa.Services { @@ -40,6 +42,10 @@ namespace Elsa.Services /// /// Starts new workflows that start with the specified activity name and resumes halted workflows that are blocked on activities with the specified activity name. /// - Task TriggerAsync(string activityType, Variables input, CancellationToken cancellationToken = default); + Task TriggerAsync( + string activityType, + Variables input, + Func activityStatePredicate = default, + CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Services/IWorkflowRegistry.cs b/src/core/Elsa.Abstractions/Services/IWorkflowRegistry.cs index 165f18c2d..3f0f3b135 100644 --- a/src/core/Elsa.Abstractions/Services/IWorkflowRegistry.cs +++ b/src/core/Elsa.Abstractions/Services/IWorkflowRegistry.cs @@ -1,5 +1,4 @@ using System.Collections.Generic; -using System.Threading; using Elsa.Models; using Elsa.Services.Models; diff --git a/src/core/Elsa.Abstractions/Services/Models/Workflow.cs b/src/core/Elsa.Abstractions/Services/Models/Workflow.cs index aa3fa2143..b06e921f6 100644 --- a/src/core/Elsa.Abstractions/Services/Models/Workflow.cs +++ b/src/core/Elsa.Abstractions/Services/Models/Workflow.cs @@ -1,5 +1,4 @@ -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; using Elsa.Models; using Elsa.Serialization.Models; diff --git a/src/core/Elsa.Core/Activities/Primitives/IfElse.cs b/src/core/Elsa.Core/Activities/Primitives/IfElse.cs index a55a88d7c..182187f69 100644 --- a/src/core/Elsa.Core/Activities/Primitives/IfElse.cs +++ b/src/core/Elsa.Core/Activities/Primitives/IfElse.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using Elsa.Core.Expressions; +using Elsa.Core.Extensions; using Elsa.Core.Services; using Elsa.Results; using Elsa.Services; diff --git a/src/core/Elsa.Core/Activities/Primitives/SetVariable.cs b/src/core/Elsa.Core/Activities/Primitives/SetVariable.cs index 411f770a7..e3b8497b6 100644 --- a/src/core/Elsa.Core/Activities/Primitives/SetVariable.cs +++ b/src/core/Elsa.Core/Activities/Primitives/SetVariable.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using Elsa.Core.Expressions; +using Elsa.Core.Extensions; using Elsa.Core.Services; using Elsa.Results; using Elsa.Services; diff --git a/src/core/Elsa.Core/Activities/Primitives/Switch.cs b/src/core/Elsa.Core/Activities/Primitives/Switch.cs index 9b93d3c76..69ce5042b 100644 --- a/src/core/Elsa.Core/Activities/Primitives/Switch.cs +++ b/src/core/Elsa.Core/Activities/Primitives/Switch.cs @@ -2,6 +2,7 @@ using System.Threading; using System.Threading.Tasks; using Elsa.Core.Expressions; +using Elsa.Core.Extensions; using Elsa.Core.Services; using Elsa.Results; using Elsa.Services; diff --git a/src/core/Elsa.Core/Expressions/JavaScriptEvaluator.cs b/src/core/Elsa.Core/Expressions/JavaScriptEvaluator.cs index 9827aac8e..d0f73b008 100644 --- a/src/core/Elsa.Core/Expressions/JavaScriptEvaluator.cs +++ b/src/core/Elsa.Core/Expressions/JavaScriptEvaluator.cs @@ -1,17 +1,13 @@ using System; using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; using System.Threading; using System.Threading.Tasks; -using Elsa.Expressions; using Elsa.Scripting; using Elsa.Services; using Elsa.Services.Models; -using Esprima.Ast; using Jint; using Jint.Native; -using Microsoft.Extensions.DependencyInjection; +using Newtonsoft.Json; namespace Elsa.Core.Expressions { @@ -32,14 +28,14 @@ namespace Elsa.Core.Expressions public string Syntax => SyntaxName; - public Task EvaluateAsync(string expression, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) + 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()); + var result = ConvertValue(engine.GetCompletionValue(), type); return Task.FromResult(result); } @@ -52,11 +48,6 @@ namespace Elsa.Core.Expressions } } - private T ConvertValue(JsValue value) - { - return (T)ConvertValue(value, typeof(T)); - } - private object ConvertValue(JsValue value, Type targetType) { if (value.IsNull()) @@ -75,7 +66,11 @@ namespace Elsa.Core.Expressions return value.AsString(); if (value.IsObject()) - return value.AsObject().ToObject(); + { + var obj = value.AsObject().ToObject(); + var json = JsonConvert.SerializeObject(obj); + return JsonConvert.DeserializeObject(json, targetType); + } if (value.IsArray()) { diff --git a/src/core/Elsa.Core/Expressions/PlainTextEvaluator.cs b/src/core/Elsa.Core/Expressions/PlainTextEvaluator.cs index ef07c7be4..6b6680210 100644 --- a/src/core/Elsa.Core/Expressions/PlainTextEvaluator.cs +++ b/src/core/Elsa.Core/Expressions/PlainTextEvaluator.cs @@ -1,7 +1,7 @@ using System; using System.Threading; using System.Threading.Tasks; -using Elsa.Expressions; +using Elsa.Core.Extensions; using Elsa.Services; using Elsa.Services.Models; @@ -17,12 +17,12 @@ namespace Elsa.Core.Expressions public const string SyntaxName = "PlainText"; public string Syntax => SyntaxName; - public Task EvaluateAsync(string expression, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) + public Task EvaluateAsync(string expression, Type type, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) { if (string.IsNullOrWhiteSpace(expression)) - return Task.FromResult(default(T)); + return Task.FromResult(default(object)); - return Task.FromResult((T) Convert.ChangeType(expression, typeof(T))); + return Task.FromResult(expression.Parse(type)); } } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Expressions/PlainTextExpression.cs b/src/core/Elsa.Core/Expressions/PlainTextExpression.cs index 6e831b09f..a2e5f48a1 100644 --- a/src/core/Elsa.Core/Expressions/PlainTextExpression.cs +++ b/src/core/Elsa.Core/Expressions/PlainTextExpression.cs @@ -1,9 +1,16 @@ namespace Elsa.Core.Expressions { - public class PlainTextExpression : WorkflowExpression + public class PlainTextExpression : WorkflowExpression { public PlainTextExpression(string expression) : base(PlainTextEvaluator.SyntaxName, expression) { } } + + public class PlainTextExpression : PlainTextExpression + { + public PlainTextExpression(string expression) : base(expression) + { + } + } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Expressions/WorkflowExpression.cs b/src/core/Elsa.Core/Expressions/WorkflowExpression.cs index c6fe0a27e..bacac6544 100644 --- a/src/core/Elsa.Core/Expressions/WorkflowExpression.cs +++ b/src/core/Elsa.Core/Expressions/WorkflowExpression.cs @@ -2,9 +2,9 @@ namespace Elsa.Core.Expressions { - public class WorkflowExpression + public class WorkflowExpression : IWorkflowExpression { - protected WorkflowExpression(string syntax, string expression) + public WorkflowExpression(string syntax, string expression) { Syntax = syntax; Expression = expression; diff --git a/src/core/Elsa.Core/Expressions/WorkflowExpressionEvaluator.cs b/src/core/Elsa.Core/Expressions/WorkflowExpressionEvaluator.cs index d9270ecc6..39689f969 100644 --- a/src/core/Elsa.Core/Expressions/WorkflowExpressionEvaluator.cs +++ b/src/core/Elsa.Core/Expressions/WorkflowExpressionEvaluator.cs @@ -1,3 +1,4 @@ +using System; using System.Collections.Generic; using System.Linq; using System.Threading; @@ -16,14 +17,14 @@ namespace Elsa.Core.Expressions { this.evaluators = evaluators.ToDictionary(x => x.Syntax); } - - public async Task EvaluateAsync(IWorkflowExpression expression, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) + + public async Task EvaluateAsync(IWorkflowExpression expression, Type type, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) { if (expression == null) return default; var evaluator = evaluators[expression.Syntax]; - return await evaluator.EvaluateAsync(expression.Expression, workflowExecutionContext, cancellationToken); + return await evaluator.EvaluateAsync(expression.Expression, type, workflowExecutionContext, cancellationToken); } } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Extensions/ExpressionEvaluatorExtensions.cs b/src/core/Elsa.Core/Extensions/ExpressionEvaluatorExtensions.cs new file mode 100644 index 000000000..b77580d0a --- /dev/null +++ b/src/core/Elsa.Core/Extensions/ExpressionEvaluatorExtensions.cs @@ -0,0 +1,15 @@ +using System.Threading; +using System.Threading.Tasks; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Core.Extensions +{ + public static class ExpressionEvaluatorExtensions + { + public static async Task EvaluateAsync(this IExpressionEvaluator evaluator, string expression, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) + { + return (T)await evaluator.EvaluateAsync(expression, typeof(T), workflowExecutionContext, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Extensions/ServiceCollectionExtensions.cs b/src/core/Elsa.Core/Extensions/ServiceCollectionExtensions.cs index 066137964..5c2038ce0 100644 --- a/src/core/Elsa.Core/Extensions/ServiceCollectionExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ServiceCollectionExtensions.cs @@ -1,10 +1,11 @@ +using System; using Elsa.Core.Activities.Primitives; using Elsa.Core.Expressions; +using Elsa.Core.Scripting; using Elsa.Core.Serialization; using Elsa.Core.Serialization.Formatters; using Elsa.Core.Services; using Elsa.Core.Services.WorkflowBuilders; -using Elsa.Expressions; using Elsa.Scripting; using Elsa.Serialization; using Elsa.Serialization.Formatters; @@ -40,6 +41,7 @@ namespace Elsa.Core.Extensions .AddSingleton() .AddSingleton() .AddTransient() + .AddSingleton>(sp => sp.GetRequiredService) .AddSingleton() .AddPrimitiveActivities(); } diff --git a/src/core/Elsa.Core/Extensions/StringExtensions.cs b/src/core/Elsa.Core/Extensions/StringExtensions.cs new file mode 100644 index 000000000..1c076627a --- /dev/null +++ b/src/core/Elsa.Core/Extensions/StringExtensions.cs @@ -0,0 +1,23 @@ +using System; +using System.ComponentModel; +using System.Globalization; + +namespace Elsa.Core.Extensions +{ + public static class StringExtensions + { + public static T Parse(this string value) + { + return (T)value.Parse(typeof(T)); + } + + public static object Parse(this string value, Type targetType) + { + if (typeof(string) == targetType) + return value; + + var converter = TypeDescriptor.GetConverter(targetType); + return converter.ConvertFromString(default, CultureInfo.InvariantCulture, value); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Extensions/WorkflowExpressionEvaluatorExtensions.cs b/src/core/Elsa.Core/Extensions/WorkflowExpressionEvaluatorExtensions.cs new file mode 100644 index 000000000..def9dae30 --- /dev/null +++ b/src/core/Elsa.Core/Extensions/WorkflowExpressionEvaluatorExtensions.cs @@ -0,0 +1,16 @@ +using System.Threading; +using System.Threading.Tasks; +using Elsa.Expressions; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Core.Extensions +{ + public static class WorkflowExpressionEvaluatorExtensions + { + public static async Task EvaluateAsync(this IWorkflowExpressionEvaluator evaluator, IWorkflowExpression expression, WorkflowExecutionContext workflowExecutionContext, CancellationToken cancellationToken) + { + return (T)await evaluator.EvaluateAsync(expression, typeof(T), workflowExecutionContext, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Persistence/Memory/MemoryWorkflowDefinitionStore.cs b/src/core/Elsa.Core/Persistence/Memory/MemoryWorkflowDefinitionStore.cs index 3e38a5881..9f0b79c5e 100644 --- a/src/core/Elsa.Core/Persistence/Memory/MemoryWorkflowDefinitionStore.cs +++ b/src/core/Elsa.Core/Persistence/Memory/MemoryWorkflowDefinitionStore.cs @@ -3,7 +3,6 @@ using System.Threading; using System.Threading.Tasks; using Elsa.Models; using Elsa.Persistence; -using Elsa.Serialization.Models; namespace Elsa.Core.Persistence.Memory { diff --git a/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs b/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs index e63e54222..b50d623b1 100644 --- a/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs +++ b/src/core/Elsa.Core/Scripting/CommonScriptEngineConfigurator.cs @@ -1,11 +1,9 @@ using System; -using System.Collections.Generic; -using Elsa.Expressions; using Elsa.Scripting; using Elsa.Services.Models; using Jint; -namespace Elsa.Core.Expressions +namespace Elsa.Core.Scripting { public class CommonScriptEngineConfigurator : IScriptEngineConfigurator { diff --git a/src/core/Elsa.Core/Services/WorkflowBuilders/ConnectionBuilder.cs b/src/core/Elsa.Core/Services/WorkflowBuilders/ConnectionBuilder.cs index c3b056c37..4d53b592a 100644 --- a/src/core/Elsa.Core/Services/WorkflowBuilders/ConnectionBuilder.cs +++ b/src/core/Elsa.Core/Services/WorkflowBuilders/ConnectionBuilder.cs @@ -1,7 +1,6 @@ using System; using Elsa.Models; using Elsa.Services; -using Elsa.Services.Models; namespace Elsa.Core.Services.WorkflowBuilders { diff --git a/src/core/Elsa.Core/Services/WorkflowBuilders/OutcomeBuilder.cs b/src/core/Elsa.Core/Services/WorkflowBuilders/OutcomeBuilder.cs index c4d63677a..3cef4a5c1 100644 --- a/src/core/Elsa.Core/Services/WorkflowBuilders/OutcomeBuilder.cs +++ b/src/core/Elsa.Core/Services/WorkflowBuilders/OutcomeBuilder.cs @@ -19,7 +19,7 @@ namespace Elsa.Core.Services.WorkflowBuilders public IActivityBuilder Source { get; } public string Outcome { get; } - public IActivityBuilder Then(Action setup, string id = null) where T : class, IActivity + public IActivityBuilder Then(Action setup = default, string id = default) where T : class, IActivity { var target = WorkflowBuilder.Add(setup, id); diff --git a/src/core/Elsa.Core/Services/WorkflowBuilders/WorkflowBuilder.cs b/src/core/Elsa.Core/Services/WorkflowBuilders/WorkflowBuilder.cs index 5a448c850..ffa346acc 100644 --- a/src/core/Elsa.Core/Services/WorkflowBuilders/WorkflowBuilder.cs +++ b/src/core/Elsa.Core/Services/WorkflowBuilders/WorkflowBuilder.cs @@ -34,7 +34,7 @@ namespace Elsa.Core.Services.WorkflowBuilders return this; } - public IActivityBuilder Add(Action setupActivity, string id = null) where T : class, IActivity + public IActivityBuilder Add(Action setupActivity = default, string id = default) where T : class, IActivity { var activity = activityResolver.ResolveActivity(setupActivity); var activityBlueprint = ActivityDefinition.FromActivity(activity); @@ -47,12 +47,12 @@ namespace Elsa.Core.Services.WorkflowBuilders return activityBuilder; } - public IConnectionBuilder Connect(IActivityBuilder source, IActivityBuilder target, string outcome = null) + public IConnectionBuilder Connect(IActivityBuilder source, IActivityBuilder target, string outcome = default) { return Connect(() => source, () => target, outcome); } - public IConnectionBuilder Connect(Func source, Func target, string outcome = null) + public IConnectionBuilder Connect(Func source, Func target, string outcome = default) { var connectionBuilder = new ConnectionBuilder(this, source, target, outcome); diff --git a/src/core/Elsa.Core/Services/WorkflowInvoker.cs b/src/core/Elsa.Core/Services/WorkflowInvoker.cs index 289ed7262..b794bcf02 100644 --- a/src/core/Elsa.Core/Services/WorkflowInvoker.cs +++ b/src/core/Elsa.Core/Services/WorkflowInvoker.cs @@ -13,6 +13,7 @@ using Elsa.Services; using Elsa.Services.Extensions; using Elsa.Services.Models; using Microsoft.Extensions.Logging; +using Newtonsoft.Json.Linq; using NodaTime; namespace Elsa.Core.Services @@ -89,15 +90,23 @@ namespace Elsa.Core.Services return InvokeAsync(definition, input, workflowInstance, startActivityIds, cancellationToken); } - public async Task TriggerAsync(string activityType, Variables input, CancellationToken cancellationToken) + public async Task TriggerAsync( + string activityType, + Variables input, + Func activityStatePredicate = default, + CancellationToken cancellationToken = default) { - var workflowDefinitions = workflowRegistry.ListByStartActivity(activityType).ToDictionary(x => x.Item1.Id); var workflowInstances = await workflowInstanceStore.ListByBlockingActivityAsync(activityType, cancellationToken).ToListAsync(); - var workflowInstancesByDefinitionId = workflowInstances.ToDictionary(x => x.Item1.DefinitionId); - var workflowsToStart = workflowDefinitions.Values.Where(x => !workflowInstancesByDefinitionId.ContainsKey(x.Item1.Id)); + var workflowDefinitions = workflowRegistry.ListByStartActivity(activityType).ToList(); - await StartWorkflowsAsync(workflowsToStart, input, cancellationToken); - await ResumeWorkflowsAsync(workflowDefinitions, workflowInstances, input, cancellationToken); + if (activityStatePredicate != null) + workflowDefinitions = workflowDefinitions.Where(x => activityStatePredicate(x.Item2.State)).ToList(); + + if (activityStatePredicate != null) + workflowInstances = workflowInstances.Where(x => activityStatePredicate(x.Item2.State)).ToList(); + + await StartWorkflowsAsync(workflowDefinitions, input, cancellationToken); + await ResumeWorkflowsAsync(workflowInstances, input, cancellationToken); } private async Task StartWorkflowsAsync(IEnumerable<(WorkflowDefinition, ActivityDefinition)> workflowDefinitions, Variables variables, CancellationToken cancellationToken1) @@ -109,15 +118,15 @@ namespace Elsa.Core.Services } } - private async Task ResumeWorkflowsAsync(IReadOnlyDictionary workflowDefinitions, IList<(WorkflowInstance, ActivityInstance)> workflowInstances, Variables input, CancellationToken cancellationToken) + private async Task ResumeWorkflowsAsync(IEnumerable<(WorkflowInstance, ActivityInstance)> workflowInstances, Variables input, CancellationToken cancellationToken) { foreach (var (workflowInstance, startActivityInstance) in workflowInstances) { - var workflowDefinition = workflowDefinitions[workflowInstance.DefinitionId]; + var workflowDefinition = workflowRegistry.GetById(workflowInstance.DefinitionId); workflowInstance.Status = WorkflowStatus.Resuming; - await InvokeAsync(workflowDefinition.Item1, input, workflowInstance, new[] { startActivityInstance.Id }, cancellationToken); + await InvokeAsync(workflowDefinition, input, workflowInstance, new[] { startActivityInstance.Id }, cancellationToken); } } diff --git a/src/core/Elsa.Core/Services/WorkflowRegistry.cs b/src/core/Elsa.Core/Services/WorkflowRegistry.cs index 8bbc41c8c..61bd1cddb 100644 --- a/src/core/Elsa.Core/Services/WorkflowRegistry.cs +++ b/src/core/Elsa.Core/Services/WorkflowRegistry.cs @@ -1,6 +1,6 @@ +using System; using System.Collections.Generic; using System.Linq; -using System.Threading; using Elsa.Models; using Elsa.Services; using Elsa.Services.Extensions; @@ -10,12 +10,12 @@ namespace Elsa.Core.Services { public class WorkflowRegistry : IWorkflowRegistry { - private readonly IWorkflowBuilder workflowBuilder; + private readonly Func workflowBuilderFactory; private readonly IDictionary workflowDefinitions; - public WorkflowRegistry(IWorkflowBuilder workflowBuilder) + public WorkflowRegistry(Func workflowBuilderFactory) { - this.workflowBuilder = workflowBuilder; + this.workflowBuilderFactory = workflowBuilderFactory; workflowDefinitions = new Dictionary(); } @@ -26,7 +26,7 @@ namespace Elsa.Core.Services public void RegisterWorkflow() where T : IWorkflow, new() { - var definition = workflowBuilder.Build(); + var definition = workflowBuilderFactory().Build(); RegisterWorkflow(definition); }