From a67361c6aee501466e1ae4be72c125d37e3d6ae0 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 28 Apr 2023 14:31:38 +0200 Subject: [PATCH] Fix variable type serialization --- .../WorkflowBuilderFactory.cs | 9 +++- .../Contracts/IWorkflowBuilderFactory.cs | 7 +++ .../Converters/VariableConverter.cs | 13 +++-- .../Services/JsonPayloadSerializer.cs | 3 +- .../Serialization/VariableTypes/Models.cs | 7 +++ .../Serialization/VariableTypes/Tests.cs | 47 +++++++++++++++++++ .../Serialization/VariableTypes/Workflows.cs | 14 ++++++ 7 files changed, 93 insertions(+), 7 deletions(-) rename src/modules/Elsa.Workflows.Core/{Services => Builders}/WorkflowBuilderFactory.cs (59%) create mode 100644 test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Models.cs create mode 100644 test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Tests.cs create mode 100644 test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Workflows.cs diff --git a/src/modules/Elsa.Workflows.Core/Services/WorkflowBuilderFactory.cs b/src/modules/Elsa.Workflows.Core/Builders/WorkflowBuilderFactory.cs similarity index 59% rename from src/modules/Elsa.Workflows.Core/Services/WorkflowBuilderFactory.cs rename to src/modules/Elsa.Workflows.Core/Builders/WorkflowBuilderFactory.cs index 835a48be2..257d095e8 100644 --- a/src/modules/Elsa.Workflows.Core/Services/WorkflowBuilderFactory.cs +++ b/src/modules/Elsa.Workflows.Core/Builders/WorkflowBuilderFactory.cs @@ -1,10 +1,17 @@ using Elsa.Workflows.Core.Contracts; -namespace Elsa.Workflows.Core.Services; +namespace Elsa.Workflows.Core.Builders; +/// public class WorkflowBuilderFactory : IWorkflowBuilderFactory { private readonly Func _factory; + + /// + /// Initializes a new instance of the class. + /// public WorkflowBuilderFactory(Func factory) => _factory = factory; + + /// public IWorkflowBuilder CreateBuilder() => _factory(); } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowBuilderFactory.cs b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowBuilderFactory.cs index 1f2dd1140..20f9fa82b 100644 --- a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowBuilderFactory.cs +++ b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowBuilderFactory.cs @@ -1,6 +1,13 @@ namespace Elsa.Workflows.Core.Contracts; +/// +/// A factory of workflow builders. +/// public interface IWorkflowBuilderFactory { + /// + /// Creates a new workflow builder. + /// + /// A new workflow builder. IWorkflowBuilder CreateBuilder(); } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Converters/VariableConverter.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/VariableConverter.cs index c577eed53..ac1637aa1 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Converters/VariableConverter.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/VariableConverter.cs @@ -25,7 +25,7 @@ public class VariableConverter : JsonConverter } /// - public override Variable? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + public override Variable Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { var newOptions = new JsonSerializerOptions(options); newOptions.Converters.Add(new JsonPrimitiveToStringConverter()); @@ -42,17 +42,20 @@ public class VariableConverter : JsonConverter JsonSerializer.Serialize(writer, model, options); } - private Variable? Map(VariableModel source) + private Variable Map(VariableModel source) { + var typeName = source.TypeName; + if (string.IsNullOrWhiteSpace(source.TypeName)) - return null; + typeName = _wellKnownTypeRegistry.GetAliasOrDefault(typeof(object)); - if (!_wellKnownTypeRegistry.TryGetTypeOrDefault(source.TypeName, out var type)) - return null; + if (!_wellKnownTypeRegistry.TryGetTypeOrDefault(typeName, out var type)) + type = typeof(object); var variableGenericType = typeof(Variable<>).MakeGenericType(type); var variable = (Variable)Activator.CreateInstance(variableGenericType)!; + // ReSharper disable once NullCoalescingConditionIsAlwaysNotNullAccordingToAPIContract variable.Id = source.Id ?? Guid.NewGuid().ToString("N"); // Temporarily assign a new ID if the source doesn't have one. variable.Name = source.Name; diff --git a/src/modules/Elsa.Workflows.Core/Services/JsonPayloadSerializer.cs b/src/modules/Elsa.Workflows.Core/Services/JsonPayloadSerializer.cs index e221f76e4..0a25ff553 100644 --- a/src/modules/Elsa.Workflows.Core/Services/JsonPayloadSerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Services/JsonPayloadSerializer.cs @@ -53,8 +53,9 @@ public class JsonPayloadSerializer : IPayloadSerializer options.Converters.Add(new JsonStringEnumConverter()); options.Converters.Add(JsonMetadataServices.TimeSpanConverter); - options.Converters.Add(new PolymorphicObjectConverterFactory()); + options.Converters.Add(GetService()); options.Converters.Add(GetService()); + options.Converters.Add(GetService()); return options; } diff --git a/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Models.cs b/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Models.cs new file mode 100644 index 000000000..07ec234b3 --- /dev/null +++ b/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Models.cs @@ -0,0 +1,7 @@ +using System.Collections.Generic; +using System.Collections.ObjectModel; +using Elsa.Workflows.Core.Models; + +namespace Elsa.IntegrationTests.Serialization.VariableTypes; + +internal record VariablesContainer(ICollection Variables); \ No newline at end of file diff --git a/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Tests.cs b/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Tests.cs new file mode 100644 index 000000000..3b0b4ffa1 --- /dev/null +++ b/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Tests.cs @@ -0,0 +1,47 @@ +using System.Collections.ObjectModel; +using System.Linq; +using Elsa.Testing.Shared; +using Elsa.Workflows.Core.Contracts; +using Elsa.Workflows.Core.Models; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Elsa.IntegrationTests.Serialization.VariableTypes; + +public class Tests +{ + private readonly IPayloadSerializer _payloadSerializer; + + public Tests(ITestOutputHelper testOutputHelper) + { + var services = new TestApplicationBuilder(testOutputHelper).Build(); + _payloadSerializer = services.GetRequiredService(); + } + + [Fact(DisplayName = "Variable types remain intact after serialization")] + public void Test1() + { + // Create collection of variables to serialize. + var variables = new Collection(); + var model = new VariablesContainer(variables); + + // Create a typed variable. + var variable = new Variable(); + + // Add variable to collection. + variables.Add(variable); + + // Serialize collection. + var json = _payloadSerializer.Serialize(model); + + // Deserialize collection. + var deserializedModel = _payloadSerializer.Deserialize(json); + + // Get the first variable. + var deserializedVariable = deserializedModel.Variables.First(); + + // Assert that the variable is of the correct type. + Assert.IsType>(deserializedVariable); + } +} \ No newline at end of file diff --git a/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Workflows.cs b/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Workflows.cs new file mode 100644 index 000000000..bed83a323 --- /dev/null +++ b/test/integration/Elsa.IntegrationTests/Serialization/VariableTypes/Workflows.cs @@ -0,0 +1,14 @@ +using Elsa.Workflows.Core.Abstractions; +using Elsa.Workflows.Core.Activities; +using Elsa.Workflows.Core.Contracts; +using Elsa.Workflows.Runtime.Activities; + +namespace Elsa.IntegrationTests.Serialization.VariableTypes; + +class SampleWorkflow : WorkflowBase +{ + protected override void Build(IWorkflowBuilder workflow) + { + workflow.WithVariable(); + } +} \ No newline at end of file