< Summary

Information
Class: Elsa.Expressions.JavaScript.Handlers.ConfigureEngineWithCommonFunctions
Assembly: Elsa.Expressions.JavaScript
File(s): /home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Expressions.JavaScript/Handlers/ConfigureEngineWithCommonFunctions.cs
Line coverage
92%
Covered lines: 46
Uncovered lines: 4
Coverable lines: 50
Total lines: 105
Line coverage: 92%
Branch coverage
100%
Covered branches: 32
Total branches: 32
Branch coverage: 100%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.ctor(...)100%11100%
HandleAsync(...)100%3232100%
Serialize(...)100%11100%
CreateJsonSerializerOptions()100%11100%
StreamToBytes(...)100%210%

File(s)

/home/runner/work/elsa-core/elsa-core/src/modules/Elsa.Expressions.JavaScript/Handlers/ConfigureEngineWithCommonFunctions.cs

#LineLine coverage
 1using System.Text;
 2using System.Text.Encodings.Web;
 3using System.Text.Json;
 4using System.Text.Json.Serialization;
 5using System.Text.RegularExpressions;
 6using System.Text.Unicode;
 7using Elsa.Extensions;
 8using Elsa.Expressions.JavaScript.Helpers;
 9using Elsa.Expressions.JavaScript.Notifications;
 10using Elsa.Expressions.JavaScript.Options;
 11using Elsa.Mediator.Contracts;
 12using JetBrains.Annotations;
 13using Jint;
 14using Jint.Native;
 15using Jint.Runtime.Descriptors;
 16using Microsoft.Extensions.Options;
 17
 18namespace Elsa.Expressions.JavaScript.Handlers;
 19
 20/// <summary>
 21/// A handler that configures the Jint engine with common functions.
 22/// </summary>
 23[UsedImplicitly]
 95424public class ConfigureEngineWithCommonFunctions(IOptions<JintOptions> options) : INotificationHandler<EvaluatingJavaScri
 25{
 95426    private readonly JsonSerializerOptions _jsonSerializerOptions = CreateJsonSerializerOptions();
 27
 28    /// <inheritdoc />
 29    public Task HandleAsync(EvaluatingJavaScript notification, CancellationToken cancellationToken)
 30    {
 30531        var engine = notification.Engine;
 30532        var context = notification.Context;
 33
 34        // Add common functions. Each one is installed as a global whose value is produced the first time a script
 35        // reads the name, so a fresh engine no longer builds twenty-seven interop function wrappers for an
 36        // expression that calls none of them. The CLR delegate is created inside the factory for the same reason.
 30837        AddFunction("getWorkflowDefinitionId", e => JsValue.FromObject(e, (Func<string>)(() => context.GetWorkflowExecut
 30838        AddFunction("getWorkflowDefinitionVersionId", e => JsValue.FromObject(e, (Func<string>)(() => context.GetWorkflo
 30839        AddFunction("getWorkflowDefinitionVersion", e => JsValue.FromObject(e, (Func<int>)(() => context.GetWorkflowExec
 30840        AddFunction("getWorkflowInstanceId", e => JsValue.FromObject(e, (Func<string>)(() => context.GetActivityExecutio
 30841        AddFunction("setCorrelationId", e => JsValue.FromObject(e, (Action<string?>)(value => context.GetActivityExecuti
 31042        AddFunction("getCorrelationId", e => JsValue.FromObject(e, (Func<string?>)(() => context.GetActivityExecutionCon
 30843        AddFunction("setWorkflowInstanceName", e => JsValue.FromObject(e, (Action<string?>)(value => context.GetWorkflow
 31044        AddFunction("getWorkflowInstanceName", e => JsValue.FromObject(e, (Func<string?>)(() => context.GetWorkflowExecu
 30945        AddFunction("setVariable", e => JsValue.FromObject(e, (Action<string, object>)((name, value) =>
 30946        {
 247            engine.SyncVariablesContainer(options, name, value);
 248            context.SetVariableInScope(name, value);
 31149        })));
 45150        AddFunction("getVariable", e => JsValue.FromObject(e, (Func<string, object?>)(name => context.GetVariableInScope
 30751        AddFunction("getInput", e => JsValue.FromObject(e, (Func<string, object?>)(name => context.GetInput(name))));
 30752        AddFunction("getOutputFrom", e => JsValue.FromObject(e, (Func<string, string?, object?>)((activityIdName, output
 30753        AddFunction("getLastResult", e => JsValue.FromObject(e, (Func<object?>)(() => context.GetLastResult())));
 31354        AddFunction("isNullOrWhiteSpace", e => JsValue.FromObject(e, (Func<string, bool>)(value => string.IsNullOrWhiteS
 31455        AddFunction("isNullOrEmpty", e => JsValue.FromObject(e, (Func<string, bool>)(value => string.IsNullOrEmpty(value
 31656        AddFunction("toJson", e => JsValue.FromObject(e, (Func<object, string>)(value => Serialize(value))));
 30957        AddFunction("parseGuid", e => JsValue.FromObject(e, (Func<string, Guid>)(value => Guid.Parse(value))));
 31358        AddFunction("newGuid", e => JsValue.FromObject(e, (Func<Guid>)(() => Guid.NewGuid())));
 31059        AddFunction("newGuidString", e => JsValue.FromObject(e, (Func<string>)(() => Guid.NewGuid().ToString())));
 31060        AddFunction("newShortGuid", e => JsValue.FromObject(e, (Func<string>)(() => Regex.Replace(Convert.ToBase64String
 31261        AddFunction("bytesToString", e => JsValue.FromObject(e, (Func<byte[], string>)(value => Encoding.UTF8.GetString(
 31062        AddFunction("bytesFromString", e => JsValue.FromObject(e, (Func<string, byte[]>)(value => Encoding.UTF8.GetBytes
 31263        AddFunction("bytesToBase64", e => JsValue.FromObject(e, (Func<byte[], string>)(value => Convert.ToBase64String(v
 31064        AddFunction("bytesFromBase64", e => JsValue.FromObject(e, (Func<string, byte[]>)(value => Convert.FromBase64Stri
 31265        AddFunction("stringToBase64", e => JsValue.FromObject(e, (Func<string, string>)(value => Convert.ToBase64String(
 31066        AddFunction("stringFromBase64", e => JsValue.FromObject(e, (Func<string, string>)(value => Encoding.UTF8.GetStri
 30767        AddFunction("streamToBytes", e => JsValue.FromObject(e, (Func<Stream, byte[]>)(value => StreamToBytes(value))));
 30668        AddFunction("streamToBase64", e => JsValue.FromObject(e, (Func<Stream, string>)(value => Convert.ToBase64String(
 69
 70        // Deprecated, use newGuidString instead.
 30871        AddFunction("getGuidString", e => JsValue.FromObject(e, (Func<string>)(() => Guid.NewGuid().ToString())));
 72
 73        // Deprecated, use newShortGuid instead.
 30974        AddFunction("getShortGuid", e => JsValue.FromObject(e, (Func<string>)(() => Regex.Replace(Convert.ToBase64String
 30575        return Task.CompletedTask;
 76
 77        // AddLazyGlobal installs the property itself eagerly and defers only its value, so `in`, `hasOwnProperty`
 78        // and Object.getOwnPropertyNames see every function immediately and the factory runs at most once, on the
 79        // first read. NonEnumerable is the flag engine.SetValue(string, Delegate) applies, so the attributes — and
 80        // therefore what Object.keys(globalThis) reports — are exactly what they were.
 81        void AddFunction(string name, Func<Engine, JsValue> valueFactory) => engine.Advanced.AddLazyGlobal(name, valueFa
 82    }
 83
 84    private string Serialize(object value)
 85    {
 586        return JsonSerializer.Serialize(value, _jsonSerializerOptions);
 87    }
 88
 89    private static JsonSerializerOptions CreateJsonSerializerOptions()
 90    {
 95491        var options = new JsonSerializerOptions
 95492        {
 95493            Encoder = JavaScriptEncoder.Create(UnicodeRanges.All)
 95494        };
 95495        options.Converters.Add(new JsonStringEnumConverter());
 95496        return options;
 97    }
 98
 99    private byte[] StreamToBytes(Stream stream)
 100    {
 0101        using var memoryStream = new MemoryStream();
 0102        stream.CopyTo(memoryStream);
 0103        return memoryStream.ToArray();
 0104    }
 105}