Initial work on separating descriptors from drivers

This commit is contained in:
Sipke Schoorstra 2019-01-05 12:50:08 +01:00
parent bddce06a9e
commit 344e95e70e
57 changed files with 514 additions and 269 deletions

View file

@ -0,0 +1,23 @@
using Elsa.Activities.Console.Activities;
using Elsa.Activities.Console.Drivers;
using Elsa.Handlers;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Console.Descriptors
{
public class ReadLineDescriptor : ActivityDescriptorBase<ReadLine>
{
public ReadLineDescriptor(IStringLocalizer<ReadLineDriver> localizer)
{
T = localizer;
}
private IStringLocalizer T { get; }
protected override LocalizedString GetEndpoint() => T["Done"];
public override LocalizedString Category => T["Console"];
public override LocalizedString DisplayText => T["Read Line"];
public override LocalizedString Description => T["Read a line from the console"];
}
}

View file

@ -0,0 +1,26 @@
using Elsa.Activities.Console.Activities;
using Elsa.Activities.Console.Drivers;
using Elsa.Handlers;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Console.Descriptors
{
/// <summary>
/// Writes a text string to the specified stream.
/// </summary>
public class WriteLineDescriptor : ActivityDescriptorBase<WriteLine>
{
public WriteLineDescriptor(IStringLocalizer<WriteLineDriver> localizer)
{
T = localizer;
}
private IStringLocalizer T { get; }
protected override LocalizedString GetEndpoint() => T["Done"];
public override LocalizedString Category => T["Console"];
public override LocalizedString DisplayText => T["Write Line"];
public override LocalizedString Description => T["Write a line to the console"];
}
}

View file

@ -7,30 +7,21 @@ using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Console.Handlers
namespace Elsa.Activities.Console.Drivers
{
public class ReadLineHandler : ActivityHandler<ReadLine>
public class ReadLineDriver : ActivityDriver<ReadLine>
{
private readonly TextReader input;
public ReadLineHandler(IStringLocalizer<ReadLineHandler> localizer)
public ReadLineDriver()
{
T = localizer;
}
public ReadLineHandler(IStringLocalizer<ReadLineHandler> localizer, TextReader input) : this(localizer)
public ReadLineDriver(TextReader input) : this()
{
this.input = input;
}
private IStringLocalizer<ReadLineHandler> T { get; }
protected override LocalizedString GetEndpoint() => T["Done"];
public override LocalizedString Category => T["Console"];
public override LocalizedString DisplayText => T["Read Line"];
public override LocalizedString Description => T["Read a line from the console"];
protected override async Task<ActivityExecutionResult> OnExecuteAsync(ReadLine activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
{
if (input == null)

View file

@ -7,36 +7,27 @@ using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Console.Handlers
namespace Elsa.Activities.Console.Drivers
{
/// <summary>
/// Writes a text string to the specified stream.
/// </summary>
public class WriteLineHandler : ActivityHandler<WriteLine>
public class WriteLineDriver : ActivityDriver<WriteLine>
{
private readonly IWorkflowExpressionEvaluator evaluator;
private readonly TextWriter output;
public WriteLineHandler(IStringLocalizer<WriteLineHandler> localizer, IWorkflowExpressionEvaluator evaluator)
: this(localizer, evaluator, System.Console.Out)
public WriteLineDriver(IWorkflowExpressionEvaluator evaluator)
: this(evaluator, System.Console.Out)
{
}
public WriteLineHandler(IStringLocalizer<WriteLineHandler> localizer, IWorkflowExpressionEvaluator evaluator, TextWriter output)
public WriteLineDriver(IWorkflowExpressionEvaluator evaluator, TextWriter output)
{
T = localizer;
this.evaluator = evaluator;
this.output = output;
}
private IStringLocalizer<WriteLineHandler> T { get; }
protected override LocalizedString GetEndpoint() => T["Done"];
public override LocalizedString Category => T["Console"];
public override LocalizedString DisplayText => T["Write Line"];
public override LocalizedString Description => T["Write a line to the console"];
protected override async Task<ActivityExecutionResult> OnExecuteAsync(WriteLine activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
{
var text = await evaluator.EvaluateAsync(activity.TextExpression, workflowContext, cancellationToken);

View file

@ -13,7 +13,11 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj"/>
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj" />
</ItemGroup>
<ItemGroup>
<Folder Include="Handlers" />
</ItemGroup>
</Project>

View file

@ -1,15 +1,24 @@
using Elsa.Activities.Console.Handlers;
using Elsa.Activities.Console.Descriptors;
using Elsa.Activities.Console.Drivers;
using Elsa.Extensions;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Activities.Console.Extensions
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddWorkflowsConsole(this IServiceCollection services)
public static IServiceCollection AddConsoleDescriptors(this IServiceCollection services)
{
return services
.AddSingleton<IActivityHandler, ReadLineHandler>()
.AddSingleton<IActivityHandler, WriteLineHandler>();
.AddActivityDescriptor<ReadLineDescriptor>()
.AddActivityDescriptor<WriteLineDescriptor>();
}
public static IServiceCollection AddConsoleDrivers(this IServiceCollection services)
{
return services
.AddActivityDriver<ReadLineDriver>()
.AddActivityDriver<WriteLineDriver>();
}
}
}

View file

@ -0,0 +1,21 @@
using Elsa.Activities.Http.Activities;
using Elsa.Handlers;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Http.Descriptors
{
public class HttpRequestTriggerDescriptor : ActivityDescriptorBase<HttpRequestTrigger>
{
public HttpRequestTriggerDescriptor(IStringLocalizer<HttpRequestTrigger> localizer)
{
T = localizer;
}
public IStringLocalizer<HttpRequestTrigger> T { get; }
public override bool IsTrigger => true;
public override LocalizedString Category => T["HTTP"];
public override LocalizedString DisplayText => T["HTTP Request"];
public override LocalizedString Description => T["Triggers when an incoming HTTP request is received."];
protected override LocalizedString GetEndpoint() => T["Done"];
}
}

View file

@ -11,30 +11,21 @@ using Elsa.Results;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Http.Handlers
namespace Elsa.Activities.Http.Drivers
{
public class HttpRequestTriggerHandler : ActivityHandler<HttpRequestTrigger>
public class HttpRequestTriggerDriver : ActivityDriver<HttpRequestTrigger>
{
private readonly IHttpContextAccessor httpContextAccessor;
private readonly IWorkflowExpressionEvaluator expressionEvaluator;
public HttpRequestTriggerHandler(
IStringLocalizer<HttpRequestTrigger> localizer,
public HttpRequestTriggerDriver(
IHttpContextAccessor httpContextAccessor,
IWorkflowExpressionEvaluator expressionEvaluator)
{
T = localizer;
this.httpContextAccessor = httpContextAccessor;
this.expressionEvaluator = expressionEvaluator;
}
public IStringLocalizer<HttpRequestTrigger> T { get; }
public override bool IsTrigger => true;
public override LocalizedString Category => T["HTTP"];
public override LocalizedString DisplayText => T["HTTP Request"];
public override LocalizedString Description => T["Triggers when an incoming HTTP request is received."];
protected override LocalizedString GetEndpoint() => T["Done"];
protected override async Task<ActivityExecutionResult> OnExecuteAsync(HttpRequestTrigger activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
{
var request = httpContextAccessor.HttpContext.Request;

View file

@ -1,7 +1,9 @@
using Elsa.Activities.Http.Handlers;
using Elsa.Activities.Http.Descriptors;
using Elsa.Activities.Http.Drivers;
using Elsa.Activities.Http.Initialization;
using Elsa.Activities.Http.Services;
using Elsa.Activities.Http.Services.Implementations;
using Elsa.Extensions;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
@ -10,12 +12,17 @@ namespace Elsa.Activities.Http.Extensions
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddWorkflowsHttp(this IServiceCollection services)
public static IServiceCollection AddHttpDescriptors(this IServiceCollection services)
{
return services.AddActivityDescriptor<HttpRequestTriggerDescriptor>();
}
public static IServiceCollection AddHttpDrivers(this IServiceCollection services)
{
services
.AddSingleton<IHttpWorkflowCache, DefaultHttpWorkflowCache>()
.AddAsyncInitializer<HttpWorkflowCacheInitializer>()
.AddSingleton<IActivityHandler, HttpRequestTriggerHandler>();
.AddActivityDriver<HttpRequestTriggerDriver>();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
return services;

View file

@ -0,0 +1,25 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Primitives.Activities;
using Elsa.Handlers;
using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Primitives.Descriptors
{
public class ForEachDescriptor : ActivityDescriptorBase<ForEach>
{
public ForEachDescriptor(IStringLocalizer<ForEachDescriptor> localizer)
{
T = localizer;
}
public IStringLocalizer T { get; }
public override LocalizedString Category => T["Control Flow"];
public override LocalizedString DisplayText => T["For Each"];
public override LocalizedString Description => T["Iterate over a list of items."];
protected override IEnumerable<LocalizedString> GetEndpoints() => Endpoints(T["Next"], T["Done"]);
}
}

View file

@ -0,0 +1,21 @@
using System.Collections.Generic;
using Elsa.Activities.Primitives.Activities;
using Elsa.Handlers;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Primitives.Descriptors
{
public class IfElseDescriptor : ActivityDescriptorBase<IfElse>
{
public IfElseDescriptor(IStringLocalizer<IfElseDescriptor> localizer)
{
T = localizer;
}
public IStringLocalizer T { get; }
public override LocalizedString Category => T["Control Flow"];
public override LocalizedString DisplayText => T["If/Else Branch"];
public override LocalizedString Description => T["Evaluate a boolean condition and continues execution based on the outcome."];
protected override IEnumerable<LocalizedString> GetEndpoints() => Endpoints(T["True"], T["False"]);
}
}

View file

@ -0,0 +1,20 @@
using Elsa.Activities.Primitives.Activities;
using Elsa.Handlers;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Primitives.Descriptors
{
public class SetVariableDescriptor : ActivityDescriptorBase<SetVariable>
{
public SetVariableDescriptor(IStringLocalizer<SetVariableDescriptor> localizer)
{
T = localizer;
}
public IStringLocalizer T { get; }
public override LocalizedString Category => T["Primitives"];
public override LocalizedString DisplayText => T["Set Variable"];
public override LocalizedString Description => T["Set a custom variable on the workflow."];
protected override LocalizedString GetEndpoint() => T["Done"];
}
}

View file

@ -0,0 +1,24 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Primitives.Activities;
using Elsa.Handlers;
using Elsa.Models;
using Elsa.Results;
namespace Elsa.Activities.Primitives.Drivers
{
public class ForEachDriver : ActivityDriver<ForEach>
{
private readonly IWorkflowExpressionEvaluator expressionEvaluator;
public ForEachDriver(IWorkflowExpressionEvaluator expressionEvaluator)
{
this.expressionEvaluator = expressionEvaluator;
}
protected override async Task<ActivityExecutionResult> OnExecuteAsync(ForEach activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
{
return TriggerEndpoint("Done");
}
}
}

View file

@ -0,0 +1,26 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Primitives.Activities;
using Elsa.Handlers;
using Elsa.Models;
using Elsa.Results;
namespace Elsa.Activities.Primitives.Drivers
{
public class IfElseDriver : ActivityDriver<IfElse>
{
private readonly IWorkflowExpressionEvaluator expressionEvaluator;
public IfElseDriver(IWorkflowExpressionEvaluator expressionEvaluator)
{
this.expressionEvaluator = expressionEvaluator;
}
protected override async Task<ActivityExecutionResult> OnExecuteAsync(IfElse activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
{
var result = await expressionEvaluator.EvaluateAsync(activity.ConditionExpression, workflowContext, cancellationToken);
return TriggerEndpoint(result ? "True" : "False");
}
}
}

View file

@ -6,25 +6,17 @@ using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Primitives.Handlers
namespace Elsa.Activities.Primitives.Drivers
{
public class SetVariableHandler : ActivityHandler<SetVariable>
public class SetVariableDriver : ActivityDriver<SetVariable>
{
private readonly IWorkflowExpressionEvaluator expressionEvaluator;
public SetVariableHandler(IStringLocalizer<SetVariableHandler> localizer, IWorkflowExpressionEvaluator expressionEvaluator)
public SetVariableDriver(IWorkflowExpressionEvaluator expressionEvaluator)
{
T = localizer;
this.expressionEvaluator = expressionEvaluator;
}
public override LocalizedString Category => T["Primitives"];
public override LocalizedString DisplayText => T["Set Variable"];
public override LocalizedString Description => T["Set a custom variable on the workflow."];
public IStringLocalizer<SetVariableHandler> T { get; }
protected override LocalizedString GetEndpoint() => T["Done"];
protected override async Task<ActivityExecutionResult> OnExecuteAsync(SetVariable activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
{
var value = await expressionEvaluator.EvaluateAsync(activity.ValueExpression, workflowContext, cancellationToken);

View file

@ -13,7 +13,7 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj"/>
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj" />
</ItemGroup>
</Project>

View file

@ -1,16 +1,26 @@
using Elsa.Activities.Primitives.Handlers;
using Elsa.Activities.Primitives.Descriptors;
using Elsa.Activities.Primitives.Drivers;
using Elsa.Extensions;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Activities.Primitives.Extensions
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddWorkflowsPrimitives(this IServiceCollection services)
public static IServiceCollection AddPrimitiveDescriptors(this IServiceCollection services)
{
return services
.AddSingleton<IActivityHandler, SetVariableHandler>()
.AddSingleton<IActivityHandler, ForEachHandler>()
.AddSingleton<IActivityHandler, IfElseHandler>();
.AddActivityDescriptor<SetVariableDescriptor>()
.AddActivityDescriptor<ForEachDescriptor>()
.AddActivityDescriptor<IfElseDescriptor>();
}
public static IServiceCollection AddPrimitiveDrivers(this IServiceCollection services)
{
return services
.AddActivityDriver<SetVariableDriver>()
.AddActivityDriver<ForEachDriver>()
.AddActivityDriver<IfElseDriver>();
}
}
}

View file

@ -1,34 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Primitives.Activities;
using Elsa.Handlers;
using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Primitives.Handlers
{
public class ForEachHandler : ActivityHandler<ForEach>
{
private readonly IWorkflowExpressionEvaluator expressionEvaluator;
public ForEachHandler(IStringLocalizer<ForEachHandler> localizer, IWorkflowExpressionEvaluator expressionEvaluator)
{
T = localizer;
this.expressionEvaluator = expressionEvaluator;
}
public override LocalizedString Category => T["Control Flow"];
public override LocalizedString DisplayText => T["For Each"];
public override LocalizedString Description => T["Iterate over a list of items."];
public IStringLocalizer<ForEachHandler> T { get; }
protected override IEnumerable<LocalizedString> GetEndpoints() => Endpoints(T["Next"], T["Done"]);
protected override async Task<ActivityExecutionResult> OnExecuteAsync(ForEach activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
{
return TriggerEndpoint("Done");
}
}
}

View file

@ -1,36 +0,0 @@
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Activities.Primitives.Activities;
using Elsa.Handlers;
using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Localization;
namespace Elsa.Activities.Primitives.Handlers
{
public class IfElseHandler : ActivityHandler<IfElse>
{
private readonly IWorkflowExpressionEvaluator expressionEvaluator;
public IfElseHandler(IStringLocalizer<IfElseHandler> localizer, IWorkflowExpressionEvaluator expressionEvaluator)
{
T = localizer;
this.expressionEvaluator = expressionEvaluator;
}
public override LocalizedString Category => T["Control Flow"];
public override LocalizedString DisplayText => T["If/Else Branch"];
public override LocalizedString Description => T["Evaluate a boolean condition and continues execution based on the outcome."];
public IStringLocalizer<IfElseHandler> T { get; }
protected override IEnumerable<LocalizedString> GetEndpoints() => Endpoints(T["True"], T["False"]);
protected override async Task<ActivityExecutionResult> OnExecuteAsync(IfElse activity, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
{
var result = await expressionEvaluator.EvaluateAsync(activity.ConditionExpression, workflowContext, cancellationToken);
return TriggerEndpoint(result ? "True" : "False");
}
}
}

View file

@ -15,6 +15,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="1.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.1" />
</ItemGroup>

View file

@ -0,0 +1,19 @@
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Extensions
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddActivityDescriptor<T>(this IServiceCollection services)
where T : class, IActivityDescriptor
{
return services.AddSingleton<IActivityDescriptor, T>();
}
public static IServiceCollection AddActivityDriver<T>(this IServiceCollection services)
where T : class, IActivityDriver
{
return services.AddSingleton<IActivityDriver, T>();
}
}
}

View file

@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Localization;
namespace Elsa.Handlers
{
public abstract class ActivityDescriptorBase<T> : IActivityDescriptor where T : IActivity
{
public Type ActivityType => typeof(T);
public virtual bool IsTrigger => false;
public abstract LocalizedString Category { get; }
public virtual LocalizedString DisplayText => new LocalizedString(ActivityType.Name, ActivityType.Name);
public virtual LocalizedString Description => new LocalizedString("", "");
protected IEnumerable<LocalizedString> Endpoints(params LocalizedString[] endpoints) => endpoints;
public virtual IEnumerable<LocalizedString> GetEndpoints(IActivity activity) => GetEndpoints((T) activity);
protected virtual IEnumerable<LocalizedString> GetEndpoints(T activity) => Endpoints(GetEndpoint());
protected virtual IEnumerable<LocalizedString> GetEndpoints() => Endpoints(GetEndpoint());
protected virtual LocalizedString GetEndpoint(IActivity activity) => GetEndpoint((T) activity);
protected virtual LocalizedString GetEndpoint(T activity) => GetEndpoint();
protected virtual LocalizedString GetEndpoint() => new LocalizedString("Done", "Done", true);
}
}

View file

@ -1,20 +1,13 @@
using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Localization;
namespace Elsa.Handlers
{
public abstract class ActivityHandlerBase<T> : IActivityHandler where T : IActivity
public abstract class ActivityDriverBase<T> : IActivityDriver where T : IActivity
{
public Type ActivityType => typeof(T);
public virtual bool IsTrigger => false;
public abstract LocalizedString Category { get; }
public virtual LocalizedString DisplayText => new LocalizedString(ActivityType.Name, ActivityType.Name);
public virtual LocalizedString Description => new LocalizedString("", "");
public string ActivityType => typeof(T).Name;
public Task<bool> CanExecuteAsync(ActivityExecutionContext activityContext, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) => OnCanExecuteAsync(activityContext, workflowContext, cancellationToken);
public Task<ActivityExecutionResult> ExecuteAsync(ActivityExecutionContext activityContext, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) => OnExecuteAsync(activityContext, workflowContext, cancellationToken);
public Task<ActivityExecutionResult> ResumeAsync(ActivityExecutionContext activityContext, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken) => OnResumeAsync(activityContext, workflowContext, cancellationToken);
@ -31,12 +24,5 @@ namespace Elsa.Handlers
protected virtual ActivityExecutionResult OnResume(ActivityExecutionContext activityContext, WorkflowExecutionContext workflowContext) => OnResume((T) activityContext.Activity, workflowContext);
protected virtual ActivityExecutionResult OnResume(T activity, WorkflowExecutionContext workflowContext) => Noop();
protected NoopResult Noop() => new NoopResult();
protected IEnumerable<LocalizedString> Endpoints(params LocalizedString[] endpoints) => endpoints;
public virtual IEnumerable<LocalizedString> GetEndpoints(IActivity activity) => GetEndpoints((T) activity);
protected virtual IEnumerable<LocalizedString> GetEndpoints(T activity) => Endpoints(GetEndpoint());
protected virtual IEnumerable<LocalizedString> GetEndpoints() => Endpoints(GetEndpoint());
protected virtual LocalizedString GetEndpoint(IActivity activity) => GetEndpoint((T) activity);
protected virtual LocalizedString GetEndpoint(T activity) => GetEndpoint();
protected virtual LocalizedString GetEndpoint() => new LocalizedString("Done", "Done", true);
}
}

View file

@ -8,7 +8,7 @@ using Microsoft.Extensions.Localization;
namespace Elsa
{
public interface IActivityHandler
public interface IActivityDescriptor
{
/// <summary>
/// A value indicating whether this activity can trigger the execution of the workflow.
@ -40,20 +40,5 @@ namespace Elsa
/// </summary>
/// <returns></returns>
IEnumerable<LocalizedString> GetEndpoints(IActivity activity);
/// <summary>
/// Returns a value of whether the specified activity can execute.
/// </summary>
Task<bool> CanExecuteAsync(ActivityExecutionContext activityContext, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified activity.
/// </summary>
Task<ActivityExecutionResult> ExecuteAsync(ActivityExecutionContext activityContext, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken);
/// <summary>
/// Resumes the specified activity.
/// </summary>
Task<ActivityExecutionResult> ResumeAsync(ActivityExecutionContext activityContext, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken);
}
}

View file

@ -0,0 +1,30 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Models;
using Elsa.Results;
namespace Elsa
{
public interface IActivityDriver
{
/// <summary>
/// The activity type of this driver.
/// </summary>
string ActivityType { get; }
/// <summary>
/// Returns a value of whether the specified activity can execute.
/// </summary>
Task<bool> CanExecuteAsync(ActivityExecutionContext activityContext, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken);
/// <summary>
/// Executes the specified activity.
/// </summary>
Task<ActivityExecutionResult> ExecuteAsync(ActivityExecutionContext activityContext, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken);
/// <summary>
/// Resumes the specified activity.
/// </summary>
Task<ActivityExecutionResult> ResumeAsync(ActivityExecutionContext activityContext, WorkflowExecutionContext workflowContext, CancellationToken cancellationToken);
}
}

View file

@ -0,0 +1,7 @@
namespace Elsa
{
public interface IActivityDriverRegistry
{
IActivityDriver GetDriver(string activityTypeName);
}
}

View file

@ -5,7 +5,7 @@ using Elsa.Models;
namespace Elsa
{
public interface IActivityProvider
public interface IActivityHarvester
{
Task<IEnumerable<ActivityDescriptor>> GetActivitiesAsync(CancellationToken cancellationToken);
}

View file

@ -0,0 +1,13 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Models;
using Elsa.Results;
namespace Elsa
{
public interface IActivityInvoker
{
Task<ActivityExecutionResult> ExecuteAsync(WorkflowExecutionContext workflowContext, IActivity activity, CancellationToken cancellationToken = default);
Task<ActivityExecutionResult> ResumeAsync(WorkflowExecutionContext workflowContext, IActivity activity, CancellationToken cancellationToken = default);
}
}

View file

@ -6,6 +6,7 @@ namespace Elsa
{
public interface IWorkflowInvoker
{
IActivityInvoker ActivityInvoker { get; }
Task<WorkflowExecutionContext> InvokeAsync(Workflow workflow, IActivity startActivity = default, Variables arguments = default, CancellationToken cancellationToken = default);
Task<WorkflowExecutionContext> ResumeAsync(Workflow workflow, IActivity startActivity = default, Variables arguments = default, CancellationToken cancellationToken = default);
}

View file

@ -17,8 +17,5 @@ namespace Elsa.Models
public LocalizedString DisplayText { get; set; }
public LocalizedString Description { get; set; }
public Func<IActivity, IEnumerable<LocalizedString>> GetEndpoints { get; set; }
public Func<ActivityExecutionContext, WorkflowExecutionContext, CancellationToken, Task<bool>> CanExecuteAsync { get; set; }
public Func<ActivityExecutionContext, WorkflowExecutionContext, CancellationToken, Task<ActivityExecutionResult>> ExecuteAsync { get; set; }
public Func<ActivityExecutionContext, WorkflowExecutionContext, CancellationToken, Task<ActivityExecutionResult>> ResumeAsync { get; set; }
}
}

View file

@ -2,13 +2,11 @@
{
public class ActivityExecutionContext
{
public ActivityExecutionContext(IActivity activity, ActivityDescriptor descriptor)
public ActivityExecutionContext(IActivity activity)
{
Activity = activity;
Descriptor = descriptor;
}
public IActivity Activity { get; }
public ActivityDescriptor Descriptor { get; }
}
}

View file

@ -6,12 +6,10 @@ namespace Elsa.Models
{
public class WorkflowExecutionContext
{
private readonly IDictionary<string, ActivityDescriptor> activityDescriptors;
private readonly Stack<IActivity> scheduledActivities;
public WorkflowExecutionContext(Workflow workflow, IDictionary<string, ActivityDescriptor> activityDescriptors)
public WorkflowExecutionContext(Workflow workflow)
{
this.activityDescriptors = activityDescriptors;
Workflow = workflow;
IsFirstPass = true;
scheduledActivities = new Stack<IActivity>();
@ -29,7 +27,7 @@ namespace Elsa.Models
}
public ActivityExecutionContext CreateActivityExecutionContext(IActivity activity) =>
new ActivityExecutionContext(activity, activityDescriptors[activity.Name]);
new ActivityExecutionContext(activity);
public void BeginScope()
{

View file

@ -0,0 +1,20 @@
using System.Collections.Generic;
using System.Linq;
namespace Elsa
{
public class ActivityDriverRegistry : IActivityDriverRegistry
{
private readonly IDictionary<string, IActivityDriver> dictionary;
public ActivityDriverRegistry(IEnumerable<IActivityDriver> drivers)
{
dictionary = drivers.ToDictionary(x => x.ActivityType);
}
public IActivityDriver GetDriver(string activityTypeName)
{
return dictionary.ContainsKey(activityTypeName) ? dictionary[activityTypeName] : null;
}
}
}

View file

@ -0,0 +1,41 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Logging;
namespace Elsa
{
public class ActivityInvoker : IActivityInvoker
{
private readonly IActivityDriverRegistry driverRegistry;
public ActivityInvoker(IActivityDriverRegistry driverRegistry)
{
this.driverRegistry = driverRegistry;
}
public async Task<ActivityExecutionResult> ExecuteAsync(WorkflowExecutionContext workflowContext, IActivity activity, CancellationToken cancellationToken = default)
{
return await InvokeAsync(workflowContext, activity, (context, driver) => driver.ExecuteAsync(context, workflowContext, cancellationToken));
}
public async Task<ActivityExecutionResult> ResumeAsync(WorkflowExecutionContext workflowContext, IActivity activity, CancellationToken cancellationToken = default)
{
return await InvokeAsync(workflowContext, activity, (context, driver) => driver.ResumeAsync(context, workflowContext, cancellationToken));
}
private Task<ActivityExecutionResult> InvokeAsync(
WorkflowExecutionContext workflowContext,
IActivity activity,
Func<ActivityExecutionContext, IActivityDriver, Task<ActivityExecutionResult>> invokeAction)
{
var activityContext = workflowContext.CreateActivityExecutionContext(activity);
var driver = driverRegistry.GetDriver(activity.Name);
return invokeAction(activityContext, driver);
}
}
}

View file

@ -8,9 +8,9 @@ namespace Elsa
{
public class ActivityLibrary : IActivityLibrary
{
private readonly IEnumerable<IActivityProvider> providers;
private readonly IEnumerable<IActivityHarvester> providers;
public ActivityLibrary(IEnumerable<IActivityProvider> providers)
public ActivityLibrary(IEnumerable<IActivityHarvester> providers)
{
this.providers = providers;
}

View file

@ -8,11 +8,11 @@ using Elsa.Models;
namespace Elsa.ActivityProviders
{
public class TypedActivityProvider : IActivityProvider
public class TypedActivityHarvester : IActivityHarvester
{
private readonly IEnumerable<IActivityHandler> handlers;
private readonly IEnumerable<IActivityDescriptor> handlers;
public TypedActivityProvider(IEnumerable<IActivityHandler> handlers)
public TypedActivityHarvester(IEnumerable<IActivityDescriptor> handlers)
{
this.handlers = handlers;
}
@ -28,10 +28,7 @@ namespace Elsa.ActivityProviders
ActivityType = x.ActivityType,
DisplayText = x.DisplayText,
Description = x.Description,
GetEndpoints = x.GetEndpoints,
CanExecuteAsync = x.CanExecuteAsync,
ExecuteAsync = x.ExecuteAsync,
ResumeAsync = x.ResumeAsync
GetEndpoints = x.GetEndpoints
});
return Task.FromResult(descriptors);

View file

@ -15,8 +15,7 @@ namespace Elsa.Extensions
.AddSingleton<IIdGenerator, DefaultIdGenerator>()
.AddScoped<IWorkflowSerializer, WorkflowSerializer>()
.AddScoped<IWorkflowTokenizer, WorkflowTokenizer>()
.AddScoped<IWorkflowInvoker, WorkflowInvoker>()
.AddScoped<IActivityProvider, TypedActivityProvider>()
.AddScoped<IActivityHarvester, TypedActivityHarvester>()
.AddScoped<IActivityLibrary, ActivityLibrary>()
.AddSingleton<ITokenFormatter, JsonTokenFormatter>()
.AddSingleton<ITokenFormatter, YamlTokenFormatter>()
@ -24,7 +23,15 @@ namespace Elsa.Extensions
.AddSingleton<ITokenFormatterProvider, TokenFormatterProvider>()
.AddSingleton<ITokenizerInvoker, TokenizerInvoker>()
.AddSingleton<ITokenizer, DefaultTokenizer>()
.AddSingleton<ITokenizer, ActivityTokenizer>()
.AddSingleton<ITokenizer, ActivityTokenizer>();
}
public static IServiceCollection AddWorkflowsInvoker(this IServiceCollection services)
{
return services
.AddSingleton<IWorkflowInvoker, WorkflowInvoker>()
.AddSingleton<IActivityInvoker, ActivityInvoker>()
.AddSingleton<IActivityDriverRegistry, ActivityDriverRegistry>()
.AddSingleton<IExpressionEvaluator, PlainTextEvaluator>()
.AddSingleton<IExpressionEvaluator, JavaScriptEvaluator>()
.AddSingleton<IWorkflowExpressionEvaluator, WorkflowExpressionEvaluator>();

View file

@ -4,7 +4,7 @@ using Elsa.Results;
namespace Elsa.Handlers
{
public abstract class ActivityHandler<T> : ActivityHandlerBase<T> where T : IActivity
public abstract class ActivityDriver<T> : ActivityDriverBase<T> where T : IActivity
{
protected HaltResult Halt() => new HaltResult();
protected TriggerEndpointsResult TriggerEndpoints(IEnumerable<string> names) => new TriggerEndpointsResult(names);

View file

@ -0,0 +1,22 @@
using System.Collections.Generic;
using System.Linq;
using Elsa.Activities;
using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Localization;
namespace Elsa.Handlers
{
public class UnknownActivityDescriptor : ActivityDescriptorBase<UnknownActivity>
{
public UnknownActivityDescriptor(IStringLocalizer<UnknownActivityDescriptor> localizer)
{
T = localizer;
}
public IStringLocalizer T { get; }
public override LocalizedString Category => T["System"];
protected override IEnumerable<LocalizedString> GetEndpoints() => Enumerable.Empty<LocalizedString>();
}
}

View file

@ -0,0 +1,14 @@
using Elsa.Activities;
using Elsa.Models;
using Elsa.Results;
namespace Elsa.Handlers
{
public class UnknownActivityDriver : ActivityDriver<UnknownActivity>
{
protected override ActivityExecutionResult OnExecute(UnknownActivity activity, WorkflowExecutionContext workflowContext)
{
return Fault($"Unknown activity: {activity.Name}, ID: {activity.Id}");
}
}
}

View file

@ -1,26 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Elsa.Activities;
using Elsa.Models;
using Elsa.Results;
using Microsoft.Extensions.Localization;
namespace Elsa.Handlers
{
public class UnknownActivityHandler : ActivityHandler<UnknownActivity>
{
public UnknownActivityHandler(IStringLocalizer<UnknownActivityHandler> localizer)
{
T = localizer;
}
public IStringLocalizer<UnknownActivityHandler> T { get; }
public override LocalizedString Category => T["System"];
protected override IEnumerable<LocalizedString> GetEndpoints() => Enumerable.Empty<LocalizedString>();
protected override ActivityExecutionResult OnExecute(UnknownActivity activity, WorkflowExecutionContext workflowContext)
{
return Fault($"Unknown activity: {activity.Name}, ID: {activity.Id}");
}
}
}

View file

@ -14,8 +14,7 @@ namespace Elsa.Results
if (workflowContext.IsFirstPass)
{
var activity = workflowContext.CurrentActivity;
var activityContext = workflowContext.CreateActivityExecutionContext(activity);
var result = await activityContext.Descriptor.ResumeAsync(activityContext, workflowContext, cancellationToken);
var result = await invoker.ActivityInvoker.ResumeAsync(workflowContext, activity, cancellationToken);
workflowContext.IsFirstPass = false;
await result.ExecuteAsync(invoker, workflowContext, cancellationToken);

View file

@ -1,4 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
@ -10,21 +11,20 @@ namespace Elsa
{
public class WorkflowInvoker : IWorkflowInvoker
{
public WorkflowInvoker(IActivityLibrary activityLibrary, ILogger<WorkflowInvoker> logger)
private readonly ILogger logger;
public WorkflowInvoker(IActivityInvoker activityInvoker, ILogger<WorkflowInvoker> logger)
{
this.activityLibrary = activityLibrary;
ActivityInvoker = activityInvoker;
this.logger = logger;
}
private readonly IActivityLibrary activityLibrary;
private readonly ILogger logger;
public IActivityInvoker ActivityInvoker { get; }
public async Task<WorkflowExecutionContext> InvokeAsync(Workflow workflow, IActivity startActivity = default, Variables arguments = default, CancellationToken cancellationToken = default)
{
workflow.Arguments = arguments ?? new Variables();
var activityDescriptors = await activityLibrary.ListAsync(cancellationToken);
var activityDescriptorsDictionary = activityDescriptors.ToDictionary(x => x.Name);
var workflowExecutionContext = new WorkflowExecutionContext(workflow, activityDescriptorsDictionary);
var workflowExecutionContext = new WorkflowExecutionContext(workflow);
var isResuming = workflowExecutionContext.Workflow.Status == WorkflowStatus.Resuming;
if (startActivity != null)
@ -91,12 +91,11 @@ namespace Elsa
workflowContext.Fault(ex, activity);
}
private static async Task<ActivityExecutionResult> ExecuteOrResumeActivityAsync(WorkflowExecutionContext workflowContext, IActivity activity, bool isResuming, CancellationToken cancellationToken)
private async Task<ActivityExecutionResult> ExecuteOrResumeActivityAsync(WorkflowExecutionContext workflowContext, IActivity activity, bool isResuming, CancellationToken cancellationToken)
{
var activityContext = workflowContext.CreateActivityExecutionContext(activity);
return isResuming
? await activityContext.Descriptor.ResumeAsync(activityContext, workflowContext, cancellationToken)
: await activityContext.Descriptor.ExecuteAsync(activityContext, workflowContext, cancellationToken);
? await ActivityInvoker.ResumeAsync(workflowContext, activity, cancellationToken)
: await ActivityInvoker.ExecuteAsync(workflowContext, activity, cancellationToken);
}
}
}

View file

@ -28,9 +28,9 @@ namespace SampleHost.Web
.AddWorkflowsCore()
.AddWorkflowsHost()
.AddWorkflowsFileSystemPersistence(Configuration.GetSection("FileStore"))
.AddWorkflowsPrimitives()
.AddWorkflowsConsole()
.AddWorkflowsHttp();
.AddPrimitiveDescriptors()
.AddConsoleDescriptors()
.AddHttpDescriptors();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Latest);
}

View file

@ -5,13 +5,13 @@ namespace Elsa.Web.Extensions
{
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddActivity<THandler, TDriver>(this IServiceCollection services)
where THandler : class, IActivityHandler
where TDriver : class, IDisplayDriver<IActivity>
public static IServiceCollection AddActivity<TDescriptor, TDisplayDriver>(this IServiceCollection services)
where TDescriptor : class, IActivityDescriptor
where TDisplayDriver : class, IDisplayDriver<IActivity>
{
return services
.AddScoped<IActivityHandler, THandler>()
.AddScoped<IDisplayDriver<IActivity>, TDriver>();
.AddScoped<IActivityDescriptor, TDescriptor>()
.AddScoped<IDisplayDriver<IActivity>, TDisplayDriver>();
}
}
}

View file

@ -3,7 +3,7 @@ using Elsa.Web.Drivers;
namespace Elsa.Web.Activities.Console.Drivers
{
public class ReadLineDriver : ActivityDisplayDriver<ReadLine>
public class ReadLineDisplay : ActivityDisplayDriver<ReadLine>
{
}
}

View file

@ -5,7 +5,7 @@ using Elsa.Web.Drivers;
namespace Elsa.Web.Activities.Console.Drivers
{
public class WriteLineDriver : ActivityDisplayDriver<WriteLine, WriteLineViewModel>
public class WriteLineDisplay : ActivityDisplayDriver<WriteLine, WriteLineViewModel>
{
protected override void EditActivity(WriteLine activity, WriteLineViewModel model)
{

View file

@ -13,21 +13,21 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Console\Elsa.Activities.Console.csproj"/>
<ProjectReference Include="..\Elsa.Web.Abstractions\Elsa.Web.Abstractions.csproj"/>
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Console\Elsa.Activities.Console.csproj" />
<ProjectReference Include="..\Elsa.Web.Abstractions\Elsa.Web.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-70362"/>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-70362" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\ReadLine.Design.cshtml"/>
<Content Include="Views\ReadLine.Card.cshtml"/>
<Content Include="Views\WriteLine.Design.cshtml"/>
<Content Include="Views\WriteLine.Edit.cshtml"/>
<Content Include="Views\WriteLine.Card.cshtml"/>
<Content Include="Views\_ViewImports.cshtml"/>
<Content Include="Views\ReadLine.Design.cshtml" />
<Content Include="Views\ReadLine.Card.cshtml" />
<Content Include="Views\WriteLine.Design.cshtml" />
<Content Include="Views\WriteLine.Edit.cshtml" />
<Content Include="Views\WriteLine.Card.cshtml" />
<Content Include="Views\_ViewImports.cshtml" />
</ItemGroup>
</Project>

View file

@ -1,4 +1,4 @@
using Elsa.Activities.Console.Handlers;
using Elsa.Activities.Console.Descriptors;
using Elsa.Web.Activities.Console.Drivers;
using Elsa.Web.Extensions;
using Microsoft.Extensions.DependencyInjection;
@ -11,8 +11,8 @@ namespace Elsa.Web.Activities.Console
public override void ConfigureServices(IServiceCollection services)
{
services
.AddActivity<ReadLineHandler, ReadLineDriver>()
.AddActivity<WriteLineHandler, WriteLineDriver>();
.AddActivity<ReadLineDescriptor, ReadLineDisplay>()
.AddActivity<WriteLineDescriptor, WriteLineDisplay>();
}
}
}

View file

@ -4,7 +4,7 @@ using Elsa.Web.Drivers;
namespace Elsa.Web.Activities.Http.Drivers
{
public class HttpRequestTriggerDriver : ActivityDisplayDriver<HttpRequestTrigger, HttpRequestTriggerViewModel>
public class HttpRequestTriggerDisplay : ActivityDisplayDriver<HttpRequestTrigger, HttpRequestTriggerViewModel>
{
protected override void EditActivity(HttpRequestTrigger activity, HttpRequestTriggerViewModel model)
{

View file

@ -13,16 +13,16 @@
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Http\Elsa.Activities.Http.csproj"/>
<ProjectReference Include="..\Elsa.Web.Abstractions\Elsa.Web.Abstractions.csproj"/>
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Http\Elsa.Activities.Http.csproj" />
<ProjectReference Include="..\Elsa.Web.Abstractions\Elsa.Web.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-70362"/>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-70362" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\_ViewImports.cshtml"/>
<Content Include="Views\_ViewImports.cshtml" />
</ItemGroup>
</Project>

View file

@ -1,4 +1,4 @@
using Elsa.Activities.Http.Handlers;
using Elsa.Activities.Http.Descriptors;
using Elsa.Web.Activities.Http.Drivers;
using Elsa.Web.Extensions;
using Microsoft.Extensions.DependencyInjection;
@ -11,7 +11,7 @@ namespace Elsa.Web.Activities.Http
public override void ConfigureServices(IServiceCollection services)
{
services
.AddActivity<HttpRequestTriggerHandler, HttpRequestTriggerDriver>();
.AddActivity<HttpRequestTriggerDescriptor, HttpRequestTriggerDisplay>();
}
}
}

View file

@ -3,7 +3,7 @@ using Elsa.Web.Drivers;
namespace Elsa.Web.Activities.Primitives.Drivers
{
public class ForEachDriver : ActivityDisplayDriver<ForEach>
public class ForEachDisplay : ActivityDisplayDriver<ForEach>
{
}
}

View file

@ -3,7 +3,7 @@ using Elsa.Web.Drivers;
namespace Elsa.Web.Activities.Primitives.Drivers
{
public class IfElseDriver : ActivityDisplayDriver<IfElse>
public class IfElseDisplay : ActivityDisplayDriver<IfElse>
{
}
}

View file

@ -3,7 +3,7 @@ using Elsa.Web.Drivers;
namespace Elsa.Web.Activities.Primitives.Drivers
{
public class UnknownActivityDriver : ActivityDisplayDriver<UnknownActivity>
public class UnknownActivityDisplay : ActivityDisplayDriver<UnknownActivity>
{
}
}

View file

@ -13,16 +13,16 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-70362"/>
<PackageReference Include="OrchardCore.Module.Targets" Version="1.0.0-beta3-70362" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Primitives\Elsa.Activities.Primitives.csproj"/>
<ProjectReference Include="..\Elsa.Web.Abstractions\Elsa.Web.Abstractions.csproj"/>
<ProjectReference Include="..\..\..\activities\Elsa.Activities.Primitives\Elsa.Activities.Primitives.csproj" />
<ProjectReference Include="..\Elsa.Web.Abstractions\Elsa.Web.Abstractions.csproj" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\_ViewImports.cshtml"/>
<Content Include="Views\_ViewImports.cshtml" />
</ItemGroup>
</Project>

View file

@ -1,4 +1,4 @@
using Elsa.Activities.Primitives.Handlers;
using Elsa.Activities.Primitives.Descriptors;
using Elsa.Handlers;
using Elsa.Web.Activities.Primitives.Drivers;
using Elsa.Web.Extensions;
@ -12,9 +12,9 @@ namespace Elsa.Web.Activities.Primitives
public override void ConfigureServices(IServiceCollection services)
{
services
.AddActivity<IfElseHandler, IfElseDriver>()
.AddActivity<ForEachHandler, ForEachDriver>()
.AddActivity<UnknownActivityHandler, UnknownActivityDriver>();
.AddActivity<IfElseDescriptor, IfElseDisplay>()
.AddActivity<ForEachDescriptor, ForEachDisplay>()
.AddActivity<UnknownActivityDescriptor, UnknownActivityDisplay>();
}
}
}