From 476656cccec3ad1da3b882e2f65ffb6873aba8a7 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 18 Apr 2025 14:29:18 +0200 Subject: [PATCH] Implement internal state activity persistence and logging mechanisms (#6601) * Implement internal state activity persistence and logging mechanisms Updated property handling to support nullable dictionaries and improved persistable states. Adjusted serialization logic to handle optional fields more robustly, ensuring better compatibility with log persistence mappings and internal state evaluations. * Replace default! with null! for string properties Updated string properties in various records to use null! instead of default! for consistency and clarity. Additionally, adjusted methods to check collection existence before serialization and streamlined object initializations with simplified syntax where possible. * Fix nullable types in DeserializeActivityState method Updated the method's return type and JSON deserialization to properly handle nullable values. This ensures better alignment with the method's behavior and avoids potential null reference issues. * Fix null reference issues in InputOutputLoggingTests Replaced forced null dereferences with safe navigation checks to prevent potential null reference exceptions. This ensures more robust and error-free test execution for activity state validations. --- .../Middleware/ConnectionMiddleware.cs | 9 ++++--- .../Records/ActivityExecutionRecord.cs | 10 ++++---- .../Records/ActivityExecutionSummaryRecord.cs | 10 ++++---- .../Runtime/Records/KeyValuePairRecord.cs | 2 +- .../Runtime/Records/StoredBookmarkRecord.cs | 6 ++--- .../Runtime/Records/StoredTriggerRecord.cs | 8 +++---- .../WorkflowExecutionLogRecordRecord.cs | 18 +++++++------- .../DapperActivityExecutionRecordStore.cs | 24 +++++++++---------- .../Runtime/ActivityExecutionLogStore.cs | 14 +++++------ .../Entities/ActivityExecutionRecord.cs | 2 +- .../Models/ActivityLogPersistenceModeMap.cs | 1 + ...ActivityPropertyLogPersistenceEvaluator.cs | 17 +++++++++++-- .../DefaultActivityExecutionMapper.cs | 19 ++++++++++----- .../InputOutputLoggingTests.cs | 6 ++--- 14 files changed, 83 insertions(+), 63 deletions(-) diff --git a/src/modules/Elsa.Connections.Core/Middleware/ConnectionMiddleware.cs b/src/modules/Elsa.Connections.Core/Middleware/ConnectionMiddleware.cs index 736470521..b48b2326c 100644 --- a/src/modules/Elsa.Connections.Core/Middleware/ConnectionMiddleware.cs +++ b/src/modules/Elsa.Connections.Core/Middleware/ConnectionMiddleware.cs @@ -50,14 +50,13 @@ public class ConnectionMiddleware(ActivityMiddlewareDelegate next LogConnectionExtensions.LogConnectionIsNull(logger); else { - //Get connection from store, if exist, - var connectionConfiguration = await connectionStore.FindAsync(new Persistence.Filters.ConnectionDefinitionFilter() { Name = connectionName }); + // Get connection from store, if exists. + var connectionConfiguration = await connectionStore.FindAsync(new() { Name = connectionName }); if (connectionConfiguration != null) { - dynamic deserializedjson = JsonSerializer.Deserialize(connectionConfiguration.ConnectionConfiguration, propertyType, SerializerOptions); - - inputValue.Properties = deserializedjson; + dynamic deserializedJson = connectionConfiguration.ConnectionConfiguration.Deserialize(propertyType, SerializerOptions)!; + inputValue.Properties = deserializedJson; input.ValueSetter(context.Activity, inputValue); } else diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs index 17b24a294..19c25c98d 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionRecord.cs @@ -10,22 +10,22 @@ internal class ActivityExecutionRecordRecord : Record /// /// Gets or sets the workflow instance ID. /// - public string WorkflowInstanceId { get; set; } = default!; + public string WorkflowInstanceId { get; set; } = null!; /// /// Gets or sets the activity ID. /// - public string ActivityId { get; set; } = default!; + public string ActivityId { get; set; } = null!; /// /// Gets or sets the activity node ID. /// - public string ActivityNodeId { get; set; } = default!; + public string ActivityNodeId { get; set; } = null!; /// /// The type of the activity. /// - public string ActivityType { get; set; } = default!; + public string ActivityType { get; set; } = null!; /// /// The version of the activity type. @@ -75,7 +75,7 @@ internal class ActivityExecutionRecordRecord : Record /// /// Gets or sets the status of the activity. /// - public string Status { get; set; } = default!; + public string Status { get; set; } = null!; /// /// Gets or sets the time at which the activity execution completed. diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs index 0e389202b..228b82e47 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/ActivityExecutionSummaryRecord.cs @@ -10,22 +10,22 @@ internal class ActivityExecutionSummaryRecord : Record /// /// Gets or sets the workflow instance ID. /// - public string WorkflowInstanceId { get; set; } = default!; + public string WorkflowInstanceId { get; set; } = null!; /// /// Gets or sets the activity ID. /// - public string ActivityId { get; set; } = default!; + public string ActivityId { get; set; } = null!; /// /// Gets or sets the activity node ID. /// - public string ActivityNodeId { get; set; } = default!; + public string ActivityNodeId { get; set; } = null!; /// /// The type of the activity. /// - public string ActivityType { get; set; } = default!; + public string ActivityType { get; set; } = null!; /// /// The version of the activity type. @@ -50,7 +50,7 @@ internal class ActivityExecutionSummaryRecord : Record /// /// Gets or sets the status of the activity. /// - public string Status { get; set; } = default!; + public string Status { get; set; } = null!; /// /// Gets or sets the time at which the activity execution completed. diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs index a1d97edff..5a276a042 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/KeyValuePairRecord.cs @@ -4,5 +4,5 @@ namespace Elsa.Dapper.Modules.Runtime.Records; internal class KeyValuePairRecord : Record { - public string Value { get; set; } = default!; + public string Value { get; set; } = null!; } \ No newline at end of file diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs index ecaf03652..4040bdc92 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredBookmarkRecord.cs @@ -4,9 +4,9 @@ namespace Elsa.Dapper.Modules.Runtime.Records; internal class StoredBookmarkRecord : Record { - public string ActivityTypeName { get; set; } = default!; - public string Hash { get; set; } = default!; - public string WorkflowInstanceId { get; set; } = default!; + public string ActivityTypeName { get; set; } = null!; + public string Hash { get; set; } = null!; + public string WorkflowInstanceId { get; set; } = null!; public string? CorrelationId { get; set; } public string? ActivityInstanceId { get; set; } public string? SerializedPayload { get; set; } diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs index c81a8318c..537c55620 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/StoredTriggerRecord.cs @@ -4,10 +4,10 @@ namespace Elsa.Dapper.Modules.Runtime.Records; internal class StoredTriggerRecord : Record { - public string WorkflowDefinitionId { get; set; } = default!; - public string WorkflowDefinitionVersionId { get; set; } = default!; - public string Name { get; set; } = default!; - public string ActivityId { get; set; } = default!; + public string WorkflowDefinitionId { get; set; } = null!; + public string WorkflowDefinitionVersionId { get; set; } = null!; + public string Name { get; set; } = null!; + public string ActivityId { get; set; } = null!; public string? Hash { get; set; } public string? SerializedPayload { get; set; } } \ No newline at end of file diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs index 60115f286..5b2b5c74a 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Records/WorkflowExecutionLogRecordRecord.cs @@ -4,18 +4,18 @@ namespace Elsa.Dapper.Modules.Runtime.Records; internal class WorkflowExecutionLogRecordRecord : Record { - public string Id { get; set; } = default!; - public string WorkflowDefinitionId { get; set; } = default!; - public string WorkflowDefinitionVersionId { get; set; } = default!; - public string WorkflowInstanceId { get; set; } = default!; + public string Id { get; set; } = null!; + public string WorkflowDefinitionId { get; set; } = null!; + public string WorkflowDefinitionVersionId { get; set; } = null!; + public string WorkflowInstanceId { get; set; } = null!; public int WorkflowVersion { get; set; } - public string ActivityInstanceId { get; set; } = default!; + public string ActivityInstanceId { get; set; } = null!; public string? ParentActivityInstanceId { get; set; } - public string ActivityId { get; set; } = default!; - public string ActivityType { get; set; } = default!; + public string ActivityId { get; set; } = null!; + public string ActivityType { get; set; } = null!; public int ActivityTypeVersion { get; set; } - public string? ActivityName { get; set; } = default!; - public string ActivityNodeId { get; set; } = default!; + public string? ActivityName { get; set; } + public string ActivityNodeId { get; set; } = null!; public DateTimeOffset Timestamp { get; set; } public long Sequence { get; set; } public string? EventName { get; set; } diff --git a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs index 622436450..37879078c 100644 --- a/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs +++ b/src/modules/Elsa.Dapper/Modules/Runtime/Stores/DapperActivityExecutionRecordStore.cs @@ -109,7 +109,7 @@ internal class DapperActivityExecutionRecordStore(Store(source.Status), ActivityTypeVersion = source.ActivityTypeVersion, - ActivityState = source.SerializedActivityState != null ? payloadSerializer.Deserialize>(source.SerializedActivityState) : default, - Payload = source.SerializedPayload != null ? safeSerializer.Deserialize>(source.SerializedPayload) : default, - Outputs = source.SerializedOutputs != null ? safeSerializer.Deserialize>(source.SerializedOutputs) : default, - Exception = source.SerializedException != null ? payloadSerializer.Deserialize(source.SerializedException) : default, - Properties = source.SerializedProperties != null ? safeSerializer.Deserialize>(source.SerializedProperties) : new Dictionary(), + ActivityState = source.SerializedActivityState != null ? payloadSerializer.Deserialize>(source.SerializedActivityState) : null, + Payload = source.SerializedPayload != null ? safeSerializer.Deserialize>(source.SerializedPayload) : null, + Outputs = source.SerializedOutputs != null ? safeSerializer.Deserialize>(source.SerializedOutputs) : null, + Exception = source.SerializedException != null ? payloadSerializer.Deserialize(source.SerializedException) : null, + Properties = source.SerializedProperties != null ? safeSerializer.Deserialize>(source.SerializedProperties) : null, TenantId = source.TenantId }; } private ActivityExecutionRecordSummary MapSummary(ActivityExecutionSummaryRecord source) { - return new ActivityExecutionRecordSummary + return new() { Id = source.Id, ActivityId = source.ActivityId, diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs index 43cb9fec6..092781327 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Runtime/ActivityExecutionLogStore.cs @@ -85,13 +85,13 @@ public class EFCoreActivityExecutionStore( { entity = entity.SanitizeLogMessage(); var compressionAlgorithm = options.Value.CompressionAlgorithm ?? nameof(None); - var serializedActivityState = entity.ActivityState != null ? safeSerializer.Serialize(entity.ActivityState) : null; + var serializedActivityState = entity.ActivityState?.Count > 0 ? safeSerializer.Serialize(entity.ActivityState) : null; var compressedSerializedActivityState = serializedActivityState != null ? await compressionCodecResolver.Resolve(compressionAlgorithm).CompressAsync(serializedActivityState, cancellationToken) : null; dbContext.Entry(entity).Property("SerializedActivityState").CurrentValue = compressedSerializedActivityState; dbContext.Entry(entity).Property("SerializedActivityStateCompressionAlgorithm").CurrentValue = compressionAlgorithm; dbContext.Entry(entity).Property("SerializedOutputs").CurrentValue = entity.Outputs?.Any() == true ? safeSerializer.Serialize(entity.Outputs) : null; - dbContext.Entry(entity).Property("SerializedProperties").CurrentValue = entity.Properties.Any() ? payloadSerializer.Serialize(entity.Properties) : null; + dbContext.Entry(entity).Property("SerializedProperties").CurrentValue = entity.Properties?.Any() == true ? payloadSerializer.Serialize(entity.Properties) : null; dbContext.Entry(entity).Property("SerializedException").CurrentValue = entity.Exception != null ? payloadSerializer.Serialize(entity.Exception) : null; dbContext.Entry(entity).Property("SerializedPayload").CurrentValue = entity.Payload?.Any() == true ? payloadSerializer.Serialize(entity.Payload) : null; } @@ -104,13 +104,13 @@ public class EFCoreActivityExecutionStore( entity.ActivityState = await DeserializeActivityState(dbContext, entity, cancellationToken); entity.Outputs = Deserialize>(dbContext, entity, "SerializedOutputs"); - entity.Properties = DeserializePayload?>(dbContext, entity, "SerializedProperties") ?? new Dictionary(); + entity.Properties = DeserializePayload?>(dbContext, entity, "SerializedProperties"); entity.Exception = DeserializePayload(dbContext, entity, "SerializedException"); entity.Payload = DeserializePayload>(dbContext, entity, "SerializedPayload"); } [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize(String, JsonSerializerOptions)")] - private async Task?> DeserializeActivityState(RuntimeElsaDbContext dbContext, ActivityExecutionRecord entity, CancellationToken cancellationToken) + private async Task?> DeserializeActivityState(RuntimeElsaDbContext dbContext, ActivityExecutionRecord entity, CancellationToken cancellationToken) { var json = dbContext.Entry(entity).Property("SerializedActivityState").CurrentValue; @@ -119,11 +119,11 @@ public class EFCoreActivityExecutionStore( var compressionAlgorithm = (string?)dbContext.Entry(entity).Property("SerializedActivityStateCompressionAlgorithm").CurrentValue ?? nameof(None); var compressionStrategy = compressionCodecResolver.Resolve(compressionAlgorithm); json = await compressionStrategy.DecompressAsync(json, cancellationToken); - var dictionary = JsonSerializer.Deserialize>(json); - return dictionary?.ToDictionary(x => x.Key, x => (object)x.Value); + var dictionary = JsonSerializer.Deserialize>(json); + return dictionary?.ToDictionary(x => x.Key, x => x.Value); } - return default; + return null; } [RequiresUnreferencedCode("Calls System.Text.Json.JsonSerializer.Deserialize(String, JsonSerializerOptions)")] diff --git a/src/modules/Elsa.Workflows.Runtime/Entities/ActivityExecutionRecord.cs b/src/modules/Elsa.Workflows.Runtime/Entities/ActivityExecutionRecord.cs index 429516fa8..64d148618 100644 --- a/src/modules/Elsa.Workflows.Runtime/Entities/ActivityExecutionRecord.cs +++ b/src/modules/Elsa.Workflows.Runtime/Entities/ActivityExecutionRecord.cs @@ -57,7 +57,7 @@ public class ActivityExecutionRecord : Entity, ILogRecord /// /// Any properties provided by the activity. /// - public IDictionary Properties { get; set; } = new Dictionary(); + public IDictionary? Properties { get; set; } /// /// Gets or sets the exception that occurred during the activity execution. diff --git a/src/modules/Elsa.Workflows.Runtime/LogPersistence/Models/ActivityLogPersistenceModeMap.cs b/src/modules/Elsa.Workflows.Runtime/LogPersistence/Models/ActivityLogPersistenceModeMap.cs index a8472268c..5513a5617 100644 --- a/src/modules/Elsa.Workflows.Runtime/LogPersistence/Models/ActivityLogPersistenceModeMap.cs +++ b/src/modules/Elsa.Workflows.Runtime/LogPersistence/Models/ActivityLogPersistenceModeMap.cs @@ -6,4 +6,5 @@ public class ActivityLogPersistenceModeMap { public IDictionary Inputs { get; set; } = new Dictionary(); public IDictionary Outputs { get; set; } = new Dictionary(); + public LogPersistenceMode InternalState { get; set; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/LogPersistence/Services/ActivityPropertyLogPersistenceEvaluator.cs b/src/modules/Elsa.Workflows.Runtime/LogPersistence/Services/ActivityPropertyLogPersistenceEvaluator.cs index 1f5e2c2e9..2113e290b 100644 --- a/src/modules/Elsa.Workflows.Runtime/LogPersistence/Services/ActivityPropertyLogPersistenceEvaluator.cs +++ b/src/modules/Elsa.Workflows.Runtime/LogPersistence/Services/ActivityPropertyLogPersistenceEvaluator.cs @@ -31,6 +31,7 @@ namespace Elsa.Workflows.Runtime; * "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": "..." } } + * "internalState": { "evaluationMode": "Strategy", "strategyType": "Elsa.Workflows.LogPersistence.Strategies.Inherit, Elsa.Workflows.Core", "expression": "..." } * } * } */ @@ -73,6 +74,7 @@ public class ActivityPropertyLogPersistenceEvaluator : IActivityPropertyLogPersi await EvaluatePropertiesAsync(context, "inputs", context.ActivityDescriptor.Inputs, legacyProps, configProps, defaultMode, map.Inputs, cancellationToken); await EvaluatePropertiesAsync(context, "outputs", context.ActivityDescriptor.Outputs, legacyProps, configProps, defaultMode, map.Outputs, cancellationToken); + map.InternalState = await EvaluateInternalStateModeAsync(context.ExpressionExecutionContext, context.Activity.CustomProperties, defaultMode, cancellationToken); return map; } @@ -85,8 +87,7 @@ public class ActivityPropertyLogPersistenceEvaluator : IActivityPropertyLogPersi return await GetPersistablePropertiesAsync(context, outputs, "outputs", legacyProps, configProps, defaultMode, cancellationToken); } - private async Task<(IDictionary legacyProps, IDictionary configProps, LogPersistenceMode defaultMode)> - GetPersistenceDefaultsAsync(ActivityExecutionContext context, CancellationToken cancellationToken) + private async Task<(IDictionary legacyProps, IDictionary configProps, LogPersistenceMode defaultMode)> GetPersistenceDefaultsAsync(ActivityExecutionContext context, CancellationToken cancellationToken) { var legacyProps = context.Activity.CustomProperties.GetValueOrDefault>(LegacyKey, () => new Dictionary())!; var rootContext = context.WorkflowExecutionContext.ActivityExecutionContexts.First(x => x.ParentActivityExecutionContext == null); @@ -133,6 +134,18 @@ public class ActivityPropertyLogPersistenceEvaluator : IActivityPropertyLogPersi var mode = legacySection.GetValueOrDefault(key, () => defaultMode); return ResolveMode(mode, () => defaultMode); } + + private async Task EvaluateInternalStateModeAsync( + ExpressionExecutionContext executionContext, + IDictionary currentConfig, + LogPersistenceMode defaultMode, + CancellationToken cancellationToken) + { + var configObject = currentConfig.GetValueOrDefault("internalState", () => new Dictionary())!; + var config = ConvertToConfig(configObject); + if (config != null) return await EvaluateConfigAsync(config, executionContext, () => defaultMode, cancellationToken); + return LogPersistenceMode.Inherit; + } private async Task> GetPersistablePropertiesAsync( ActivityExecutionContext context, diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs index 8a3b04c69..054da084f 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultActivityExecutionMapper.cs @@ -13,8 +13,10 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper var outputs = source.GetOutputs(); var inputs = source.GetInputs(); var persistenceMap = source.GetLogPersistenceModeMap(); - var persistableInputs = GetPersistableProperties(inputs, persistenceMap.Inputs); - var persistableOutputs = GetPersistableProperties(outputs, persistenceMap.Outputs); + var persistableInputs = GetPersistableInputOutput(inputs, persistenceMap.Inputs); + var persistableOutputs = GetPersistableInputOutput(outputs, persistenceMap.Outputs); + var persistableProperties = GetPersistableDictionary(source.Properties!, persistenceMap.InternalState); + var persistablePayload = GetPersistableDictionary(payload!, persistenceMap.InternalState); return new() { @@ -26,8 +28,8 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper ActivityName = source.Activity.Name, ActivityState = persistableInputs, Outputs = persistableOutputs, - Properties = source.Properties, - Payload = payload, + Properties = persistableProperties, + Payload = persistablePayload!, Exception = ExceptionState.FromException(source.Exception), ActivityTypeVersion = source.Activity.Version, StartedAt = source.StartedAt, @@ -43,7 +45,7 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper return Task.FromResult(Map(source)); } - private IDictionary GetPersistableProperties(IDictionary state, IDictionary map) + private IDictionary GetPersistableInputOutput(IDictionary state, IDictionary map) { var result = new Dictionary(); foreach (var stateEntry in state) @@ -55,7 +57,12 @@ public class DefaultActivityExecutionMapper : IActivityExecutionMapper return result; } - + + private IDictionary? GetPersistableDictionary(IDictionary dictionary, LogPersistenceMode mode) + { + return mode == LogPersistenceMode.Include ? dictionary : null; + } + private static IDictionary GetPayload(ActivityExecutionContext source) { var outcomes = source.JournalData.TryGetValue("Outcomes", out var resultValue) ? resultValue as string[] : null; diff --git a/test/component/Elsa.Workflows.ComponentTests/Scenarios/LogPersistenceModes/InputOutputLoggingTests.cs b/test/component/Elsa.Workflows.ComponentTests/Scenarios/LogPersistenceModes/InputOutputLoggingTests.cs index 4d2a5629e..ad6e60b29 100644 --- a/test/component/Elsa.Workflows.ComponentTests/Scenarios/LogPersistenceModes/InputOutputLoggingTests.cs +++ b/test/component/Elsa.Workflows.ComponentTests/Scenarios/LogPersistenceModes/InputOutputLoggingTests.cs @@ -32,7 +32,7 @@ public class InputOutputLoggingTests(App app) : AppComponentTest(app) { var activityExecutionRecord = activityExecutionRecords[i]; var shouldBeIncluded = shouldBeIncludedArray[i]; - var isIncluded = activityExecutionRecord.ActivityState!.ContainsKey(nameof(WriteLine.Text)); + var isIncluded = activityExecutionRecord.ActivityState?.ContainsKey(nameof(WriteLine.Text)) == true; Assert.Equal(shouldBeIncluded, isIncluded); } } @@ -55,8 +55,8 @@ public class InputOutputLoggingTests(App app) : AppComponentTest(app) await ExecuteWorkflowAsync("input-output-logging-3"); var setOutput1Record = await GetRecordByActivityNameAsync("SetOutput1"); var setOutput2Record = await GetRecordByActivityNameAsync("SetOutput2"); - var output1IsIncluded = setOutput1Record.ActivityState!.ContainsKey("OutputName"); - var output2IsIncluded = setOutput2Record.ActivityState!.ContainsKey("OutputName"); + var output1IsIncluded = setOutput1Record.ActivityState?.ContainsKey("OutputName") == true; + var output2IsIncluded = setOutput2Record.ActivityState?.ContainsKey("OutputName") == true; Assert.False(output1IsIncluded); Assert.True(output2IsIncluded);