Merge pull request #4856 from elsa-workflows/issue(4849)
Remove StringObjectDictionaryConverter and update JintJavaScriptEvaluator
This commit is contained in:
commit
a53c792c4b
|
|
@ -19,7 +19,7 @@
|
|||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
|
||||
<PackageReference Include="Jint" Version="3.0.0-beta-2057" />
|
||||
<PackageReference Include="Jint" Version="3.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
|||
39
src/modules/Elsa.JavaScript/Helpers/ObjectArrayHelper.cs
Normal file
39
src/modules/Elsa.JavaScript/Helpers/ObjectArrayHelper.cs
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
using System.Collections;
|
||||
|
||||
namespace Elsa.JavaScript.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Contains helper methods for working with object arrays.
|
||||
/// </summary>
|
||||
public static class ObjectArrayHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// Determines if the specified object is an array-like CLR collection.
|
||||
/// </summary>
|
||||
public static bool DetermineIfObjectIsArrayLikeClrCollection(Type type)
|
||||
{
|
||||
var isDictionary = typeof(IDictionary).IsAssignableFrom(type);
|
||||
|
||||
if (isDictionary)
|
||||
return false;
|
||||
|
||||
if (typeof(ICollection).IsAssignableFrom(type))
|
||||
return true;
|
||||
|
||||
foreach (var interfaceType in type.GetInterfaces())
|
||||
{
|
||||
if (!interfaceType.IsGenericType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if (interfaceType.GetGenericTypeDefinition() == typeof(IReadOnlyCollection<>)
|
||||
|| interfaceType.GetGenericTypeDefinition() == typeof(ICollection<>))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,40 +0,0 @@
|
|||
using System.Collections;
|
||||
|
||||
namespace Elsa.JavaScript.Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// Contains methods for converting dictionaries with string keys and object values by replacing IList fields with Array fields.
|
||||
/// </summary>
|
||||
public static class StringObjectDictionaryConverter
|
||||
{
|
||||
/// <summary>
|
||||
/// Recursively converts all IList fields of an ExpandoObject to Array fields.
|
||||
/// This allows JS expressions to properly use Array methods on lists, such as .length, filter, etc.
|
||||
/// </summary>
|
||||
public static object? ConvertListsToArray(object? value)
|
||||
{
|
||||
if (value is not IDictionary<string, object> dictionary)
|
||||
return value;
|
||||
|
||||
// Copy the dictionary to avoid modifying the original.
|
||||
dictionary = new Dictionary<string, object>(dictionary);
|
||||
var keys = dictionary.Keys.ToList();
|
||||
foreach (var key in keys)
|
||||
{
|
||||
if (dictionary[key] is IList && dictionary[key].GetType().IsGenericType)
|
||||
{
|
||||
var list = (IList)dictionary[key];
|
||||
var elementType = dictionary[key].GetType().GetGenericArguments()[0];
|
||||
var array = Array.CreateInstance(elementType, list.Count);
|
||||
list.CopyTo(array, 0);
|
||||
dictionary[key] = array;
|
||||
}
|
||||
else
|
||||
{
|
||||
ConvertListsToArray(dictionary[key]);
|
||||
}
|
||||
}
|
||||
|
||||
return dictionary;
|
||||
}
|
||||
}
|
||||
|
|
@ -11,6 +11,7 @@ using Elsa.JavaScript.Options;
|
|||
using Elsa.Mediator.Contracts;
|
||||
using Humanizer;
|
||||
using Jint;
|
||||
using Jint.Runtime.Interop;
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
// ReSharper disable ConvertClosureToMethodGroup
|
||||
|
|
@ -56,6 +57,17 @@ public class JintJavaScriptEvaluator : IJavaScriptEvaluator
|
|||
{
|
||||
if (_jintOptions.AllowClrAccess)
|
||||
opts.AllowClr();
|
||||
|
||||
// Wrap objects in ObjectWrapper instances and set their prototype to Array.prototype if they are array-like.
|
||||
opts.SetWrapObjectHandler((engine, target, type) =>
|
||||
{
|
||||
var instance = new ObjectWrapper(engine, target);
|
||||
|
||||
if (ObjectArrayHelper.DetermineIfObjectIsArrayLikeClrCollection(target.GetType()))
|
||||
instance.Prototype = engine.Intrinsics.Array.PrototypeObject;
|
||||
|
||||
return instance;
|
||||
});
|
||||
});
|
||||
|
||||
configureEngine?.Invoke(engine);
|
||||
|
|
@ -122,7 +134,7 @@ public class JintJavaScriptEvaluator : IJavaScriptEvaluator
|
|||
var inputs = context.GetWorkflowInputs();
|
||||
|
||||
foreach (var input in inputs)
|
||||
engine.SetValue($"get{input.Name}", (Func<object?>)(() => StringObjectDictionaryConverter.ConvertListsToArray(input.Value)));
|
||||
engine.SetValue($"get{input.Name}", (Func<object?>)(() => input.Value));
|
||||
}
|
||||
|
||||
private static void CreateVariableAccessors(Engine engine, ExpressionExecutionContext context)
|
||||
|
|
|
|||
|
|
@ -0,0 +1,57 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using Elsa.JavaScript.Helpers;
|
||||
using Jint;
|
||||
using Jint.Runtime.Interop;
|
||||
using Xunit;
|
||||
|
||||
namespace Elsa.IntegrationTests.Scenarios.JavaScriptListAndArray;
|
||||
|
||||
/// <summary>
|
||||
/// Contains test cases for the functionality of the Engine class to enable list and array-like objects to be used in JavaScript as arrays.
|
||||
/// </summary>
|
||||
public class Tests
|
||||
{
|
||||
private readonly Engine _engine;
|
||||
|
||||
public Tests()
|
||||
{
|
||||
_engine = new Engine(cfg => cfg
|
||||
.SetWrapObjectHandler((engine, target, type) =>
|
||||
{
|
||||
var instance = new ObjectWrapper(engine, target);
|
||||
|
||||
if (ObjectArrayHelper.DetermineIfObjectIsArrayLikeClrCollection(target.GetType()))
|
||||
instance.Prototype = engine.Intrinsics.Array.PrototypeObject;
|
||||
|
||||
return instance;
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
[Fact(DisplayName = "Can access list properties as arrays")]
|
||||
public void Test1()
|
||||
{
|
||||
var person = new ExpandoObject();
|
||||
|
||||
person.TryAdd("name", "John");
|
||||
person.TryAdd("age", 12);
|
||||
|
||||
var languages = new List<object>
|
||||
{
|
||||
"English",
|
||||
"French"
|
||||
};
|
||||
|
||||
person.TryAdd("languages", languages);
|
||||
|
||||
var obj = new ExpandoObject();
|
||||
obj.TryAdd("persons", new List<object> { person });
|
||||
_engine.SetValue("o", obj);
|
||||
|
||||
var name = _engine.Evaluate("o.persons.filter(x => x.age == 12)[0].name").ToString();
|
||||
var language = _engine.Evaluate("o.persons[0].languages.filter(x => x == 'English')[0]").ToString();
|
||||
Assert.Equal("John", name);
|
||||
Assert.Equal("English", language);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue