From 08632471e3a5acb7e851de4b637abc495c589ae6 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 28 Nov 2024 12:19:38 +0100 Subject: [PATCH] Implement Dynamic Expression Evaluation for Log Persistence Mode (#6159) * Add log persistence configuration and strategy evaluation Introduced `LogPersistenceConfiguration` to support dynamic strategy and expression-based log persistence evaluations. Updated `Default default values. * Add LogPersistenceConfiguration and enum support Introduced LogPersistenceConfiguration class and LogPersistenceEvaluationMode enum in both Elsa.Api.Client and Elsa.Workflows.Runtime. Updated JavaScript services to handle enums correctly and register LogPersistenceMode. * Rename log persistence key for consistency Updated the log persistence key from `LogPersistenceStrategyKey` to `LogPersistenceConfigKey` to maintain consistency across the codebase. This change ensures that property access aligns with the updated naming conventions used in the application's configuration. * Reuse JSON serializer options * Update log persistence config structure in comments This commit revises the JSON example in code comments to reflect the updated structure of the log persistence configuration. The changes include updated evaluation modes and strategy types for default, inputs, and outputs sections. These modifications aim to enhance clarity and provide accurate documentation of the expected configuration format. --- .../Enums/LogPersistenceEvaluationMode.cs | 7 ++ .../Models/LogPersistenceConfiguration.cs | 11 ++ .../ConfigureEngineWithCommonTypes.cs | 2 + .../VariableTypeDefinitionProvider.cs | 2 +- .../Services/TypeAliasRegistry.cs | 2 + .../TypeDefinitionDocumentRenderer.cs | 15 ++- .../TypeDefinitions/Services/TypeDescriber.cs | 21 +++- .../Extensions/DictionaryExtensions.cs | 13 ++ .../Features/WorkflowManagementFeature.cs | 4 +- .../Elsa.Workflows.Runtime.csproj.DotSettings | 1 + .../Enums/LogPersistenceEvaluationMode.cs | 7 ++ .../Models/LogPersistenceConfiguration.cs | 10 ++ .../DefaultActivityExecutionMapper.cs | 118 +++++++++++++----- 13 files changed, 176 insertions(+), 37 deletions(-) create mode 100644 src/clients/Elsa.Api.Client/Shared/Enums/LogPersistenceEvaluationMode.cs create mode 100644 src/clients/Elsa.Api.Client/Shared/Models/LogPersistenceConfiguration.cs create mode 100644 src/modules/Elsa.Workflows.Runtime/Enums/LogPersistenceEvaluationMode.cs create mode 100644 src/modules/Elsa.Workflows.Runtime/Models/LogPersistenceConfiguration.cs diff --git a/src/clients/Elsa.Api.Client/Shared/Enums/LogPersistenceEvaluationMode.cs b/src/clients/Elsa.Api.Client/Shared/Enums/LogPersistenceEvaluationMode.cs new file mode 100644 index 000000000..1f4a18a17 --- /dev/null +++ b/src/clients/Elsa.Api.Client/Shared/Enums/LogPersistenceEvaluationMode.cs @@ -0,0 +1,7 @@ +namespace Elsa.Api.Client.Shared.Enums; + +public enum LogPersistenceEvaluationMode +{ + Strategy, + Expression +} \ No newline at end of file diff --git a/src/clients/Elsa.Api.Client/Shared/Models/LogPersistenceConfiguration.cs b/src/clients/Elsa.Api.Client/Shared/Models/LogPersistenceConfiguration.cs new file mode 100644 index 000000000..ac4531b50 --- /dev/null +++ b/src/clients/Elsa.Api.Client/Shared/Models/LogPersistenceConfiguration.cs @@ -0,0 +1,11 @@ +using Elsa.Api.Client.Resources.Scripting.Models; +using Elsa.Api.Client.Shared.Enums; + +namespace Elsa.Api.Client.Shared.Models; + +public class LogPersistenceConfiguration +{ + public LogPersistenceEvaluationMode EvaluationMode { get; set; } + public string? StrategyType { get; set; } + public Expression? Expression { get; set; } +} \ No newline at end of file diff --git a/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithCommonTypes.cs b/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithCommonTypes.cs index a296d1d7a..c0a303024 100644 --- a/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithCommonTypes.cs +++ b/src/modules/Elsa.JavaScript/Handlers/ConfigureEngineWithCommonTypes.cs @@ -1,6 +1,7 @@ using Elsa.Extensions; using Elsa.JavaScript.Notifications; using Elsa.Mediator.Contracts; +using Elsa.Workflows.LogPersistence; using JetBrains.Annotations; namespace Elsa.JavaScript.Handlers; @@ -22,6 +23,7 @@ public class ConfigureEngineWithCommonTypes : INotificationHandler(); engine.RegisterType(); engine.RegisterType(); + engine.RegisterType(); return Task.CompletedTask; } diff --git a/src/modules/Elsa.JavaScript/Providers/VariableTypeDefinitionProvider.cs b/src/modules/Elsa.JavaScript/Providers/VariableTypeDefinitionProvider.cs index d56e6c255..a5888b87b 100644 --- a/src/modules/Elsa.JavaScript/Providers/VariableTypeDefinitionProvider.cs +++ b/src/modules/Elsa.JavaScript/Providers/VariableTypeDefinitionProvider.cs @@ -25,7 +25,7 @@ internal class VariableTypeDefinitionProvider(ITypeDescriber typeDescriber) : Ty var variableTypeQuery = from variable in variables let variableType = variable.GetVariableType() - where (variableType.IsClass || variableType.IsInterface) && !variableType.IsPrimitive && !excludedTypes.Any(x => x(variableType)) + where (variableType.IsClass || variableType.IsInterface || variableType.IsEnum) && !variableType.IsPrimitive && !excludedTypes.Any(x => x(variableType)) select variableType; var variableTypes = variableTypeQuery.Distinct(); diff --git a/src/modules/Elsa.JavaScript/Services/TypeAliasRegistry.cs b/src/modules/Elsa.JavaScript/Services/TypeAliasRegistry.cs index 77af847c9..71ca4c238 100644 --- a/src/modules/Elsa.JavaScript/Services/TypeAliasRegistry.cs +++ b/src/modules/Elsa.JavaScript/Services/TypeAliasRegistry.cs @@ -1,6 +1,7 @@ using System.Dynamic; using Elsa.JavaScript.Contracts; using Elsa.JavaScript.Extensions; +using Elsa.Workflows.LogPersistence; namespace Elsa.JavaScript.Services; @@ -32,6 +33,7 @@ public class TypeAliasRegistry : ITypeAliasRegistry this.RegisterType("Date"); this.RegisterType("Date"); this.RegisterType>("ObjectDictionary"); + this.RegisterType("LogPersistenceMode"); } /// diff --git a/src/modules/Elsa.JavaScript/TypeDefinitions/Services/TypeDefinitionDocumentRenderer.cs b/src/modules/Elsa.JavaScript/TypeDefinitions/Services/TypeDefinitionDocumentRenderer.cs index 103b074ab..f5699e6f5 100644 --- a/src/modules/Elsa.JavaScript/TypeDefinitions/Services/TypeDefinitionDocumentRenderer.cs +++ b/src/modules/Elsa.JavaScript/TypeDefinitions/Services/TypeDefinitionDocumentRenderer.cs @@ -40,9 +40,17 @@ public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer { output.AppendLine($"declare {typeDefinition.DeclarationKeyword} {typeDefinition.Name} {{"); - foreach (var property in typeDefinition.Properties) - Render(property, output); - + if (typeDefinition.DeclarationKeyword == "enum") + { + foreach (var property in typeDefinition.Properties) + RenderEnumMember(property, output); + } + else + { + foreach (var property in typeDefinition.Properties) + Render(property, output); + } + foreach (var method in typeDefinition.Methods) RenderMethod(method, output); @@ -50,6 +58,7 @@ public class TypeDefinitionDocumentRenderer : ITypeDefinitionDocumentRenderer } private void Render(PropertyDefinition property, StringBuilder output) => output.AppendLine($"{property.Name}{(property.IsOptional ? "?" : "")}: {property.Type};"); + private void RenderEnumMember(PropertyDefinition property, StringBuilder output) => output.AppendLine($"{property.Name} = \"{property.Name}\";"); private void Render(VariableDefinition variable, StringBuilder output) => output.AppendLine($"declare var {variable.Name}: {variable.Type};"); string RenderParameter(ParameterDefinition parameter) => $"{parameter.Name}{(parameter.IsOptional ? "?" : "")}: {parameter.Type}"; string RenderParameters(IEnumerable parameters) => string.Join(", ", parameters.Select(RenderParameter)); diff --git a/src/modules/Elsa.JavaScript/TypeDefinitions/Services/TypeDescriber.cs b/src/modules/Elsa.JavaScript/TypeDefinitions/Services/TypeDescriber.cs index 65e76b403..af4a85b74 100644 --- a/src/modules/Elsa.JavaScript/TypeDefinitions/Services/TypeDescriber.cs +++ b/src/modules/Elsa.JavaScript/TypeDefinitions/Services/TypeDescriber.cs @@ -36,6 +36,9 @@ public class TypeDescriber : ITypeDescriber private IEnumerable GetMethodDefinitions(Type type) { + if(type.IsEnum) + yield break; + #pragma warning disable IL2070 var methods = type.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static).Where(x => !x.IsSpecialName).ToList(); #pragma warning restore IL2070 @@ -68,6 +71,22 @@ public class TypeDescriber : ITypeDescriber private IEnumerable GetPropertyDefinitions([DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicProperties)] Type type) { + // If the type is an enum, enumerate its members. + if (type.IsEnum) + { + foreach (var name in Enum.GetNames(type)) + { + yield return new PropertyDefinition + { + Name = name, + Type = "string", + IsOptional = false, + }; + } + + yield break; + } + var properties = type.GetProperties(); foreach (var property in properties) @@ -86,8 +105,8 @@ public class TypeDescriber : ITypeDescriber { { IsInterface: true } => "interface", { IsClass: true } => "class", - { IsValueType: true } => "class", { IsEnum: true } => "enum", + { IsValueType: true, IsEnum: false } => "class", _ => "interface" }; } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Extensions/DictionaryExtensions.cs b/src/modules/Elsa.Workflows.Core/Extensions/DictionaryExtensions.cs index 4731da51c..bc1bb6c44 100644 --- a/src/modules/Elsa.Workflows.Core/Extensions/DictionaryExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Extensions/DictionaryExtensions.cs @@ -9,6 +9,18 @@ public static class DictionaryExtensions public static bool TryGetValue(this IDictionary dictionary, IEnumerable keys, out T value) => dictionary.TryGetValue(keys, out value); public static bool TryGetValue(this IDictionary dictionary, string key, out T value) => dictionary.TryGetValue(key, out value); + public static bool TryGetValue(this IDictionary dictionary, TKey key, out T value) + { + if (!dictionary.TryGetValue(key, out var item)) + { + value = default!; + return false; + } + + value = item; + return true; + } + public static bool TryGetValue(this IDictionary dictionary, TKey key, out T value) { if (!dictionary.TryGetValue(key, out var item)) @@ -38,6 +50,7 @@ public static class DictionaryExtensions public static T? GetValue(this IDictionary dictionary, TKey key) => ConvertValue(dictionary[key]); public static T? GetValue(this IDictionary dictionary, string key) => ConvertValue(dictionary[key]); + public static T? GetValueOrDefault(this IDictionary dictionary, TKey key, Func defaultValueFactory) => TryGetValue(dictionary, key, out var value) ? value : defaultValueFactory(); public static T? GetValueOrDefault(this IDictionary dictionary, TKey key, Func defaultValueFactory) => TryGetValue(dictionary, key, out var value) ? value : defaultValueFactory(); public static T? GetValueOrDefault(this IDictionary dictionary, TKey key) => GetValueOrDefault(dictionary, key, () => default); diff --git a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs index 7a57818d8..a81d8fd47 100644 --- a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs @@ -47,6 +47,7 @@ public class WorkflowManagementFeature : FeatureBase private const string LookupsCategory = "Lookups"; private const string DynamicCategory = "Dynamic"; private const string DataCategory = "Data"; + private const string SystemCategory = "System"; private string CompressionAlgorithm { get; set; } = nameof(None); private LogPersistenceMode LogPersistenceMode { get; set; } = LogPersistenceMode.Include; @@ -86,7 +87,8 @@ public class WorkflowManagementFeature : FeatureBase new(typeof(JsonNode), DynamicCategory, "A JSON node for reading and writing a JSON structure."), new(typeof(JsonObject), DynamicCategory, "A JSON object for reading and writing a JSON structure."), new(typeof(byte[]), DataCategory, "A byte array."), - new(typeof(Stream), DataCategory, "A stream.") + new(typeof(Stream), DataCategory, "A stream."), + new(typeof(LogPersistenceMode), SystemCategory, "A LogPersistenceMode enum value.") ]; /// diff --git a/src/modules/Elsa.Workflows.Runtime/Elsa.Workflows.Runtime.csproj.DotSettings b/src/modules/Elsa.Workflows.Runtime/Elsa.Workflows.Runtime.csproj.DotSettings index e0fbdd65a..9b4d77e2f 100644 --- a/src/modules/Elsa.Workflows.Runtime/Elsa.Workflows.Runtime.csproj.DotSettings +++ b/src/modules/Elsa.Workflows.Runtime/Elsa.Workflows.Runtime.csproj.DotSettings @@ -1,6 +1,7 @@  True True + True True True True diff --git a/src/modules/Elsa.Workflows.Runtime/Enums/LogPersistenceEvaluationMode.cs b/src/modules/Elsa.Workflows.Runtime/Enums/LogPersistenceEvaluationMode.cs new file mode 100644 index 000000000..75aea73c0 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/Enums/LogPersistenceEvaluationMode.cs @@ -0,0 +1,7 @@ +namespace Elsa.Workflows.Runtime; + +public enum LogPersistenceEvaluationMode +{ + Strategy, + Expression +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Models/LogPersistenceConfiguration.cs b/src/modules/Elsa.Workflows.Runtime/Models/LogPersistenceConfiguration.cs new file mode 100644 index 000000000..19d8b6e12 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/Models/LogPersistenceConfiguration.cs @@ -0,0 +1,10 @@ +using Elsa.Expressions.Models; + +namespace Elsa.Workflows.Runtime; + +public class LogPersistenceConfiguration +{ + public LogPersistenceEvaluationMode EvaluationMode { get; set; } + public string? StrategyType { get; set; } + public Expression? Expression { get; set; } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs index 2fe92dce7..c04958358 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs @@ -1,9 +1,15 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Elsa.Expressions.Contracts; +using Elsa.Expressions.Models; using Elsa.Extensions; using Elsa.Workflows.Activities; using Elsa.Workflows.LogPersistence; +using Elsa.Workflows.LogPersistence.Strategies; using Elsa.Workflows.Management.Options; using Elsa.Workflows.Models; using Elsa.Workflows.Runtime.Entities; +using Elsa.Workflows.Serialization.Converters; using Elsa.Workflows.State; using Humanizer; using Microsoft.Extensions.Options; @@ -13,17 +19,32 @@ namespace Elsa.Workflows.Runtime; /// public class DefaultActivityExecutionMapper : IActivityExecutionMapper { + private readonly JsonSerializerOptions _logPersistenceConfigSerializerOptions; private readonly IOptions _options; + private readonly IExpressionEvaluator _expressionEvaluator; private readonly IDictionary _logPersistenceStrategies; - public DefaultActivityExecutionMapper(IOptions options, ILogPersistenceStrategyService logPersistenceStrategyService) + public DefaultActivityExecutionMapper( + IOptions options, + ILogPersistenceStrategyService logPersistenceStrategyService, + IExpressionEvaluator expressionEvaluator, + IExpressionDescriptorRegistry expressionDescriptorRegistry) { _options = options; + _expressionEvaluator = expressionEvaluator; _logPersistenceStrategies = logPersistenceStrategyService.ListStrategies().ToDictionary(x => x.GetType().GetSimpleAssemblyQualifiedName(), x => x); + + _logPersistenceConfigSerializerOptions = new JsonSerializerOptions + { + PropertyNameCaseInsensitive = true + }.WithConverters( + new ExpressionJsonConverterFactory(expressionDescriptorRegistry), + new JsonStringEnumConverter(), + new ExpandoObjectConverterFactory()); } private const string LegacyLogPersistenceModeKey = "logPersistenceMode"; - private const string LogPersistenceStrategyKey = "logPersistenceStrategy"; + private const string LogPersistenceConfigKey = "logPersistenceConfig"; /// public async Task MapAsync(ActivityExecutionContext source) @@ -40,24 +61,25 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper /* The following JSON structure is expected to be found in the custom properties of the workflow and activity: * { - * "logPersistenceStrategy": { - * "default": "null", - * "inputs": { k : v }, - * "outputs": { k: v } + * "logPersistenceConfig": { + * "default": { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." }, + * "inputs": { "input1" : { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." } }, + * "outputs": { "output1" : { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." } } * } * } */ var cancellationToken = source.WorkflowExecutionContext.CancellationToken; var workflow = (Workflow?)source.GetAncestors().FirstOrDefault(x => x.Activity is Workflow)?.Activity ?? source.WorkflowExecutionContext.Workflow; - var workflowPersistenceProperty = await GetDefaultPersistenceModeAsync(workflow.CustomProperties, () => _options.Value.LogPersistenceMode, cancellationToken); - var activityPersistencePropertyDefault = await GetDefaultPersistenceModeAsync(source.Activity.CustomProperties, () => workflowPersistenceProperty, cancellationToken); + var workflowPersistenceProperty = await GetDefaultPersistenceModeAsync(source.WorkflowExecutionContext.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>(LogPersistenceStrategyKey, () => new Dictionary()); + var activityPersistenceProperties = source.Activity.CustomProperties.GetValueOrDefault>(LogPersistenceConfigKey, () => new Dictionary()); var payload = GetPayload(source); var outputs = GetOutputs(source); outputs = await StorePropertyUsingPersistenceMode( + source.ExpressionExecutionContext, outputs, legacyActivityPersistenceProperties!.GetValueOrDefault("outputs", () => new Dictionary())!, activityPersistenceProperties!.GetValueOrDefault("outputs", () => new Dictionary())!, @@ -65,7 +87,8 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper cancellationToken); var inputs = await StorePropertyUsingPersistenceMode( - source.ActivityState, + source.ExpressionExecutionContext, + source.ActivityState, legacyActivityPersistenceProperties!.GetValueOrDefault("inputs", () => new Dictionary())!, activityPersistenceProperties!.GetValueOrDefault("inputs", () => new Dictionary())!, activityPersistencePropertyDefault, @@ -92,13 +115,13 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper }; } - private async Task GetDefaultPersistenceModeAsync(IDictionary customProperties, Func defaultFactory, CancellationToken cancellationToken) + private async Task GetDefaultPersistenceModeAsync(ExpressionExecutionContext expressionExecutionContext, IDictionary customProperties, Func defaultFactory, CancellationToken cancellationToken) { var legacyProperties = customProperties.GetValueOrDefault>(LegacyLogPersistenceModeKey, () => new Dictionary()); - var properties = customProperties.GetValueOrDefault>(LogPersistenceStrategyKey, () => new Dictionary()); - var defaultPersistenceStrategyTypeName = properties!.GetValueOrDefault("default"); + var properties = customProperties.GetValueOrDefault>(LogPersistenceConfigKey, () => new Dictionary()); + var defaultPersistenceConfigObject = properties?.TryGetValue("default", out var defaultPersistenceConfigObjectValue) == true ? defaultPersistenceConfigObjectValue : null; - if (defaultPersistenceStrategyTypeName == null) + if (defaultPersistenceConfigObject == null) { var legacyPersistencePropertyDefault = legacyProperties!.GetValueOrDefault("default", defaultFactory); @@ -107,18 +130,14 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper return legacyPersistencePropertyDefault; } - var strategy = _logPersistenceStrategies.TryGetValue(defaultPersistenceStrategyTypeName, out var v) ? v : null; - - if (strategy == null) - return defaultFactory(); - - var strategyContext = new LogPersistenceStrategyContext(cancellationToken); - return await strategy.GetPersistenceModeAsync(strategyContext); + var defaultPersistenceConfig = Convert(defaultPersistenceConfigObject); + return await EvaluateLogPersistenceConfigAsync(defaultPersistenceConfig, expressionExecutionContext, defaultFactory, cancellationToken); } private async Task> StorePropertyUsingPersistenceMode( + ExpressionExecutionContext expressionExecutionContext, IDictionary state, - IDictionary persistenceModeConfiguration, + IDictionary obsoletePersistenceModeConfiguration, IDictionary persistenceStrategyConfiguration, LogPersistenceMode defaultLogPersistenceMode, CancellationToken cancellationToken) @@ -128,17 +147,17 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper foreach (var value in state) { var propKey = value.Key.Camelize(); - var strategy = persistenceStrategyConfiguration.GetValueOrDefault(propKey) is string strategyTypeName ? _logPersistenceStrategies.TryGetValue(strategyTypeName, out var v) ? v : null : null; + var logPersistenceConfigObject = persistenceStrategyConfiguration.GetValueOrDefault(propKey, () => null); + var logPersistenceConfig = Convert(logPersistenceConfigObject); var mode = defaultLogPersistenceMode; - if (strategy != null) + if (logPersistenceConfig != null) { - var strategyContext = new LogPersistenceStrategyContext(cancellationToken); - mode = await strategy.GetPersistenceModeAsync(strategyContext); + mode = await EvaluateLogPersistenceConfigAsync(logPersistenceConfig, expressionExecutionContext, () => defaultLogPersistenceMode, cancellationToken); } else { - mode = persistenceModeConfiguration.GetValueOrDefault(propKey, () => defaultLogPersistenceMode); + mode = obsoletePersistenceModeConfiguration.GetValueOrDefault(propKey, () => defaultLogPersistenceMode); } if (mode == LogPersistenceMode.Include || mode == LogPersistenceMode.Inherit && defaultLogPersistenceMode is LogPersistenceMode.Include or LogPersistenceMode.Inherit) @@ -148,6 +167,44 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper return result; } + private LogPersistenceConfiguration? Convert(object? value) + { + if (value == null) + return null; + + if (value is LogPersistenceConfiguration c) + return c; + + var json = JsonSerializer.Serialize(value); + var config = JsonSerializer.Deserialize(json, _logPersistenceConfigSerializerOptions); + + return config; + } + + private async Task EvaluateLogPersistenceConfigAsync(LogPersistenceConfiguration? config, ExpressionExecutionContext executionContext, Func defaultMode, CancellationToken cancellationToken) + { + if (config == null) + return defaultMode(); + + if (config.EvaluationMode == LogPersistenceEvaluationMode.Strategy) + { + var strategyTypeName = config.StrategyType ?? typeof(Inherit).GetSimpleAssemblyQualifiedName(); + var strategy = _logPersistenceStrategies.TryGetValue(strategyTypeName, out var v) ? v : null; + + if (strategy == null) + return defaultMode(); + + var strategyContext = new LogPersistenceStrategyContext(cancellationToken); + return await strategy.GetPersistenceModeAsync(strategyContext); + } + + if (config.Expression == null) + return defaultMode(); + + var expression = config.Expression; + return await _expressionEvaluator.EvaluateAsync(expression, executionContext); + } + private static ActivityStatus GetAggregateStatus(ActivityExecutionContext context) { // If any child activity is faulted, the aggregate status is faulted. @@ -158,7 +215,7 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper return context.Status; } - + private static IDictionary GetPayload(ActivityExecutionContext source) { var outcomes = source.JournalData.TryGetValue("Outcomes", out var resultValue) ? resultValue as string[] : default; @@ -166,11 +223,10 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper if (outcomes != null) payload.Add("Outcomes", outcomes); - + return payload; } - // Get the outputs of the activity execution context. private static IDictionary GetOutputs(ActivityExecutionContext source) { var activity = source.Activity; @@ -193,7 +249,7 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper return default; }); - + return outputs; } } \ No newline at end of file