From 341eeb471f3dc4e79896ac1541027e78d01f7a14 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 28 Nov 2024 19:23:13 +0100 Subject: [PATCH] Implement Enum to String Conversion for JavaScript Engine Add a new `EnumToStringConverter` to convert enums to strings within the JavaScript engine, enhancing object conversion capabilities. Update `DefaultActivityExecutionMapper` with logging functionality for error handling. Modify constructors and improve code styling across multiple files. --- .../GetWorkflowJavaScriptDefinitionRequest.cs | 27 ++++++------------- ...unJavaScriptFunctionsDefinitionProvider.cs | 2 +- .../ObjectConverters/EnumToStringConverter.cs | 21 +++++++++++++++ .../Services/JintJavaScriptEvaluator.cs | 12 ++++----- .../DefaultActivityExecutionMapper.cs | 20 +++++++++++--- 5 files changed, 53 insertions(+), 29 deletions(-) create mode 100644 src/modules/Elsa.JavaScript/ObjectConverters/EnumToStringConverter.cs diff --git a/src/clients/Elsa.Api.Client/Resources/Scripting/Requests/GetWorkflowJavaScriptDefinitionRequest.cs b/src/clients/Elsa.Api.Client/Resources/Scripting/Requests/GetWorkflowJavaScriptDefinitionRequest.cs index a344a6571..9b8900628 100644 --- a/src/clients/Elsa.Api.Client/Resources/Scripting/Requests/GetWorkflowJavaScriptDefinitionRequest.cs +++ b/src/clients/Elsa.Api.Client/Resources/Scripting/Requests/GetWorkflowJavaScriptDefinitionRequest.cs @@ -3,31 +3,20 @@ namespace Elsa.Api.Client.Resources.Scripting.Requests; /// /// A request to retrieve the type definitions /// -public class GetWorkflowJavaScriptDefinitionRequest +public class GetWorkflowJavaScriptDefinitionRequest(string workflowDefinitionId, string? activityTypeName, string? propertyName) { - /// - /// Initialize new type definition request. - /// - /// Id of workflow definition to retrieve type definitions for. - /// Type name of the activity to retrieve type definitions for. - /// Name of the property to retrieve type definitions for. - public GetWorkflowJavaScriptDefinitionRequest(string workflowDefinitionId, string activityTypeName, string propertyName) - { - WorkflowDefinitionId = workflowDefinitionId; - ActivityTypeName = activityTypeName; - PropertyName = propertyName; - } - /// /// Gets or sets workflow definition to retrieve type definitions for. /// - public string WorkflowDefinitionId { get; set; } + public string WorkflowDefinitionId { get; set; } = workflowDefinitionId; + /// - /// Gets or sets type name of the activity to retrieve type definitions for. + /// Gets or sets the type name of the activity to retrieve type definitions for. /// - public string ActivityTypeName { get; set; } + public string? ActivityTypeName { get; set; } = activityTypeName; + /// - /// Gets or sets name of the property to retrieve type definitions for. + /// Gets or sets the name of the property to retrieve type definitions for. /// - public string PropertyName { get; set; } + public string? PropertyName { get; set; } = propertyName; } diff --git a/src/modules/Elsa.JavaScript/Activities/RunJavaScript/RunJavaScriptFunctionsDefinitionProvider.cs b/src/modules/Elsa.JavaScript/Activities/RunJavaScript/RunJavaScriptFunctionsDefinitionProvider.cs index 20cbb2b9d..fd99f50ce 100644 --- a/src/modules/Elsa.JavaScript/Activities/RunJavaScript/RunJavaScriptFunctionsDefinitionProvider.cs +++ b/src/modules/Elsa.JavaScript/Activities/RunJavaScript/RunJavaScriptFunctionsDefinitionProvider.cs @@ -10,7 +10,7 @@ namespace Elsa.JavaScript.Activities; /// Produces s for common functions. /// [UsedImplicitly] -internal class RunJavaScriptFunctionsDefinitionProvider() : FunctionDefinitionProvider +internal class RunJavaScriptFunctionsDefinitionProvider : FunctionDefinitionProvider { protected override IEnumerable GetFunctionDefinitions(TypeDefinitionContext context) { diff --git a/src/modules/Elsa.JavaScript/ObjectConverters/EnumToStringConverter.cs b/src/modules/Elsa.JavaScript/ObjectConverters/EnumToStringConverter.cs new file mode 100644 index 000000000..a4b4f2f78 --- /dev/null +++ b/src/modules/Elsa.JavaScript/ObjectConverters/EnumToStringConverter.cs @@ -0,0 +1,21 @@ +using System.Diagnostics.CodeAnalysis; +using Jint; +using Jint.Native; +using Jint.Runtime.Interop; + +namespace Elsa.JavaScript.ObjectConverters; + +internal class EnumToStringConverter : IObjectConverter +{ + public bool TryConvert(Engine engine, object value, [NotNullWhen(true)] out JsValue? result) + { + if (value is Enum) + { + result = value.ToString(); + return true; + } + + result = JsValue.Null; + return false; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.JavaScript/Services/JintJavaScriptEvaluator.cs b/src/modules/Elsa.JavaScript/Services/JintJavaScriptEvaluator.cs index 9541382d4..0f16cab66 100644 --- a/src/modules/Elsa.JavaScript/Services/JintJavaScriptEvaluator.cs +++ b/src/modules/Elsa.JavaScript/Services/JintJavaScriptEvaluator.cs @@ -50,7 +50,7 @@ public class JintJavaScriptEvaluator(IConfiguration configuration, INotification var engineOptions = new Jint.Options { - ExperimentalFeatures = ExperimentalFeature.TaskInterop + ExperimentalFeatures = ExperimentalFeature.TaskInterop, }; ConfigureClrAccess(engineOptions); @@ -59,14 +59,14 @@ public class JintJavaScriptEvaluator(IConfiguration configuration, INotification await mediator.SendAsync(new CreatingJavaScriptEngine(engineOptions, context), cancellationToken); _jintOptions.ConfigureEngineOptionsCallback(engineOptions, context); - + var engine = new Engine(engineOptions); configureEngine?.Invoke(engine); ConfigureArgumentGetters(engine, options); ConfigureConfigurationAccess(engine); _jintOptions.ConfigureEngineCallback(engine, context); - + return engine; } @@ -75,7 +75,7 @@ public class JintJavaScriptEvaluator(IConfiguration configuration, INotification if (_jintOptions.AllowClrAccess) options.AllowClr(); } - + private void ConfigureObjectWrapper(Jint.Options options) { options.SetWrapObjectHandler((engine, target, type) => @@ -91,7 +91,7 @@ public class JintJavaScriptEvaluator(IConfiguration configuration, INotification private void ConfigureObjectConverters(Jint.Options options) { - options.Interop.ObjectConverters.Add(new ByteArrayConverter()); + options.Interop.ObjectConverters.AddRange([new ByteArrayConverter(), new EnumToStringConverter()]); } private void ConfigureArgumentGetters(Engine engine, ExpressionEvaluatorOptions options) @@ -99,7 +99,7 @@ public class JintJavaScriptEvaluator(IConfiguration configuration, INotification foreach (var argument in options.Arguments) engine.SetValue($"get{argument.Key}", (Func)(() => argument.Value)); } - + private void ConfigureConfigurationAccess(Engine engine) { if (_jintOptions.AllowConfigurationAccess) diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs index c04958358..a2ba663f9 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs @@ -12,6 +12,7 @@ using Elsa.Workflows.Runtime.Entities; using Elsa.Workflows.Serialization.Converters; using Elsa.Workflows.State; using Humanizer; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Elsa.Workflows.Runtime; @@ -22,16 +23,19 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper private readonly JsonSerializerOptions _logPersistenceConfigSerializerOptions; private readonly IOptions _options; private readonly IExpressionEvaluator _expressionEvaluator; + private readonly ILogger _logger; private readonly IDictionary _logPersistenceStrategies; public DefaultActivityExecutionMapper( IOptions options, ILogPersistenceStrategyService logPersistenceStrategyService, IExpressionEvaluator expressionEvaluator, - IExpressionDescriptorRegistry expressionDescriptorRegistry) + IExpressionDescriptorRegistry expressionDescriptorRegistry, + ILogger logger) { _options = options; _expressionEvaluator = expressionEvaluator; + _logger = logger; _logPersistenceStrategies = logPersistenceStrategyService.ListStrategies().ToDictionary(x => x.GetType().GetSimpleAssemblyQualifiedName(), x => x); _logPersistenceConfigSerializerOptions = new JsonSerializerOptions @@ -71,7 +75,8 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper var cancellationToken = source.WorkflowExecutionContext.CancellationToken; var workflow = (Workflow?)source.GetAncestors().FirstOrDefault(x => x.Activity is Workflow)?.Activity ?? source.WorkflowExecutionContext.Workflow; - var workflowPersistenceProperty = await GetDefaultPersistenceModeAsync(source.WorkflowExecutionContext.ExpressionExecutionContext!, workflow.CustomProperties, () => _options.Value.LogPersistenceMode, cancellationToken); + var rootActivityExecutionContext = source.WorkflowExecutionContext.ActivityExecutionContexts.First(x => x.ParentActivityExecutionContext == null); + var workflowPersistenceProperty = await GetDefaultPersistenceModeAsync(rootActivityExecutionContext.ExpressionExecutionContext, workflow.CustomProperties, () => _options.Value.LogPersistenceMode, cancellationToken); var activityPersistencePropertyDefault = await GetDefaultPersistenceModeAsync(source.ExpressionExecutionContext, source.Activity.CustomProperties, () => workflowPersistenceProperty, cancellationToken); var legacyActivityPersistenceProperties = source.Activity.CustomProperties.GetValueOrDefault>(LegacyLogPersistenceModeKey, () => new Dictionary()); var activityPersistenceProperties = source.Activity.CustomProperties.GetValueOrDefault>(LogPersistenceConfigKey, () => new Dictionary()); @@ -202,7 +207,16 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper return defaultMode(); var expression = config.Expression; - return await _expressionEvaluator.EvaluateAsync(expression, executionContext); + + try + { + return await _expressionEvaluator.EvaluateAsync(expression, executionContext); + } + catch (Exception e) + { + _logger.LogWarning(e, "Error evaluating log persistence expression"); + return defaultMode(); + } } private static ActivityStatus GetAggregateStatus(ActivityExecutionContext context)