Added VariableExpression (#217)
* Add VariableExpression * Workflow Runner bug fixes
This commit is contained in:
parent
8092d166a5
commit
ebc2a4a89e
|
|
@ -33,7 +33,7 @@ namespace Elsa.Activities.Console.Activities
|
|||
protected override async Task<IActivityExecutionResult> OnExecuteAsync(ActivityExecutionContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
if (input == null)
|
||||
return Halt();
|
||||
return Suspend();
|
||||
|
||||
var receivedInput = await input.ReadLineAsync();
|
||||
return Execute(receivedInput);
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ namespace Elsa.Activities.Http.Activities
|
|||
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
|
||||
{
|
||||
return Halt(true);
|
||||
return Suspend(true);
|
||||
}
|
||||
|
||||
protected override async Task<IActivityExecutionResult> OnResumeAsync(ActivityExecutionContext context, CancellationToken cancellationToken)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,7 @@ namespace Elsa.Activities.MassTransit.Activities
|
|||
}
|
||||
|
||||
protected override bool OnCanExecute(ActivityExecutionContext context) => context.Input.Value?.GetType() == MessageType;
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context) => Halt(true);
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context) => Suspend(true);
|
||||
|
||||
protected override Task<IActivityExecutionResult> OnResumeAsync(ActivityExecutionContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -44,7 +44,7 @@ namespace Elsa.Activities.Timers.Activities
|
|||
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext workflowContext)
|
||||
{
|
||||
return Halt();
|
||||
return Suspend();
|
||||
}
|
||||
|
||||
protected override async Task<IActivityExecutionResult> OnResumeAsync(ActivityExecutionContext context, CancellationToken cancellationToken)
|
||||
|
|
@ -55,7 +55,7 @@ namespace Elsa.Activities.Timers.Activities
|
|||
return Done();
|
||||
}
|
||||
|
||||
return Halt();
|
||||
return Suspend();
|
||||
}
|
||||
|
||||
private async Task<bool> IsExpiredAsync(ActivityExecutionContext context, CancellationToken cancellationToken)
|
||||
|
|
|
|||
|
|
@ -38,14 +38,14 @@ namespace Elsa.Activities.Timers.Activities
|
|||
{
|
||||
var isExpired = await IsExpiredAsync(context, cancellationToken);
|
||||
|
||||
return isExpired ? (IActivityExecutionResult)Done() : Halt();
|
||||
return isExpired ? (IActivityExecutionResult)Done() : Suspend();
|
||||
}
|
||||
|
||||
protected override async Task<IActivityExecutionResult> OnResumeAsync(ActivityExecutionContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var isExpired = await IsExpiredAsync(context, cancellationToken);
|
||||
|
||||
return isExpired ? (IActivityExecutionResult)Done() : Halt();
|
||||
return isExpired ? (IActivityExecutionResult)Done() : Suspend();
|
||||
}
|
||||
|
||||
private async Task<bool> IsExpiredAsync(ActivityExecutionContext workflowContext, CancellationToken cancellationToken)
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ namespace Elsa.Activities.Timers.Activities
|
|||
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
|
||||
{
|
||||
return Halt();
|
||||
return Suspend();
|
||||
}
|
||||
|
||||
protected override async Task<IActivityExecutionResult> OnResumeAsync(ActivityExecutionContext context, CancellationToken cancellationToken)
|
||||
|
|
@ -53,7 +53,7 @@ namespace Elsa.Activities.Timers.Activities
|
|||
return Done();
|
||||
}
|
||||
|
||||
return Halt();
|
||||
return Suspend();
|
||||
}
|
||||
|
||||
private async Task<bool> IsExpiredAsync(ActivityExecutionContext context, CancellationToken cancellationToken)
|
||||
|
|
|
|||
|
|
@ -35,13 +35,11 @@ namespace Elsa.Activities.Timers.HostedServices
|
|||
{
|
||||
try
|
||||
{
|
||||
using (var scope = serviceProvider.CreateScope())
|
||||
{
|
||||
var workflowInvoker = scope.ServiceProvider.GetRequiredService<IWorkflowRunner>();
|
||||
await workflowInvoker.TriggerAsync(nameof(TimerEvent), Variables.Empty, stoppingToken);
|
||||
await workflowInvoker.TriggerAsync(nameof(CronEvent), Variables.Empty, stoppingToken);
|
||||
await workflowInvoker.TriggerAsync(nameof(InstantEvent), Variables.Empty, stoppingToken);
|
||||
}
|
||||
using var scope = serviceProvider.CreateScope();
|
||||
var workflowInvoker = scope.ServiceProvider.GetRequiredService<IWorkflowRunner>();
|
||||
await workflowInvoker.TriggerAsync(nameof(TimerEvent), cancellationToken: stoppingToken);
|
||||
await workflowInvoker.TriggerAsync(nameof(CronEvent), cancellationToken:stoppingToken);
|
||||
await workflowInvoker.TriggerAsync(nameof(InstantEvent), cancellationToken: stoppingToken);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace Elsa.Activities.UserTask.Activities
|
|||
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
|
||||
{
|
||||
return Halt(true);
|
||||
return Suspend(true);
|
||||
}
|
||||
|
||||
protected override IActivityExecutionResult OnResume(ActivityExecutionContext context)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ namespace Elsa.Activities.Workflows.Activities
|
|||
public class Correlate : Activity
|
||||
{
|
||||
[ActivityProperty(Hint = "An expression that evaluates to the value to store as the correlation ID.")]
|
||||
public IWorkflowExpression<string> ValueScriptExpression
|
||||
public IWorkflowExpression<string> Value
|
||||
{
|
||||
get => GetState<IWorkflowExpression<string>>();
|
||||
set => SetState(value);
|
||||
|
|
@ -26,7 +26,7 @@ namespace Elsa.Activities.Workflows.Activities
|
|||
|
||||
protected override async Task<IActivityExecutionResult> OnExecuteAsync(ActivityExecutionContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
var value = await context.EvaluateAsync(ValueScriptExpression, cancellationToken);
|
||||
var value = await context.EvaluateAsync(Value, cancellationToken);
|
||||
context.WorkflowExecutionContext.Workflow.CorrelationId = value;
|
||||
return Done();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ namespace Elsa.Activities.Workflows.Activities
|
|||
|
||||
protected override IActivityExecutionResult OnExecute(ActivityExecutionContext context)
|
||||
{
|
||||
return Halt(true);
|
||||
return Suspend(true);
|
||||
}
|
||||
|
||||
protected override IActivityExecutionResult OnResume(ActivityExecutionContext context)
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ namespace Elsa.Services.Extensions
|
|||
public static Task TriggerAsync(
|
||||
this IWorkflowRunner workflowRunner,
|
||||
string activityType,
|
||||
Variables input,
|
||||
Variable input,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
return workflowRunner.TriggerAsync(activityType, input, cancellationToken: cancellationToken);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,8 @@ namespace Elsa.Services.Models
|
|||
bool isDisabled = false,
|
||||
string? name = default,
|
||||
string? description = default,
|
||||
bool isLatest = false,
|
||||
bool isPublished = false,
|
||||
IEnumerable<IActivity>? activities = default,
|
||||
IEnumerable<Connection>? connections = default)
|
||||
{
|
||||
|
|
@ -24,6 +26,8 @@ namespace Elsa.Services.Models
|
|||
Version = version;
|
||||
IsSingleton = isSingleton;
|
||||
IsDisabled = isDisabled;
|
||||
IsLatest = isLatest;
|
||||
IsPublished = isPublished;
|
||||
Name = name;
|
||||
Description = description;
|
||||
Activities = activities?.ToList() ?? new List<IActivity>();
|
||||
|
|
|
|||
24
src/core/Elsa.Core/Expressions/VariableExpression.cs
Normal file
24
src/core/Elsa.Core/Expressions/VariableExpression.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using System;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.Expressions
|
||||
{
|
||||
public class VariableExpression : WorkflowExpression
|
||||
{
|
||||
public static string ExpressionType => "Variable";
|
||||
|
||||
public VariableExpression(string variableName, Type returnType) : base(ExpressionType, returnType)
|
||||
{
|
||||
VariableName = variableName;
|
||||
}
|
||||
|
||||
public string VariableName { get; }
|
||||
}
|
||||
|
||||
public class VariableExpression<T> : VariableExpression, IWorkflowExpression<T>
|
||||
{
|
||||
public VariableExpression(string variableName) : base(variableName, typeof(T))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
21
src/core/Elsa.Core/Expressions/VariableHandler.cs
Normal file
21
src/core/Elsa.Core/Expressions/VariableHandler.cs
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.Expressions
|
||||
{
|
||||
public class VariableHandler : IWorkflowExpressionHandler
|
||||
{
|
||||
public string Type => VariableExpression.ExpressionType;
|
||||
|
||||
public Task<object> EvaluateAsync(
|
||||
IWorkflowExpression expression,
|
||||
ActivityExecutionContext context,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
var variableExpression = (VariableExpression)expression;
|
||||
var result = context.GetVariable(variableExpression.VariableName);
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -75,6 +75,7 @@ namespace Microsoft.Extensions.DependencyInjection
|
|||
.TryAddProvider<ITokenFormatter, XmlTokenFormatter>(ServiceLifetime.Singleton)
|
||||
.TryAddProvider<IWorkflowExpressionHandler, LiteralHandler>(ServiceLifetime.Singleton)
|
||||
.TryAddProvider<IWorkflowExpressionHandler, CodeHandler>(ServiceLifetime.Singleton)
|
||||
.TryAddProvider<IWorkflowExpressionHandler, VariableHandler>(ServiceLifetime.Singleton)
|
||||
.AddTransient<IWorkflowFactory, WorkflowFactory>()
|
||||
.AddScoped<IActivityInvoker, ActivityInvoker>()
|
||||
.AddScoped<IWorkflowExpressionEvaluator, WorkflowExpressionEvaluator>()
|
||||
|
|
|
|||
|
|
@ -40,8 +40,11 @@ namespace Elsa.Messages.Handlers
|
|||
|
||||
public async Task Handle(WorkflowCompleted notification, CancellationToken cancellationToken)
|
||||
{
|
||||
if (notification.Workflow.Blueprint.DeleteCompletedWorkflows)
|
||||
await workflowInstanceStore.DeleteAsync(notification.Workflow.Id, cancellationToken);
|
||||
var workflow = notification.Workflow;
|
||||
var blueprint = workflow.Blueprint;
|
||||
|
||||
if (blueprint.DeleteCompletedWorkflows || blueprint.PersistenceBehavior == WorkflowPersistenceBehavior.Suspended)
|
||||
await workflowInstanceStore.DeleteAsync(workflow.Id, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task SaveWorkflowAsync(Workflow workflow, CancellationToken cancellationToken)
|
||||
|
|
|
|||
|
|
@ -51,7 +51,7 @@ namespace Elsa.Persistence.Memory
|
|||
{
|
||||
var query = workflowInstances.Values.AsQueryable();
|
||||
|
||||
query = query.Where(x => x.Status == WorkflowStatus.Running);
|
||||
query = query.Where(x => x.Status == WorkflowStatus.Suspended);
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(correlationId))
|
||||
query = query.Where(x => x.CorrelationId == correlationId);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ namespace Elsa.Services
|
|||
{
|
||||
public abstract class Activity : ActivityBase
|
||||
{
|
||||
protected SuspendWorkflowResult Halt(bool continueOnFirstPass = false) => new SuspendWorkflowResult(continueOnFirstPass);
|
||||
protected SuspendWorkflowResult Suspend(bool continueOnFirstPass = false) => new SuspendWorkflowResult(continueOnFirstPass);
|
||||
protected OutcomeResult Outcomes(IEnumerable<string> names) => new OutcomeResult(names);
|
||||
protected OutcomeResult Outcome(string name) => Outcomes(new[] { name });
|
||||
protected OutcomeResult Outcome(string name, object output) => Outcome(name, Variable.From(output));
|
||||
|
|
|
|||
|
|
@ -36,12 +36,22 @@ namespace Elsa.Services
|
|||
var workflowDefinition = workflowBuilder().Build<T>();
|
||||
return CreateWorkflow(workflowDefinition, input, workflowInstance, correlationId);
|
||||
}
|
||||
|
||||
|
||||
public WorkflowBlueprint CreateWorkflowBlueprint(WorkflowDefinitionVersion definition)
|
||||
{
|
||||
var activities = CreateActivities(definition.Activities).ToList();
|
||||
var connections = CreateConnections(definition.Connections, activities).ToList();
|
||||
return new WorkflowBlueprint(definition.DefinitionId, definition.Version, definition.IsSingleton, definition.IsDisabled, definition.Name, definition.Description, activities, connections);
|
||||
return new WorkflowBlueprint(
|
||||
definition.DefinitionId,
|
||||
definition.Version,
|
||||
definition.IsSingleton,
|
||||
definition.IsDisabled,
|
||||
definition.Name,
|
||||
definition.Description,
|
||||
definition.IsLatest,
|
||||
definition.IsPublished,
|
||||
activities,
|
||||
connections);
|
||||
}
|
||||
|
||||
public Workflow CreateWorkflow(
|
||||
|
|
@ -52,7 +62,7 @@ namespace Elsa.Services
|
|||
{
|
||||
if (blueprint.IsDisabled)
|
||||
throw new InvalidOperationException("Cannot instantiate disabled workflow definitions.");
|
||||
|
||||
|
||||
var id = idGenerator.Generate();
|
||||
var workflow = new Workflow(
|
||||
id,
|
||||
|
|
|
|||
|
|
@ -318,15 +318,13 @@ namespace Elsa.Services
|
|||
? await ExecuteActivityAsync(workflowExecutionContext, scheduledActivity, cancellationToken)
|
||||
: await ResumeActivityAsync(workflowExecutionContext, scheduledActivity, cancellationToken);
|
||||
|
||||
workflowExecutionContext.IsFirstPass = false;
|
||||
start = true;
|
||||
|
||||
await mediator.Publish(new ActivityExecuted(workflow, activity), cancellationToken);
|
||||
|
||||
if (result == null)
|
||||
break;
|
||||
|
||||
await result.ExecuteAsync(this, workflowExecutionContext, cancellationToken);
|
||||
if (result != null)
|
||||
await result.ExecuteAsync(this, workflowExecutionContext, cancellationToken);
|
||||
|
||||
workflowExecutionContext.IsFirstPass = false;
|
||||
start = true;
|
||||
}
|
||||
|
||||
// Determine new workflow state.
|
||||
|
|
@ -470,7 +468,7 @@ namespace Elsa.Services
|
|||
{
|
||||
var instances = await workflowInstanceStore.ListByStatusAsync(
|
||||
definition.Item1.DefinitionId,
|
||||
WorkflowStatus.Running,
|
||||
WorkflowStatus.Suspended,
|
||||
cancellationToken
|
||||
);
|
||||
|
||||
|
|
|
|||
|
|
@ -135,7 +135,7 @@ namespace Elsa.WorkflowBuilders
|
|||
var connections = CreateConnections(connectionDefinitions, activities);
|
||||
var definitionId = !string.IsNullOrWhiteSpace(Id) ? Id : idGenerator.Generate();
|
||||
|
||||
return new WorkflowBlueprint(definitionId, Version, IsSingleton, IsDisabled, Name, Description, activities, connections);
|
||||
return new WorkflowBlueprint(definitionId, Version, IsSingleton, IsDisabled, Name, Description, true, true, activities, connections);
|
||||
}
|
||||
|
||||
private IEnumerable<Connection> CreateConnections(IEnumerable<ConnectionDefinition> connectionDefinitions, IEnumerable<IActivity> activities)
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ namespace Sample02
|
|||
var workflowBuilderFactory = services.GetRequiredService<Func<IWorkflowBuilder>>();
|
||||
var workflowBuilder = workflowBuilderFactory();
|
||||
var workflowBlueprint = workflowBuilder
|
||||
.StartWith<WriteLine>(x => x.Text = new LiteralExpression<string>("Hello world!"))
|
||||
.StartWith<WriteLine>(x => x.Text = new CodeExpression<string>(() => "Hello world!"))
|
||||
.Then(() => Console.WriteLine("Look, custom code!"))
|
||||
.Then<WriteLine>(x => x.Text = new LiteralExpression<string>("Goodbye cruel world..."))
|
||||
.Build();
|
||||
|
|
|
|||
|
|
@ -16,7 +16,7 @@ namespace Sample05
|
|||
.WithId("RecurringWorkflow")
|
||||
.AsSingleton()
|
||||
.StartWith<TimerEvent>(x => x.TimeoutScriptExpression = new LiteralExpression<TimeSpan>("00:00:05"))
|
||||
.Then<WriteLine>(x => x.Text = new JavaScriptExpression<string>("`Trigger received. The time is: ${new Date().toISOString()}`"));
|
||||
.Then<WriteLine>(x => x.Text = new CodeExpression<string>(context => $"Trigger received. The time is: {DateTime.UtcNow.ToLocalTime()}"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ namespace Sample08.Workflows
|
|||
)
|
||||
// Need to ensure that the correlation ID is the same string format that is used by the WorkflowConsumer<T>
|
||||
// MassTransit will always use Guid values for correlation ID's, so need to ensure the same string format is used.
|
||||
.Then<Correlate>(activity => activity.ValueScriptExpression = new JavaScriptExpression<string>("newGuid()"))
|
||||
.Then<Correlate>(activity => activity.Value = new CodeExpression<string>(() => Guid.NewGuid().ToString()))
|
||||
.Then<SendMassTransitMessage>(activity =>
|
||||
{
|
||||
activity.Message = new JavaScriptExpression<CreateOrder>("return { correlationId: correlationId(), order: order};");
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ using Microsoft.Extensions.DependencyInjection;
|
|||
namespace Sample13
|
||||
{
|
||||
/// <summary>
|
||||
/// A strongly-typed workflows program demonstrating scripting, and branching.
|
||||
/// A strongly-typed workflows program demonstrating scripting and branching.
|
||||
/// </summary>
|
||||
internal class Program
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in a new issue