diff --git a/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Enums/LogPersistenceMode.cs b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Enums/LogPersistenceMode.cs new file mode 100644 index 000000000..01765ba40 --- /dev/null +++ b/src/clients/Elsa.Api.Client/Resources/WorkflowDefinitions/Enums/LogPersistenceMode.cs @@ -0,0 +1,23 @@ +namespace Elsa.Api.Client.Resources.WorkflowDefinitions.Enums; + +/// +/// Define the Log Persistence mode to store information +/// +public enum LogPersistenceMode +{ + /// + /// Persist using the Parent mode + /// + Default, + + /// + /// Include property to store + /// + Include, + + /// + /// Exclude Property to store + /// + Exclude + +} \ No newline at end of file diff --git a/src/modules/Elsa.MassTransit.RabbitMq/Features/RabbitMqServiceBusFeature.cs b/src/modules/Elsa.MassTransit.RabbitMq/Features/RabbitMqServiceBusFeature.cs index 12d38d83a..4a49012f9 100644 --- a/src/modules/Elsa.MassTransit.RabbitMq/Features/RabbitMqServiceBusFeature.cs +++ b/src/modules/Elsa.MassTransit.RabbitMq/Features/RabbitMqServiceBusFeature.cs @@ -29,7 +29,9 @@ public class RabbitMqServiceBusFeature : FeatureBase /// A RabbitMQ connection string. public string? ConnectionString { get; set; } + /// /// Configures the RabbitMQ transport options. + /// public Action? TransportOptions { get; set; } /// diff --git a/src/modules/Elsa.Workflows.Core/Enums/LogPersistenceMode.cs b/src/modules/Elsa.Workflows.Core/Enums/LogPersistenceMode.cs new file mode 100644 index 000000000..26ec3c88a --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/Enums/LogPersistenceMode.cs @@ -0,0 +1,28 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Elsa.Workflows.Enums; +/// +/// Define the Log Persistence mode to store information +/// +public enum LogPersistenceMode +{ + /// + /// Persist using the Parent mode + /// + Default, + + /// + /// Include property to store + /// + Include, + + /// + /// Exclude Property to store + /// + Exclude + +} diff --git a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs index e96d0212d..a7a58fb2c 100644 --- a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs @@ -9,6 +9,7 @@ using Elsa.Features.Abstractions; using Elsa.Features.Attributes; using Elsa.Features.Services; using Elsa.Workflows.Contracts; +using Elsa.Workflows.Enums; using Elsa.Workflows.Features; using Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity; using Elsa.Workflows.Management.Compression; @@ -42,7 +43,7 @@ public class WorkflowManagementFeature : FeatureBase private const string DynamicCategory = "Dynamic"; private string CompressionAlgorithm { get; set; } = nameof(None); - + private LogPersistenceMode LogPersistenceMode { get; set; } = LogPersistenceMode.Include; /// public WorkflowManagementFeature(IModule module) : base(module) { @@ -161,6 +162,16 @@ public class WorkflowManagementFeature : FeatureBase return this; } + /// + /// Set the default Log Persistence mode to use for worflow state (default is Include) + /// + /// The mode persistence value + public WorkflowManagementFeature SetDefaultLogPersistenceMode(LogPersistenceMode logPersistenceMode) + { + LogPersistenceMode = logPersistenceMode; + return this; + } + /// [RequiresUnreferencedCode("The assembly containing the specified marker type will be scanned for activity types.")] public override void Configure() @@ -211,6 +222,7 @@ public class WorkflowManagementFeature : FeatureBase options.VariableDescriptors.Add(descriptor); options.CompressionAlgorithm = CompressionAlgorithm; + options.LogPersistenceMode = LogPersistenceMode; }); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Options/ManagementOptions.cs b/src/modules/Elsa.Workflows.Management/Options/ManagementOptions.cs index 200c5557d..9063efa8d 100644 --- a/src/modules/Elsa.Workflows.Management/Options/ManagementOptions.cs +++ b/src/modules/Elsa.Workflows.Management/Options/ManagementOptions.cs @@ -1,3 +1,4 @@ +using Elsa.Workflows.Enums; using Elsa.Workflows.Management.Models; namespace Elsa.Workflows.Management.Options; @@ -20,5 +21,10 @@ public class ManagementOptions /// /// The format to use for compressing workflow state. /// - public string? CompressionAlgorithm { get; set; } + public string? CompressionAlgorithm { get; set; } + + /// + /// The default Log Persistence Mode to use for all the system + /// + public LogPersistenceMode LogPersistenceMode { get; set; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs index bc08b314c..fd29a5ae4 100644 --- a/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs +++ b/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs @@ -215,7 +215,7 @@ public class WorkflowRuntimeFeature : FeatureBase .AddScoped() .AddScoped() .AddScoped() - .AddScoped() + .AddScoped() .AddScoped() .AddScoped() .AddScoped() diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs index 0c54a2108..bf012ab59 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs @@ -1,17 +1,49 @@ using Elsa.Extensions; +using Elsa.Workflows.Enums; +using Elsa.Workflows.Management.Options; using Elsa.Workflows.Models; using Elsa.Workflows.Runtime.Contracts; using Elsa.Workflows.Runtime.Entities; using Elsa.Workflows.State; +using Humanizer; +using Microsoft.Extensions.Options; namespace Elsa.Workflows.Runtime.Services; /// public class DefaultActivityExecutionMapper : IActivityExecutionMapper -{ +{ + private LogPersistenceMode _serverLogPersistenceMode; + private const string LogPersistenceModeKey = "logPersistenceMode"; + + public DefaultActivityExecutionMapper(IOptions options) + { + _serverLogPersistenceMode = options.Value.LogPersistenceMode; + } + /// public ActivityExecutionRecord Map(ActivityExecutionContext source) - { + { + /* + * { + * "logPersistenceMode": { + * "default": "default", + * "inputs": { k : v }, + * "outputs": { k: v } + * } + * } + */ + + var workflowPersistenceProperty = + GetDefaultPersistenceMode(source.WorkflowExecutionContext.Workflow.CustomProperties, () => _serverLogPersistenceMode); + + + var activityPersistenceProperties = source.Activity.CustomProperties + .GetValueOrDefault>(LogPersistenceModeKey, () => new Dictionary()); + var activityPersistencePropertyDefault = + GetDefaultPersistenceMode(source.Activity.CustomProperties, () => workflowPersistenceProperty); + + // Get any outcomes that were added to the activity execution context. var outcomes = source.JournalData.TryGetValue("Outcomes", out var resultValue) ? resultValue as string[] : default; var payload = new Dictionary(); @@ -40,7 +72,10 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper return default; }); - + + outputs = StorePropertyUsingPersistanceMode(outputs, activityPersistenceProperties!.GetValueOrDefault("outputs", () => new Dictionary())!, activityPersistencePropertyDefault); + var activityState = StorePropertyUsingPersistanceMode(source.ActivityState, activityPersistenceProperties!.GetValueOrDefault("inputs", () => new Dictionary())!, activityPersistencePropertyDefault ); + return new ActivityExecutionRecord { Id = source.Id, @@ -49,7 +84,7 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper WorkflowInstanceId = source.WorkflowExecutionContext.Id, ActivityType = source.Activity.Type, ActivityName = source.Activity.Name, - ActivityState = source.ActivityState, + ActivityState = activityState, Outputs = outputs, Properties = source.Properties, Payload = payload, @@ -60,8 +95,39 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper Status = GetAggregateStatus(source), CompletedAt = source.CompletedAt }; - } - + } + + private static LogPersistenceMode GetDefaultPersistenceMode(IDictionary customProperties,Func defaultFactory) + { + var properties = customProperties + .GetValueOrDefault>(LogPersistenceModeKey, () => new Dictionary()); + var persistencePropertyDefault = properties! + .GetValueOrDefault("default", defaultFactory); + + if(persistencePropertyDefault == LogPersistenceMode.Default) + return defaultFactory(); + return persistencePropertyDefault; + } + + private static Dictionary StorePropertyUsingPersistanceMode(IDictionary inputs + , IDictionary persistenceModeConfiguration + , LogPersistenceMode defaultLogPersistenceMode = LogPersistenceMode.Exclude) + { + var result = new Dictionary(); + + foreach (var input in inputs) + { + var persistence = persistenceModeConfiguration.GetValueOrDefault(input.Key.Camelize(), () => defaultLogPersistenceMode); + if (persistence.Equals(LogPersistenceMode.Include) + || (persistence.Equals(LogPersistenceMode.Default) && defaultLogPersistenceMode == LogPersistenceMode.Include) + ) + result.Add(input.Key, input.Value); + + } + + return result; + } + private ActivityStatus GetAggregateStatus(ActivityExecutionContext context) { // If any child activity is faulted, the aggregate status is faulted.