Add While activity (#110)

* Implement While activity

* Variable scope bug fixes

* Merge designer attributes with activity definition

* Update package references
This commit is contained in:
Sipke Schoorstra 2019-10-26 12:46:20 +02:00 committed by GitHub
parent 06412c0cc0
commit cb25239fd1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
55 changed files with 280 additions and 216 deletions

View file

@ -13,11 +13,8 @@ namespace Elsa.Activities.Console.Activities
/// </summary>
[ActivityDefinition(
Category = "Console",
Description = "Read text from standard in."
)]
[ActivityDefinitionDesigner(
Description =
"a => !!a.state.variableName ? `Read text from standard in and store into <strong>${ a.state.variableName }</strong>.` : 'Read text from standard in.'",
Description = "Read text from standard in.",
RuntimeDescription = "a => !!a.state.variableName ? `Read text from standard in and store into <strong>${ a.state.variableName }</strong>.` : 'Read text from standard in.'",
Outcomes = new[] { OutcomeNames.Done }
)]
public class ReadLine : Activity

View file

@ -15,11 +15,8 @@ namespace Elsa.Activities.Console.Activities
/// </summary>
[ActivityDefinition(
Category = "Console",
Description = "Write text to standard out."
)]
[ActivityDefinitionDesigner(
Description =
"x => !!x.state.textExpression ? `Write <strong>${ x.state.textExpression.expression }</strong> to standard out.` : x.definition.description",
Description = "Write text to standard out.",
RuntimeDescription = "x => !!x.state.textExpression ? `Write <strong>${ x.state.textExpression.expression }</strong> to standard out.` : x.definition.description",
Outcomes = new[] { OutcomeNames.Done }
)]
public class WriteLine : Activity

View file

@ -17,8 +17,4 @@
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
</ItemGroup>
</Project>

View file

@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Http" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.0.0" />
</ItemGroup>
</Project>

View file

@ -23,11 +23,8 @@ namespace Elsa.Activities.Http.Activities
[ActivityDefinition(
Category = "HTTP",
DisplayName = "Send HTTP Request",
Description = "Send an HTTP request."
)]
[ActivityDefinitionDesigner(
Description =
"x => !!x.state.url ? `Send HTTP <strong>${ x.state.method } ${ x.state.url.expression }</strong>.` : x.definition.description",
Description = "Send an HTTP request.",
RuntimeDescription = "x => !!x.state.url ? `Send HTTP <strong>${ x.state.method } ${ x.state.url.expression }</strong>.` : x.definition.description",
Outcomes = "x => !!x.state.supportedStatusCodes ? x.state.supportedStatusCodes : []"
)]
public class HttpRequestAction : Activity

View file

@ -20,11 +20,8 @@ namespace Elsa.Activities.Http.Activities
[ActivityDefinition(
Category = "HTTP",
DisplayName = "Receive HTTP Request",
Description = "Receive an incoming HTTP request."
)]
[ActivityDefinitionDesigner(
Description =
"x => !!x.state.path ? `Handle <strong>${ x.state.method } ${ x.state.path }</strong>.` : x.definition.description",
Description = "Receive an incoming HTTP request.",
RuntimeDescription = "x => !!x.state.path ? `Handle <strong>${ x.state.method } ${ x.state.path }</strong>.` : x.definition.description",
Outcomes = new[] { OutcomeNames.Done }
)]
public class HttpRequestEvent : Activity

View file

@ -18,11 +18,8 @@ namespace Elsa.Activities.Http.Activities
[ActivityDefinition(
Category = "HTTP",
DisplayName = "Send HTTP Response",
Description = "Send an HTTP response."
)]
[ActivityDefinitionDesigner(
Description =
"x => !!x.state.statusCode ? `Send an HTTP <strong>${ x.state.statusCode }</strong> - <strong>${ x.state.contentType }</strong> response` : x.definition.description",
Description = "Send an HTTP response.",
RuntimeDescription = "x => !!x.state.statusCode ? `Send an HTTP <strong>${ x.state.statusCode }</strong> - <strong>${ x.state.contentType }</strong> response` : x.definition.description",
Outcomes = new[] { OutcomeNames.Done }
)]
public class HttpResponseAction : Activity

View file

@ -19,11 +19,11 @@
<ItemGroup>
<PackageReference Include="LanguageExt.Core" Version="3.3.29" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection" Version="3.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Http" Version="2.2.2" />
<PackageReference Include="Microsoft.AspNetCore.Http.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Core" Version="2.2.5" />
<PackageReference Include="Microsoft.Extensions.Http" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="3.0.0" />
</ItemGroup>
</Project>

View file

@ -14,9 +14,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MassTransit.AspNetCore" Version="5.5.2" />
<PackageReference Include="MassTransit.Extensions.DependencyInjection" Version="5.5.2" />
<PackageReference Include="MassTransit.RabbitMQ" Version="5.5.2" />
<PackageReference Include="MassTransit.AspNetCore" Version="5.5.6" />
<PackageReference Include="MassTransit.Extensions.DependencyInjection" Version="5.5.6" />
<PackageReference Include="MassTransit.RabbitMQ" Version="5.5.6" />
</ItemGroup>
<ItemGroup>

View file

@ -13,11 +13,8 @@ namespace Elsa.Activities.Timers.Activities
{
[ActivityDefinition(
Category = "Timers",
Description = "Triggers periodically based on a specified CRON expression."
)]
[ActivityDefinitionDesigner(
Description =
"x => !!x.state.cronExpression ? `<strong>${ x.state.cronExpression.expression }</strong>.` : x.definition.description",
Description = "Triggers periodically based on a specified CRON expression.",
RuntimeDescription = "x => !!x.state.cronExpression ? `<strong>${ x.state.cronExpression.expression }</strong>.` : x.definition.description",
Outcomes = new[] { OutcomeNames.Done }
)]
public class CronEvent : Activity
@ -43,7 +40,7 @@ namespace Elsa.Activities.Timers.Activities
get => GetState<Instant?>();
set => SetState(value);
}
protected override async Task<bool> OnCanExecuteAsync(WorkflowExecutionContext context, CancellationToken cancellationToken)
{
return StartTime == null || await IsExpiredAsync(context, cancellationToken);
@ -54,7 +51,8 @@ namespace Elsa.Activities.Timers.Activities
return Halt();
}
protected override async Task<ActivityExecutionResult> OnResumeAsync(WorkflowExecutionContext context,
protected override async Task<ActivityExecutionResult> OnResumeAsync(
WorkflowExecutionContext context,
CancellationToken cancellationToken)
{
if (await IsExpiredAsync(context, cancellationToken))
@ -62,11 +60,12 @@ namespace Elsa.Activities.Timers.Activities
StartTime = null;
return Done();
}
return Halt();
}
private async Task<bool> IsExpiredAsync(WorkflowExecutionContext workflowContext,
private async Task<bool> IsExpiredAsync(
WorkflowExecutionContext workflowContext,
CancellationToken cancellationToken)
{
var cronExpression = await expressionEvaluator.EvaluateAsync(
@ -76,10 +75,10 @@ namespace Elsa.Activities.Timers.Activities
);
var schedule = CrontabSchedule.Parse(cronExpression);
var now = clock.GetCurrentInstant();
if (StartTime == null)
StartTime = now;
var nextOccurrence = schedule.GetNextOccurrence(StartTime.Value.ToDateTimeUtc());
return now.ToDateTimeUtc() >= nextOccurrence;

View file

@ -18,7 +18,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="3.0.0" />
<PackageReference Include="NCrontab" Version="3.3.1" />
</ItemGroup>

View file

@ -8,7 +8,9 @@ namespace Elsa.Attributes
public string Type { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public string RuntimeDescription { get; set; }
public string Category { get; set; }
public string Icon { get; set; }
public object Outcomes { get; set; }
}
}

View file

@ -1,11 +0,0 @@
using System;
namespace Elsa.Attributes
{
[AttributeUsage(AttributeTargets.Class)]
public class ActivityDefinitionDesignerAttribute : Attribute
{
public string Description { get; set; }
public object Outcomes { get; set; }
}
}

View file

@ -15,7 +15,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Jint" Version="3.0.0-beta-1612" />
<PackageReference Include="Jint" Version="3.0.0-beta-1629" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Primitives" Version="3.0.0" />

View file

@ -25,6 +25,8 @@ namespace Elsa.Models
public string Id { get; set; }
public string Type { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public int Left { get; set; }
public int Top { get; set; }
public JObject State { get; set; }

View file

@ -7,5 +7,6 @@ namespace Elsa
public const string True = "True";
public const string False = "False";
public const string Iterate = "Iterate";
public const string Loop = "Loop";
}
}

View file

@ -13,7 +13,10 @@ namespace Elsa.Services
IOutcomeBuilder When(string outcome);
IActivityBuilder Then<T>(Action<T> setup = null, Action<IActivityBuilder> branch = null, string id = null) where T : class, IActivity;
IActivityBuilder WithId(string id);
IActivityBuilder WithDisplayName(string displayName);
IActivityBuilder WithDescription(string description);
IWorkflowBuilder Then(string activityId);
IActivityBuilder Then(IActivityBuilder targetActivity);
WorkflowDefinitionVersion Build();
ActivityDefinition BuildActivity();
}

View file

@ -12,8 +12,7 @@ namespace Elsa.Services.Models
{
public Workflow(
string id,
string definitionId,
int version,
WorkflowDefinitionVersion definition,
Instant createdAt,
IEnumerable<IActivity> activities,
IEnumerable<Connection> connections,
@ -21,8 +20,7 @@ namespace Elsa.Services.Models
string correlationId = default) : this()
{
Id = id;
DefinitionId = definitionId;
Version = version;
Definition = definition;
CreatedAt = createdAt;
CorrelationId = correlationId;
Activities = activities.ToList();
@ -30,6 +28,8 @@ namespace Elsa.Services.Models
Input = new Variables(input ?? Variables.Empty);
}
public Workflow()
{
Scopes = new Stack<WorkflowExecutionScope>(new[] { new WorkflowExecutionScope() });
@ -38,8 +38,7 @@ namespace Elsa.Services.Models
}
public string Id { get; set; }
public string DefinitionId { get; }
public int Version { get; }
public WorkflowDefinitionVersion Definition { get; }
public string CorrelationId { get; set; }
public WorkflowStatus Status { get; set; }
public Instant CreatedAt { get; set; }
@ -63,8 +62,8 @@ namespace Elsa.Services.Models
return new WorkflowInstance
{
Id = Id,
DefinitionId = DefinitionId,
Version = Version,
DefinitionId = Definition.Id,
Version = Definition.Version,
CorrelationId = CorrelationId,
Status = Status,
CreatedAt = CreatedAt,

View file

@ -53,8 +53,21 @@ namespace Elsa.Services.Models
public IActivity PopScheduledActivity() => CurrentActivity = scheduledActivities.Pop();
public void ScheduleHaltingActivity(IActivity activity) => scheduledHaltingActivities.Push(activity);
public IActivity PopScheduledHaltingActivity() => scheduledHaltingActivities.Pop();
public void SetVariable(string name, object value) => CurrentScope.SetVariable(name, value);
public T GetVariable<T>(string name) => CurrentScope.GetVariable<T>(name);
public void SetVariable(string name, object value)
{
// Get the first scope (starting from the oldest one) containing the variable (existing variable). Otherwise use the current scope (new variable declaration)
var scope = Workflow.Scopes.Reverse().FirstOrDefault(x => x.Variables.ContainsKey(name)) ?? CurrentScope;
scope.SetVariable(name, value);
}
public T GetVariable<T>(string name)
{
// Get the first scope (starting from the newest one) containing the variable.
var scope = Workflow.Scopes.FirstOrDefault(x => x.Variables.ContainsKey(name)) ?? CurrentScope;
return scope.GetVariable<T>(name);
}
public void SetLastResult(object value) => CurrentScope.LastResult = value;
public void Start()

View file

@ -14,9 +14,9 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AutoMapper" Version="8.1.1" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="2.2.0" />
<PackageReference Include="NodaTime" Version="2.4.5" />
<PackageReference Include="AutoMapper" Version="9.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="3.0.0" />
<PackageReference Include="NodaTime" Version="3.0.0-beta01" />
</ItemGroup>
</Project>

View file

@ -9,8 +9,8 @@ namespace Elsa.Activities.ControlFlow
[ActivityDefinition(
Category = "Control Flow",
Description = "Fork workflow execution into multiple branches.",
Icon = "fas fa-code-branch fa-rotate-180")]
[ActivityDefinitionDesigner(Outcomes = "x => x.state.branches")]
Icon = "fas fa-code-branch fa-rotate-180",
Outcomes = "x => x.state.branches")]
public class Fork : Activity
{
[ActivityProperty(

View file

@ -12,11 +12,8 @@ namespace Elsa.Activities.ControlFlow
[ActivityDefinition(
DisplayName = "If/Else",
Category = "Control Flow",
Description = "Evaluate a Boolean expression and continue execution depending on the result."
)]
[ActivityDefinitionDesigner(
Description =
"x => !!x.state.expression ? `Evaluate <strong>${ x.state.expression.expression }</strong> and continue execution depending on the result.` : x.definition.description",
Description = "Evaluate a Boolean expression and continue execution depending on the result.",
RuntimeDescription = "x => !!x.state.expression ? `Evaluate <strong>${ x.state.expression.expression }</strong> and continue execution depending on the result.` : x.definition.description",
Outcomes = new[] { OutcomeNames.True, OutcomeNames.False }
)]
public class IfElse : Activity

View file

@ -14,11 +14,8 @@ namespace Elsa.Activities.ControlFlow
[ActivityDefinition(
Category = "Control Flow",
Description = "Merge workflow execution back into a single branch.",
Icon = "fas fa-code-branch"
)]
[ActivityDefinitionDesigner(
Description =
"x => !!x.state.joinMode ? `Merge workflow execution back into a single branch using mode <strong>${ x.state.joinMode }</strong>` : x.definition.description",
Icon = "fas fa-code-branch",
RuntimeDescription = "x => !!x.state.joinMode ? `Merge workflow execution back into a single branch using mode <strong>${ x.state.joinMode }</strong>` : x.definition.description",
Outcomes = new[] { OutcomeNames.Done }
)]
public class Join : Activity, IWorkflowEventHandler

View file

@ -13,11 +13,8 @@ namespace Elsa.Activities.ControlFlow
[ActivityDefinition(
Category = "Control Flow",
Description = "Switch execution based on a given expression.",
Icon = "far fa-list-alt"
)]
[ActivityDefinitionDesigner(
Description =
"x => !!x.state.expression ? `Switch execution based on <strong>${ x.state.expression.expression }</strong>.` : x.definition.description",
Icon = "far fa-list-alt",
RuntimeDescription = "x => !!x.state.expression ? `Switch execution based on <strong>${ x.state.expression.expression }</strong>.` : x.definition.description",
Outcomes = "x => x.state.cases.map(c => c.toString())"
)]
public class Switch : Activity
@ -44,7 +41,8 @@ namespace Elsa.Activities.ControlFlow
set => SetState(value);
}
protected override async Task<ActivityExecutionResult> OnExecuteAsync(WorkflowExecutionContext workflowContext,
protected override async Task<ActivityExecutionResult> OnExecuteAsync(
WorkflowExecutionContext workflowContext,
CancellationToken cancellationToken)
{
var result = await expressionEvaluator.EvaluateAsync(Expression, workflowContext, cancellationToken);

View file

@ -0,0 +1,56 @@
using System.Threading;
using System.Threading.Tasks;
using Elsa.Attributes;
using Elsa.Expressions;
using Elsa.Extensions;
using Elsa.Results;
using Elsa.Services;
using Elsa.Services.Models;
namespace Elsa.Activities.ControlFlow
{
[ActivityDefinition(Category = "Control Flow", Description = "Execute while a given condition is true.", Icon = "far fa-circle")]
public class While : Activity
{
private readonly IWorkflowExpressionEvaluator expressionEvaluator;
public While(IWorkflowExpressionEvaluator expressionEvaluator)
{
this.expressionEvaluator = expressionEvaluator;
}
[ActivityProperty(Hint = "Enter an expression that evaluates to a boolean value.")]
public WorkflowExpression<bool> ConditionExpression
{
get => GetState(() => new JavaScriptExpression<bool>("true"));
set => SetState(value);
}
public bool HasStarted
{
get => GetState(() => false);
set => SetState(value);
}
protected override async Task<ActivityExecutionResult> OnExecuteAsync(
WorkflowExecutionContext context,
CancellationToken cancellationToken)
{
var loop = await expressionEvaluator.EvaluateAsync(ConditionExpression, context, cancellationToken);
if(HasStarted)
context.EndScope();
if (loop)
{
if (!HasStarted)
HasStarted = true;
context.BeginScope();
return Outcome(OutcomeNames.Loop);
}
return Done();
}
}
}

View file

@ -11,11 +11,8 @@ namespace Elsa.Activities.Primitives
[ActivityDefinition(
DisplayName = "Set Variable",
Description = "Set variable on the workflow.",
Category = "Primitives"
)]
[ActivityDefinitionDesigner(
Description =
"x => !!x.state.variableName ? `<strong>${x.state.variableName}</strong> = <strong>${x.state.valueExpression.expression}</strong><br/>${x.state.valueExpression.syntax}` : x.definition.description",
Category = "Primitives",
RuntimeDescription = "x => !!x.state.variableName ? `<strong>${x.state.variableName}</strong> = <strong>${x.state.valueExpression.expression}</strong><br/>${x.state.valueExpression.syntax}` : x.definition.description",
Outcomes = new[] { OutcomeNames.Done }
)]
public class SetVariable : Activity
@ -51,7 +48,7 @@ namespace Elsa.Activities.Primitives
workflowContext,
cancellationToken
);
workflowContext.CurrentScope.SetVariable(VariableName, value);
workflowContext.SetVariable(VariableName, value);
return Done();
}
}

View file

@ -16,10 +16,11 @@
<ItemGroup>
<PackageReference Include="AutoMapper" Version="9.0.0" />
<PackageReference Include="Humanizer.Core" Version="2.7.9" />
<PackageReference Include="MediatR" Version="7.0.0" />
<PackageReference Include="MediatR.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
<PackageReference Include="Microsoft.AspNetCore.Hosting.Server.Abstractions" Version="2.2.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.5.0" />
<PackageReference Include="Microsoft.CSharp" Version="4.6.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Abstractions" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" />
@ -29,7 +30,7 @@
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
<PackageReference Include="NodaTime.Serialization.JsonNet" Version="2.2.0" />
<PackageReference Include="Elsa.Scrutor" Version="3.0.2.25" />
<PackageReference Include="Elsa.Scrutor" Version="3.0.2.26" />
<PackageReference Include="YamlDotNet.NetStandard" Version="4.0.0" />
</ItemGroup>

View file

@ -175,6 +175,7 @@ namespace Microsoft.Extensions.DependencyInjection
{
return services
.AddActivity<ForEach>()
.AddActivity<While>()
.AddActivity<Fork>()
.AddActivity<Join>()
.AddSingleton<IWorkflowEventHandler>(sp => sp.GetRequiredService<Join>())

View file

@ -7,22 +7,20 @@ using Elsa.Attributes;
using Elsa.Design;
using Elsa.Expressions;
using Elsa.Services.Models;
using Elsa.WorkflowDesigner.Models;
using Humanizer;
namespace Elsa.WorkflowDesigner
namespace Elsa.Metadata
{
public static class ActivityDescriber
{
public static ActivityDefinitionModel Describe<T>() where T : IActivity
public static ActivityDescriptor Describe<T>() where T : IActivity
{
return Describe(typeof(T));
}
public static ActivityDefinitionModel Describe(Type activityType)
public static ActivityDescriptor Describe(Type activityType)
{
var activityDefinitionAttribute = activityType.GetCustomAttribute<ActivityDefinitionAttribute>();
var activityDesignerAttribute = activityType.GetCustomAttribute<ActivityDefinitionDesignerAttribute>();
var typeName = activityDefinitionAttribute?.Type ?? activityType.Name;
var displayName =
@ -30,25 +28,22 @@ namespace Elsa.WorkflowDesigner
activityType.Name.Humanize(LetterCasing.Title);
var description = activityDefinitionAttribute?.Description;
var runtimeDescription = activityDefinitionAttribute?.RuntimeDescription;
var category = activityDefinitionAttribute?.Category ?? "Miscellaneous";
var icon = activityDefinitionAttribute?.Icon;
var designerDescription = activityDesignerAttribute?.Description;
var designerOutcomes = activityDesignerAttribute?.Outcomes ?? new[] { OutcomeNames.Done };
var outcomes = activityDefinitionAttribute?.Outcomes ?? new[] { OutcomeNames.Done };
var properties = DescribeProperties(activityType);
return new ActivityDefinitionModel
return new ActivityDescriptor
{
Type = typeName.Pascalize(),
DisplayName = displayName,
Description = description,
RuntimeDescription = runtimeDescription,
Category = category,
Icon = icon,
Properties = properties.ToArray(),
Designer = new ActivityDesignerSettings
{
Description = designerDescription,
Outcomes = designerOutcomes
}
Outcomes = outcomes
};
}

View file

@ -1,23 +1,24 @@
namespace Elsa.WorkflowDesigner.Models
namespace Elsa.Metadata
{
public class ActivityDefinitionModel
public class ActivityDescriptor
{
public ActivityDefinitionModel()
public ActivityDescriptor()
{
Type = "Activity";
Properties = new ActivityPropertyDescriptor[0];
Category = "Miscellaneous";
DisplayName = "Activity";
Properties = new ActivityPropertyDescriptor[0];
Designer = new ActivityDesignerSettings();
Outcomes = null;
}
public string Type { get; set; }
public string DisplayName { get; set; }
public string? Description { get; set; }
public string? RuntimeDescription { get; set; }
public string Category { get; set; }
public string? Icon { get; set; }
public object? Outcomes { get; set; }
public ActivityPropertyDescriptor[] Properties { get; set; }
public ActivityDesignerSettings Designer { get; set; }
}
}

View file

@ -1,4 +1,4 @@
namespace Elsa.WorkflowDesigner.Models
namespace Elsa.Metadata
{
public class ActivityPropertyDescriptor
{

View file

@ -23,7 +23,7 @@ namespace Elsa.Scripting
engine.SetValue("correlationId", (Func<object>) (() => context.Workflow.CorrelationId));
engine.SetValue("currentCulture", (Func<object>) (() => CultureInfo.InvariantCulture));
var variables = workflowExecutionContext.Workflow.Scopes
var variables = workflowExecutionContext.Workflow.Scopes.Reverse()
.Select(x => x.Variables)
.Aggregate(Variables.Empty, (x, y) => new Variables(x.Union(y)));

View file

@ -52,8 +52,7 @@ namespace Elsa.Services
var id = idGenerator.Generate();
var workflow = new Workflow(
id,
definition.DefinitionId,
definition.Version,
definition,
clock.GetCurrentInstant(),
activities,
connections,

View file

@ -18,6 +18,8 @@ namespace Elsa.WorkflowBuilders
public WorkflowBuilder WorkflowBuilder { get; }
public ActivityDefinition Activity { get; }
public string Id { get; set; }
public string Description { get; set; }
public string DisplayName { get; set; }
public IActivityBuilder StartWith<T>(Action<T> setup = default, string id = default) where T : class, IActivity
{
@ -45,6 +47,18 @@ namespace Elsa.WorkflowBuilders
Id = id;
return this;
}
public IActivityBuilder WithDisplayName(string displayName)
{
DisplayName = displayName;
return this;
}
public IActivityBuilder WithDescription(string description)
{
Description = description;
return this;
}
public IWorkflowBuilder Then(string activityId)
{
@ -56,9 +70,17 @@ namespace Elsa.WorkflowBuilders
return WorkflowBuilder;
}
public IActivityBuilder Then(IActivityBuilder targetActivity)
{
WorkflowBuilder.Connect(this, targetActivity);
return this;
}
public ActivityDefinition BuildActivity()
{
Activity.Id = Id;
Activity.Description = Description;
Activity.DisplayName = DisplayName;
return Activity;
}

View file

@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Elsa.Metadata;
using Elsa.Models;
using Elsa.Services;
using Elsa.Services.Models;
@ -79,10 +80,13 @@ namespace Elsa.WorkflowBuilders
var activity = activityResolver.ResolveActivity(setupActivity);
var activityBlueprint = ActivityDefinition.FromActivity(activity);
var activityBuilder = new ActivityBuilder(this, activityBlueprint, id);
var activityDescriptor = ActivityDescriber.Describe<T>();
if (id != null)
activity.Id = id;
activityBuilder.DisplayName = activityDescriptor.DisplayName;
activityBuilder.Description = activityDescriptor.Description;
activityBuilders.Add(activityBuilder);
return activityBuilder;
}

View file

@ -1,3 +1,4 @@
using Elsa.Metadata;
using Elsa.WorkflowDesigner.Models;
namespace Elsa.Dashboard.Areas.Elsa.ViewModels
@ -11,7 +12,7 @@ namespace Elsa.Dashboard.Areas.Elsa.ViewModels
public string Description { get; set; }
public bool IsSingleton { get; set; }
public bool IsDisabled { get; set; }
public ActivityDefinitionModel[] ActivityDefinitions { get; set; }
public ActivityDescriptor[] ActivityDefinitions { get; set; }
public WorkflowModel WorkflowModel { get; set; }
}
}

View file

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Elsa.Metadata;
using Elsa.Models;
using Elsa.WorkflowDesigner.Models;
@ -11,7 +12,7 @@ namespace Elsa.Dashboard.Areas.Elsa.ViewModels
WorkflowInstance workflowInstance,
WorkflowDefinitionVersion workflowDefinition,
WorkflowModel workflowModel,
IEnumerable<ActivityDefinitionModel> activityDefinitions,
IEnumerable<ActivityDescriptor> activityDefinitions,
string returnUrl)
{
WorkflowInstance = workflowInstance;
@ -24,7 +25,7 @@ namespace Elsa.Dashboard.Areas.Elsa.ViewModels
public WorkflowInstance WorkflowInstance { get; }
public WorkflowDefinitionVersion WorkflowDefinition { get; }
public WorkflowModel WorkflowModel { get; }
public ActivityDefinitionModel[] ActivityDefinitions { get; }
public ActivityDescriptor[] ActivityDefinitions { get; }
public string ReturnUrl { get; }
}
}

View file

@ -32,8 +32,8 @@
<!-- CSS Files -->
<link href="~/_content/Elsa.Dashboard/assets/css/argon-dashboard.css?v=1.1.0" rel="stylesheet"/>
<link href="~/_content/Elsa.Dashboard/assets/css/elsa.css?v=1.1.0" rel="stylesheet"/>
<script type="module" src="https://unpkg.com/@@elsa-workflows/elsa-workflow-designer@0.0.60/dist/elsa-workflow-designer/elsa-workflow-designer.esm.js"></script>
<script nomodule="" src="https://unpkg.com/@@elsa-workflows/elsa-workflow-designer@0.0.60/dist/elsa-workflow-designer/elsa-workflow-designer.js"></script>
<script type="module" src="https://unpkg.com/@@elsa-workflows/elsa-workflow-designer@0.0.61/dist/elsa-workflow-designer/elsa-workflow-designer.esm.js"></script>
<script nomodule="" src="https://unpkg.com/@@elsa-workflows/elsa-workflow-designer@0.0.61/dist/elsa-workflow-designer/elsa-workflow-designer.js"></script>
@await RenderSectionAsync("HeadScripts", false)
</head>

View file

@ -12,9 +12,6 @@
</div>
<div class="col-4 text-right">
<a href="#!" class="btn btn-secondary" onclick="exportWorkflow()">Download</a>
<a href="#!" class="btn btn-secondary" data-toggle="modal" data-target="#workflow-properties-modal">
<i class="ni ni-settings-gear-65"></i>
</a>
</div>
</div>
</div>

View file

@ -1,5 +1,6 @@
using System;
using Elsa.Dashboard.Options;
using Elsa.Metadata;
using Elsa.Services.Models;
using Elsa.WorkflowDesigner;
using Microsoft.Extensions.DependencyInjection;

View file

@ -1,25 +1,26 @@
using System.Collections;
using System.Collections.Generic;
using Elsa.Metadata;
using Elsa.WorkflowDesigner.Models;
namespace Elsa.Dashboard.Options
{
public class ActivityDefinitionList : IEnumerable<ActivityDefinitionModel>
public class ActivityDefinitionList : IEnumerable<ActivityDescriptor>
{
public ActivityDefinitionList()
{
Items = new Dictionary<string, ActivityDefinitionModel>();
Items = new Dictionary<string, ActivityDescriptor>();
}
private IDictionary<string, ActivityDefinitionModel> Items { get; }
private IDictionary<string, ActivityDescriptor> Items { get; }
public ActivityDefinitionList Add(ActivityDefinitionModel item)
public ActivityDefinitionList Add(ActivityDescriptor item)
{
Items[item.Type] = item;
return this;
}
public IEnumerator<ActivityDefinitionModel> GetEnumerator() => Items.Values.GetEnumerator();
public IEnumerator<ActivityDescriptor> GetEnumerator() => Items.Values.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator()
{

View file

@ -15,12 +15,12 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Humanizer.Core" Version="2.7.2" />
<PackageReference Include="Humanizer.Core" Version="2.7.9" />
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="2.2.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\core\Elsa.Abstractions\Elsa.Abstractions.csproj" />
<ProjectReference Include="..\..\core\Elsa.Core\Elsa.Core.csproj" />
</ItemGroup>
</Project>

View file

@ -1,8 +0,0 @@
namespace Elsa.WorkflowDesigner.Models
{
public class ActivityDesignerSettings
{
public object? Description { get; set; }
public object? Outcomes { get; set; }
}
}

View file

@ -1,3 +1,4 @@
using Elsa.Metadata;
using Elsa.WorkflowDesigner.Models;
using Elsa.WorkflowDesigner.ViewModels;
using Microsoft.AspNetCore.Mvc;
@ -10,13 +11,13 @@ namespace Elsa.WorkflowDesigner.ViewComponents
{
public IViewComponentResult Invoke(
string id,
ActivityDefinitionModel[]? activityDefinitions = null,
ActivityDescriptor[]? activityDefinitions = null,
WorkflowModel? workflow = null,
bool? isReadonly = null)
{
var model = new WorkflowDesignerViewComponentModel(
id,
Serialize(activityDefinitions ?? new ActivityDefinitionModel[0]),
Serialize(activityDefinitions ?? new ActivityDescriptor[0]),
Serialize(workflow ?? new WorkflowModel()),
isReadonly.GetValueOrDefault()
);

View file

@ -8,7 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="2.6.0" />
<PackageReference Include="Microsoft.Azure.DocumentDB.Core" Version="2.8.1" />
</ItemGroup>
<ItemGroup>

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
<LangVersion>latest</LangVersion>
<PackageVersion>1.0.0</PackageVersion>
<Authors>Sipke Schoorstra</Authors>
@ -15,11 +15,11 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="2.2.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="2.2.6" />
<PackageReference Include="FlexLabs.EntityFrameworkCore.Upsert" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.0.0" />
</ItemGroup>
<ItemGroup>

View file

@ -21,11 +21,11 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="3.0.0" />
<PackageReference Include="YesSql.Core" Version="2.0.0-beta-1395" />
<PackageReference Include="YesSql.Provider.MySql" Version="1.0.0-beta-1395" />
<PackageReference Include="YesSql.Provider.PostgreSql" Version="1.0.0-beta-1395" />
<PackageReference Include="YesSql.Provider.Sqlite" Version="1.0.0-beta-1395" />
<PackageReference Include="YesSql.Provider.SqlServer" Version="1.0.0-beta-1395" />
<PackageReference Include="YesSql.Core" Version="2.0.0-beta-1437" />
<PackageReference Include="YesSql.Provider.MySql" Version="1.0.0-beta-1437" />
<PackageReference Include="YesSql.Provider.PostgreSql" Version="1.0.0-beta-1437" />
<PackageReference Include="YesSql.Provider.Sqlite" Version="1.0.0-beta-1437" />
<PackageReference Include="YesSql.Provider.SqlServer" Version="1.0.0-beta-1437" />
</ItemGroup>
</Project>

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
</PropertyGroup>
<ItemGroup>

View file

@ -1,6 +1,7 @@
using Elsa;
using Elsa.Activities.Console.Activities;
using Elsa.Activities.ControlFlow;
using Elsa.Activities.Primitives;
using Elsa.Expressions;
using Elsa.Services;
using Elsa.Services.Models;
@ -13,57 +14,66 @@ namespace Sample04
public void Build(IWorkflowBuilder builder)
{
builder
.StartWith<WriteLine>(x => x.TextExpression = new LiteralExpression("Welcome to Calculator Workflow!"), "welcome")
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Enter number 1:"), id: "enter-first-number-prompt")
.Then<ReadLine>(x => x.VariableName = "number1", id: "read-first-number")
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Enter number 2:"), id: "enter-second-number-prompt")
.Then<ReadLine>(x => x.VariableName = "number2", id: "read-second-number")
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Now enter the operation you wish to apply. Options are: add, subtract, multiply or divide:"), id: "enter-operation-prompt")
.Then<ReadLine>(x => x.VariableName = "operation", id: "read-operation")
.Then<Switch>(@switch =>
.StartWith<WriteLine>(x => x.TextExpression = new LiteralExpression("Welcome to Calculator Workflow!")).WithDisplayName("Welcome")
.Then<SetVariable>(
x =>
{
@switch.Expression = new JavaScriptExpression<string>("operation");
@switch.Cases = new[] { "add", "subtract", "multiply", "divide" };
},
@switch =>
x.VariableName = "Exit";
x.ValueExpression = new JavaScriptExpression<bool>("false");
}).WithDisplayName("Initialize Exit Variable")
.Then<While>(
x => x.ConditionExpression = new JavaScriptExpression<bool>("!Exit"),
whileActivity =>
{
@switch
.When("add")
.Then<Sum>(SetupOperation, id: "perform-sum")
.Then("show-result");
@switch
.When("subtract")
.Then<Subtract>(SetupOperation, id: "perform-subtract")
.Then("show-result");
@switch
.When("multiply")
.Then<Multiply>(SetupOperation, id: "perform-multiply")
.Then("show-result");
@switch
.When("divide")
.Then<Divide>(SetupOperation, id: "perform-divide")
.Then("show-result");
},
"inspect-selected-operation"
)
.Add<WriteLine>(x => x.TextExpression = new JavaScriptExpression<string>("`Result: ${result}`"), "show-result")
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Try again? (y/n)"), id: "try-again-prompt")
.Then<ReadLine>(x => x.VariableName = "retry", id: "read-try-again")
.Then<IfElse>(
x => x.ConditionExpression = new JavaScriptExpression<bool>("retry.toLowerCase() === 'y'"),
ifElse =>
{
ifElse
.When(OutcomeNames.True)
.Then("enter-first-number-prompt");
ifElse
.When(OutcomeNames.False)
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Bye!"), id: "say-good-bye");
}, id: "inspect-retry");
whileActivity
.When(OutcomeNames.Loop)
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Enter number 1:"))
.Then<ReadLine>(x => x.VariableName = "number1")
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Enter number 2:"))
.Then<ReadLine>(x => x.VariableName = "number2")
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Now enter the operation you wish to apply. Options are: add, subtract, multiply or divide:"))
.Then<ReadLine>(x => x.VariableName = "operation")
.Then<Switch>(
@switch =>
{
@switch.Expression = new JavaScriptExpression<string>("operation");
@switch.Cases = new[] { "add", "subtract", "multiply", "divide" };
},
@switch =>
{
@switch
.When("add")
.Then<Sum>(SetupOperation, id: "perform-sum")
.Then("ShowResult");
@switch
.When("subtract")
.Then<Subtract>(SetupOperation, id: "perform-subtract")
.Then("ShowResult");
@switch
.When("multiply")
.Then<Multiply>(SetupOperation, id: "perform-multiply")
.Then("ShowResult");
@switch
.When("divide")
.Then<Divide>(SetupOperation, id: "perform-divide")
.Then("ShowResult");
},
"inspect-selected-operation"
)
.Add<WriteLine>(x => x.TextExpression = new JavaScriptExpression<string>("`Result: ${result}`")).WithId("ShowResult")
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Try again? (y/n)"))
.Then<ReadLine>().WithId("TryAgain")
.Then<SetVariable>(x =>
{
x.VariableName = "Exit";
x.ValueExpression = new JavaScriptExpression<bool>("TryAgain.Input !== 'y' && TryAgain.Input !== 'Y'");
})
.Then(whileActivity);
}).WithDisplayName("While")
.Then<WriteLine>(x => x.TextExpression = new LiteralExpression("Bye!"));
}
private void SetupOperation(ArithmeticOperation operation)

View file

@ -1,4 +1,5 @@
using System;
using System.Linq;
using System.Threading.Tasks;
using Elsa.Activities.Console.Extensions;
using Elsa.Services;
@ -16,6 +17,7 @@ namespace Sample04
{
// Setup a service collection.
var services = new ServiceCollection()
.AddSingleton(Console.In)
.AddWorkflows()
.AddConsoleActivities()
.AddActivity<Sum>()
@ -35,7 +37,8 @@ namespace Sample04
Console.WriteLine("Workflow has ended. Here are the activities that have executed:");
foreach (var logEntry in workflow.ExecutionLog)
{
Console.WriteLine("{0}: {1}", logEntry.Timestamp, logEntry.ActivityId);
var activity = workflow.Definition.Activities.First(x => x.Id == logEntry.ActivityId);
Console.WriteLine("{0}: {1}", logEntry.Timestamp, activity.DisplayName);
}
Console.ReadLine();
}

View file

@ -12,7 +12,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Hosting" Version="3.1.0-preview1.19506.1" />
</ItemGroup>
</Project>

View file

@ -5,7 +5,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="MassTransit.AspNetCore" Version="5.5.2" />
<PackageReference Include="MassTransit.AspNetCore" Version="6.0.0-develop.2231" />
</ItemGroup>
<ItemGroup>

View file

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>netcoreapp2.2</TargetFramework>
<TargetFramework>netcoreapp3.0</TargetFramework>
<LangVersion>latest</LangVersion>
</PropertyGroup>
@ -16,8 +16,8 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="2.2.6" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.0.0" />
</ItemGroup>
</Project>