Skip to content

Commit

Permalink
Merge branch 'v3.0.5'
Browse files Browse the repository at this point in the history
  • Loading branch information
sfmskywalker committed Feb 1, 2024
2 parents 31e927b + bbd3819 commit 5436d95
Show file tree
Hide file tree
Showing 7 changed files with 151 additions and 15 deletions.
15 changes: 11 additions & 4 deletions src/bundles/Elsa.Server.Web/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@
const bool useProtoActor = false;
const bool useHangfire = false;
const bool useQuartz = true;
const bool useMassTransit = true;
const bool useMassTransitAzureServiceBus = false;
const bool useMassTransit = false;
const bool useMassTransitAzureServiceBus = true;
const bool useMassTransitRabbitMq = false;

var builder = WebApplication.CreateBuilder(args);
Expand Down Expand Up @@ -133,7 +133,10 @@
});
}
runtime.UseMassTransitDispatcher();
if(useMassTransit)
{
runtime.UseMassTransitDispatcher();
}
runtime.WorkflowInboxCleanupOptions = options => configuration.GetSection("Runtime:WorkflowInboxCleanup").Bind(options);
})
.UseEnvironments(environments => environments.EnvironmentsOptions = options => configuration.GetSection("Environments").Bind(options))
Expand Down Expand Up @@ -186,7 +189,11 @@
ef.UseSqlite(sqliteConnectionString);
});
alterations.UseMassTransitDispatcher();
if (useMassTransit)
{
alterations.UseMassTransitDispatcher();
}
})
.UseWorkflowContexts();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public interface IJavaScriptEvaluator
string expression,
Type returnType,
ExpressionExecutionContext context,
ExpressionEvaluatorOptions options,
ExpressionEvaluatorOptions? options = default,
Action<Engine>? configureEngine = default,
CancellationToken cancellationToken = default);
}
2 changes: 1 addition & 1 deletion src/modules/Elsa.JavaScript/Elsa.JavaScript.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -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 changes: 39 additions & 0 deletions src/modules/Elsa.JavaScript/Helpers/ObjectArrayHelper.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
23 changes: 19 additions & 4 deletions src/modules/Elsa.JavaScript/Services/JintJavaScriptEvaluator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.JavaScript.Contracts;
using Elsa.JavaScript.Helpers;
using Elsa.JavaScript.Notifications;
using Elsa.JavaScript.Options;
using Elsa.Mediator.Contracts;
using Humanizer;
using Jint;
using Jint.Runtime.Interop;
using Microsoft.Extensions.Options;

// ReSharper disable ConvertClosureToMethodGroup
Expand Down Expand Up @@ -37,7 +39,7 @@ public JintJavaScriptEvaluator(INotificationSender mediator, IOptions<JintOption
public async Task<object?> EvaluateAsync(string expression,
Type returnType,
ExpressionExecutionContext context,
ExpressionEvaluatorOptions options,
ExpressionEvaluatorOptions? options = default,
Action<Engine>? configureEngine = default,
CancellationToken cancellationToken = default)
{
Expand All @@ -47,12 +49,25 @@ public JintJavaScriptEvaluator(INotificationSender mediator, IOptions<JintOption
return result.ConvertTo(returnType);
}

private async Task<Engine> GetConfiguredEngine(Action<Engine>? configureEngine, ExpressionExecutionContext context, ExpressionEvaluatorOptions options, CancellationToken cancellationToken)
private async Task<Engine> GetConfiguredEngine(Action<Engine>? configureEngine, ExpressionExecutionContext context, ExpressionEvaluatorOptions? options, CancellationToken cancellationToken)
{
options ??= new ExpressionEvaluatorOptions();

var engine = new Engine(opts =>
{
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);
Expand All @@ -76,11 +91,11 @@ private async Task<Engine> GetConfiguredEngine(Action<Engine>? configureEngine,

// Create output getters for each activity.
CreateActivityOutputAccessors(engine, context);

// Create argument getters for each argument.
foreach (var argument in options.Arguments)
engine.SetValue($"get{argument.Key}", (Func<object?>)(() => argument.Value));

// Add common functions.
engine.SetValue("isNullOrWhiteSpace", (Func<string, bool>)(value => string.IsNullOrWhiteSpace(value)));
engine.SetValue("isNullOrEmpty", (Func<string, bool>)(value => string.IsNullOrEmpty(value)));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,10 @@ where v.TryGet(context, out _)
select v;

var variable = q.FirstOrDefault();
if(variable != null)

if (variable != null)
variable.Set(context, value);

if (variable == null)
CreateVariable(context, variableName, value);
}
Expand Down Expand Up @@ -362,7 +362,7 @@ public static IEnumerable<Variable> EnumerateVariablesInScope(this ExpressionExe
var input = workflowExecutionContext.Input;
return input.TryGetValue(name, out var value) ? value : default;
}

/// <summary>
/// Returns the value of the specified input.
/// </summary>
Expand Down Expand Up @@ -393,7 +393,9 @@ public static IEnumerable<Variable> EnumerateVariablesInScope(this ExpressionExe
/// </summary>
public static IEnumerable<ActivityOutputs> GetActivityOutputs(this ExpressionExecutionContext context)
{
var activityExecutionContext = context.GetActivityExecutionContext();
if (!context.TryGetActivityExecutionContext(out var activityExecutionContext))
yield break;

var useActivityName = activityExecutionContext.WorkflowExecutionContext.Workflow.CreatedWithModernTooling();
var activitiesWithOutputs = activityExecutionContext.GetActivitiesWithOutputs();

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
using System;
using System.Collections.Generic;
using System.Dynamic;
using System.Threading.Tasks;
using Elsa.Expressions.Models;
using Elsa.Extensions;
using Elsa.JavaScript.Contracts;
using Elsa.Testing.Shared;
using Jint;
using Microsoft.Extensions.DependencyInjection;
using Xunit;
using Xunit.Abstractions;

namespace Elsa.IntegrationTests.Scenarios.JavaScriptListsAndArrays;

public class Tests
{
private readonly IServiceProvider _services;
private readonly IJavaScriptEvaluator _evaluator;
private readonly ExpressionExecutionContext _expressionContext;

public Tests(ITestOutputHelper testOutputHelper)
{
var testOutputHelper1 = testOutputHelper ?? throw new ArgumentNullException(nameof(testOutputHelper));
_services = new TestApplicationBuilder(testOutputHelper1).Build();
_evaluator = _services.GetRequiredService<IJavaScriptEvaluator>();
_expressionContext = new ExpressionExecutionContext(_services, new MemoryRegister());
}

[Fact(DisplayName = "Workflow inputs containing .NET lists on dynamic objects are converted to arrays for use in JavaScript.")]
public async Task Test1()
{
dynamic dynamicObject = new ExpandoObject();

dynamicObject.List = new List<string> { "a", "b", "c" };
var script = "getObj().List.filter(x => x === 'b').length === 1";
_expressionContext.SetVariable("obj", (object)dynamicObject);
var result = await _evaluator.EvaluateAsync(script, typeof(bool), _expressionContext);

Assert.True((bool)result!);
}

[Fact(DisplayName = "Can access list properties as arrays")]
public async Task Test2()
{
var person = new ExpandoObject();
var magicNumbers = new[]{42, 43, 44};
var order1 = new ExpandoObject();

order1.TryAdd("magicNumbers", magicNumbers);
person.TryAdd("orders", new []{ order1 });
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 });

var name = await _evaluator.EvaluateAsync("o.persons.filter(x => x.age == 12)[0].name", typeof(string), _expressionContext, configureEngine: engine => engine.SetValue("o", obj));
var language = await _evaluator.EvaluateAsync("o.persons[0].languages.filter(x => x == 'English')[0]", typeof(string), _expressionContext, configureEngine: engine => engine.SetValue("o", obj));
var magicNumber = await _evaluator.EvaluateAsync("o.persons[0].orders[0].magicNumbers[1]", typeof(int), _expressionContext, configureEngine: engine => engine.SetValue("o", obj));
Assert.Equal("John", name);
Assert.Equal("English", language);
Assert.Equal(43, magicNumber);
}
}

0 comments on commit 5436d95

Please sign in to comment.