Update variable storage types and fix JavaScript handling

Changed storage driver types for 'Products' and 'Product' variables to `WorkflowInstanceStorageDriver`. Refactored JavaScript handling to properly process variables and exclude input names from overwriting. Re-enabled a previously skipped array sorting test.
This commit is contained in:
Sipke Schoorstra 2024-09-16 01:49:53 -07:00
parent 6b9079d61c
commit 290eb9fe19
4 changed files with 39 additions and 15 deletions

View file

@ -1,9 +1,11 @@
using System.Dynamic;
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.JavaScript.Helpers;
using Elsa.JavaScript.Models;
using Elsa.JavaScript.Notifications;
using Elsa.Mediator.Contracts;
using Elsa.Workflows.Activities;
using JetBrains.Annotations;
using Jint;
using Jint.Native;
@ -20,7 +22,7 @@ public class ConfigureEngineWithVariables : INotificationHandler<EvaluatingJavaS
CopyVariablesIntoEngine(notification);
return Task.CompletedTask;
}
public Task HandleAsync(EvaluatedJavaScript notification, CancellationToken cancellationToken)
{
CopyVariablesIntoWorkflowExecutionContext(notification);
@ -32,28 +34,50 @@ public class ConfigureEngineWithVariables : INotificationHandler<EvaluatingJavaS
var context = notification.Context;
var engine = notification.Engine;
var variablesContainer = (IDictionary<string, object?>)engine.GetValue("variables").ToObject()!;
var inputNames = GetInputNames(context).Distinct().ToList();
foreach (var (variableName, variableValue) in variablesContainer)
{
var processedValue = variableValue is JsObject jsObject ? jsObject.ToObject() : variableValue;
if (inputNames.Contains(variableName))
continue;
var processedValue = variableValue is JsObject jsValue ? jsValue.ToObject() : variableValue ?? context.GetVariableInScope(variableName);
context.SetVariable(variableName, processedValue);
}
}
private IEnumerable<string> GetInputNames(ExpressionExecutionContext context)
{
var activityExecutionContext = context.TryGetActivityExecutionContext(out var aec) ? aec : null;
while (activityExecutionContext != null)
{
if (activityExecutionContext.Activity is Workflow workflow)
{
var inputDefinitions = workflow.Inputs;
foreach (var inputDefinition in inputDefinitions)
yield return inputDefinition.Name;
}
activityExecutionContext = activityExecutionContext.ParentActivityExecutionContext;
}
}
private void CopyVariablesIntoEngine(EvaluatingJavaScript notification)
{
var engine = notification.Engine;
var context = notification.Context;
var variableNames = context.GetVariableNamesInScope().ToList();
var variablesContainer = (IDictionary<string, object?>)new ExpandoObject();
foreach (var variableName in variableNames)
{
var variableValue = context.GetVariableInScope(variableName);
variableValue = ProcessVariableValue(engine, variableValue);
variablesContainer[variableName] = variableValue;
}
engine.SetValue("variables", variablesContainer);
}
@ -61,8 +85,8 @@ public class ConfigureEngineWithVariables : INotificationHandler<EvaluatingJavaS
{
if (variableValue == null)
return null;
if(variableValue is not ExpandoObject expandoObject)
if (variableValue is not ExpandoObject expandoObject)
return variableValue;
return ObjectConverterHelper.ConvertToJsObject(engine, expandoObject);

View file

@ -44,9 +44,6 @@ public class ParallelForEach<T> : Activity
var tags = new List<Guid>();
var currentIndex = 0;
SetTagList(context, ScheduledTagsProperty, tags);
SetTagList(context, CompletedTagsProperty, new List<Guid>());
await foreach (var item in items)
{
// For each item, declare a new variable for the work to be scheduled.
@ -65,6 +62,9 @@ public class ParallelForEach<T> : Activity
await context.ScheduleActivityAsync(Body, OnChildCompleted, tag, variables);
}
SetTagList(context, ScheduledTagsProperty, tags);
SetTagList(context, CompletedTagsProperty, new List<Guid>());
// If there were no items, we're done.
if (tags.Count == 0)
await context.CompleteActivityAsync();

View file

@ -13,8 +13,8 @@ public class JavascriptAndLiquidWorkflow : WorkflowBase
{
protected override void Build(IWorkflowBuilder builder)
{
var products = new Variable<object> { Name = "Products", StorageDriverType = typeof(WorkflowStorageDriver) };
var product = new Variable<object> { Name = "Product", StorageDriverType = typeof(WorkflowStorageDriver) };
var products = new Variable<object> { Name = "Products", StorageDriverType = typeof(WorkflowInstanceStorageDriver) };
var product = new Variable<object> { Name = "Product", StorageDriverType = typeof(WorkflowInstanceStorageDriver) };
builder.Root = new Sequence
{
@ -31,7 +31,7 @@ public class JavascriptAndLiquidWorkflow : WorkflowBase
new WriteLine(new Expression("Liquid", "Second product id: {{ Variables.Products[1].id }}")),
new RunJavaScript
{
Script = new(@"setProduct({""id"":2, ""price"":10})")
Script = new(@"variables.Product = {""id"":2, ""price"":10}")
},
new WriteLine(new Expression("Liquid", "Single product id: {{ Variables.Product.id }}")),
new WriteLine(new Expression("Liquid", "Single product as json: {{ Variables.Product | json }}")),

View file

@ -79,7 +79,7 @@ public class Tests
Assert.Equal(43, magicNumber);
}
[Theory(DisplayName = "Can sort array and list properties as mutable arrays", Skip = "Not supported yet. See https://github.com/elsa-workflows/elsa-core/issues/5912")]
[Theory(DisplayName = "Can sort array and list properties as mutable arrays")]
[MemberData(nameof(ArraySortEnumerableData))]
// See also:
// - https://github.com/sebastienros/jint/issues/1942
@ -89,7 +89,7 @@ public class Tests
dynamic dynamicObject = new ExpandoObject();
dynamicObject.Items = collection;
var script = """
const model = getModel();
const model = variables.Model;
model.Items.sort((a, b) => a - b);
return model;
""";