From fa44259cd1b918bcb8db17f13d5d25e027dbfc3a Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Mon, 5 Feb 2024 22:19:51 +0100 Subject: [PATCH] Add serialization tests and refactor serialization classes Implemented new integration tests for serialization of programmatic workflows. Additionally, major refactoring was performed on the serialization classes. This involved moving the classes to new namespaces and enhancing their functionalities to correctly handle null inputs and to support the serialization of activities and expressions. Also, updated Elsa.sln.DotSettings and JsonActivitySerializer to handle new serializers. --- Elsa.sln.DotSettings | 1 + .../Converters/ActivityJsonConverter.cs | 2 +- .../ActivityJsonConverterFactory.cs | 2 +- .../Converters/InputJsonConverter.cs | 2 +- .../Converters/InputJsonConverterFactory.cs | 2 +- .../JsonIgnoreCompositeRootConverter.cs | 12 +++++- .../Converters/OutputJsonConverter.cs | 2 +- .../Converters/OutputJsonConverterFactory.cs | 2 +- .../Serializers/JsonActivitySerializer.cs | 1 + .../SerializationOptionsConfigurator.cs | 4 +- .../Features/WorkflowManagementFeature.cs | 2 +- .../Tests.cs | 40 +++++++++++++++++++ .../Workflows.cs | 20 ++++++++++ 13 files changed, 82 insertions(+), 10 deletions(-) rename src/modules/{Elsa.Workflows.Management => Elsa.Workflows.Core}/Serialization/Converters/ActivityJsonConverter.cs (99%) rename src/modules/{Elsa.Workflows.Management => Elsa.Workflows.Core}/Serialization/Converters/ActivityJsonConverterFactory.cs (94%) rename src/modules/{Elsa.Workflows.Management => Elsa.Workflows.Core}/Serialization/Converters/InputJsonConverter.cs (98%) rename src/modules/{Elsa.Workflows.Management => Elsa.Workflows.Core}/Serialization/Converters/InputJsonConverterFactory.cs (94%) rename src/modules/{Elsa.Workflows.Management => Elsa.Workflows.Core}/Serialization/Converters/OutputJsonConverter.cs (97%) rename src/modules/{Elsa.Workflows.Management => Elsa.Workflows.Core}/Serialization/Converters/OutputJsonConverterFactory.cs (94%) rename src/modules/{Elsa.Workflows.Management/Serialization => Elsa.Workflows.Core/Serialization/Serializers}/SerializationOptionsConfigurator.cs (89%) create mode 100644 test/integration/Elsa.IntegrationTests/Serialization/ProgrammaticWorkflowsAndDelegates/Tests.cs create mode 100644 test/integration/Elsa.IntegrationTests/Serialization/ProgrammaticWorkflowsAndDelegates/Workflows.cs diff --git a/Elsa.sln.DotSettings b/Elsa.sln.DotSettings index d88a97a28..19414abf6 100644 --- a/Elsa.sln.DotSettings +++ b/Elsa.sln.DotSettings @@ -4,6 +4,7 @@ 1 False True + UI True True True diff --git a/src/modules/Elsa.Workflows.Management/Serialization/Converters/ActivityJsonConverter.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/ActivityJsonConverter.cs similarity index 99% rename from src/modules/Elsa.Workflows.Management/Serialization/Converters/ActivityJsonConverter.cs rename to src/modules/Elsa.Workflows.Core/Serialization/Converters/ActivityJsonConverter.cs index dd52d01de..1a59fdf26 100644 --- a/src/modules/Elsa.Workflows.Management/Serialization/Converters/ActivityJsonConverter.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/ActivityJsonConverter.cs @@ -10,7 +10,7 @@ using Elsa.Workflows.Models; using Humanizer; using Microsoft.Extensions.Logging; -namespace Elsa.Workflows.Management.Serialization.Converters; +namespace Elsa.Workflows.Serialization.Converters; /// /// (De)serializes objects of type . diff --git a/src/modules/Elsa.Workflows.Management/Serialization/Converters/ActivityJsonConverterFactory.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/ActivityJsonConverterFactory.cs similarity index 94% rename from src/modules/Elsa.Workflows.Management/Serialization/Converters/ActivityJsonConverterFactory.cs rename to src/modules/Elsa.Workflows.Core/Serialization/Converters/ActivityJsonConverterFactory.cs index 9cca6e3a2..7baa83d7d 100644 --- a/src/modules/Elsa.Workflows.Management/Serialization/Converters/ActivityJsonConverterFactory.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/ActivityJsonConverterFactory.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; using Elsa.Workflows.Contracts; using Microsoft.Extensions.DependencyInjection; -namespace Elsa.Workflows.Management.Serialization.Converters; +namespace Elsa.Workflows.Serialization.Converters; /// /// Creates instances of . diff --git a/src/modules/Elsa.Workflows.Management/Serialization/Converters/InputJsonConverter.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/InputJsonConverter.cs similarity index 98% rename from src/modules/Elsa.Workflows.Management/Serialization/Converters/InputJsonConverter.cs rename to src/modules/Elsa.Workflows.Core/Serialization/Converters/InputJsonConverter.cs index fdf2e229b..abd643cd7 100644 --- a/src/modules/Elsa.Workflows.Management/Serialization/Converters/InputJsonConverter.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/InputJsonConverter.cs @@ -5,7 +5,7 @@ using Elsa.Expressions.Helpers; using Elsa.Expressions.Models; using Elsa.Workflows.Models; -namespace Elsa.Workflows.Management.Serialization.Converters; +namespace Elsa.Workflows.Serialization.Converters; /// /// Serializes objects. diff --git a/src/modules/Elsa.Workflows.Management/Serialization/Converters/InputJsonConverterFactory.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/InputJsonConverterFactory.cs similarity index 94% rename from src/modules/Elsa.Workflows.Management/Serialization/Converters/InputJsonConverterFactory.cs rename to src/modules/Elsa.Workflows.Core/Serialization/Converters/InputJsonConverterFactory.cs index 144edb67d..9fa8a20bc 100644 --- a/src/modules/Elsa.Workflows.Management/Serialization/Converters/InputJsonConverterFactory.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/InputJsonConverterFactory.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; using Elsa.Workflows.Models; using Microsoft.Extensions.DependencyInjection; -namespace Elsa.Workflows.Management.Serialization.Converters; +namespace Elsa.Workflows.Serialization.Converters; /// /// A JSON converter factory that creates instances. diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Converters/JsonIgnoreCompositeRootConverter.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/JsonIgnoreCompositeRootConverter.cs index aaf442e44..dfd7072b8 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Converters/JsonIgnoreCompositeRootConverter.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/JsonIgnoreCompositeRootConverter.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using System.Reflection; using System.Text.Json; using System.Text.Json.Serialization; @@ -18,6 +19,7 @@ public class JsonIgnoreCompositeRootConverter : JsonConverter } /// + [UnconditionalSuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "")] public override void Write(Utf8JsonWriter writer, IActivity? value, JsonSerializerOptions options) { writer.WriteStartObject(); @@ -35,7 +37,15 @@ public class JsonIgnoreCompositeRootConverter : JsonConverter var propName = options.PropertyNamingPolicy?.ConvertName(property.Name) ?? property.Name; writer.WritePropertyName(propName); - JsonSerializer.Serialize(writer, property.GetValue(value), newOptions); + var input = property.GetValue(value); + + if (input == null) + { + writer.WriteNullValue(); + continue; + } + + JsonSerializer.Serialize(writer, input, newOptions); } writer.WriteEndObject(); diff --git a/src/modules/Elsa.Workflows.Management/Serialization/Converters/OutputJsonConverter.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/OutputJsonConverter.cs similarity index 97% rename from src/modules/Elsa.Workflows.Management/Serialization/Converters/OutputJsonConverter.cs rename to src/modules/Elsa.Workflows.Core/Serialization/Converters/OutputJsonConverter.cs index a78444c71..ecefa40b6 100644 --- a/src/modules/Elsa.Workflows.Management/Serialization/Converters/OutputJsonConverter.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/OutputJsonConverter.cs @@ -5,7 +5,7 @@ using Elsa.Expressions.Extensions; using Elsa.Workflows.Memory; using Elsa.Workflows.Models; -namespace Elsa.Workflows.Management.Serialization.Converters; +namespace Elsa.Workflows.Serialization.Converters; /// /// Serializes objects. diff --git a/src/modules/Elsa.Workflows.Management/Serialization/Converters/OutputJsonConverterFactory.cs b/src/modules/Elsa.Workflows.Core/Serialization/Converters/OutputJsonConverterFactory.cs similarity index 94% rename from src/modules/Elsa.Workflows.Management/Serialization/Converters/OutputJsonConverterFactory.cs rename to src/modules/Elsa.Workflows.Core/Serialization/Converters/OutputJsonConverterFactory.cs index 48dcc1dbf..752198fa9 100644 --- a/src/modules/Elsa.Workflows.Management/Serialization/Converters/OutputJsonConverterFactory.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Converters/OutputJsonConverterFactory.cs @@ -3,7 +3,7 @@ using System.Text.Json.Serialization; using Elsa.Workflows.Models; using Microsoft.Extensions.DependencyInjection; -namespace Elsa.Workflows.Management.Serialization.Converters; +namespace Elsa.Workflows.Serialization.Converters; /// /// A JSON converter factory that creates instances. diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonActivitySerializer.cs b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonActivitySerializer.cs index a8b260ff3..7ce83bb14 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonActivitySerializer.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/JsonActivitySerializer.cs @@ -44,5 +44,6 @@ public class JsonActivitySerializer : ConfigurableSerializer, IActivitySerialize protected override void AddConverters(JsonSerializerOptions options) { options.Converters.Add(CreateInstance()); + options.Converters.Add(CreateInstance()); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Serialization/SerializationOptionsConfigurator.cs b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/SerializationOptionsConfigurator.cs similarity index 89% rename from src/modules/Elsa.Workflows.Management/Serialization/SerializationOptionsConfigurator.cs rename to src/modules/Elsa.Workflows.Core/Serialization/Serializers/SerializationOptionsConfigurator.cs index 0a35ebc6b..53a3d7e26 100644 --- a/src/modules/Elsa.Workflows.Management/Serialization/SerializationOptionsConfigurator.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Serializers/SerializationOptionsConfigurator.cs @@ -1,8 +1,8 @@ using System.Text.Json; -using Elsa.Workflows.Management.Serialization.Converters; +using Elsa.Workflows.Serialization.Converters; using Microsoft.Extensions.DependencyInjection; -namespace Elsa.Workflows.Management.Serialization; +namespace Elsa.Workflows.Serialization.Serializers; /// /// Configures the JSON serialization options with support for serializing and deserializing activities and expressions. diff --git a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs index df1288171..e703e9444 100644 --- a/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs +++ b/src/modules/Elsa.Workflows.Management/Features/WorkflowManagementFeature.cs @@ -20,8 +20,8 @@ using Elsa.Workflows.Management.Materializers; using Elsa.Workflows.Management.Models; using Elsa.Workflows.Management.Options; using Elsa.Workflows.Management.Providers; -using Elsa.Workflows.Management.Serialization; using Elsa.Workflows.Management.Services; +using Elsa.Workflows.Serialization.Serializers; using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; diff --git a/test/integration/Elsa.IntegrationTests/Serialization/ProgrammaticWorkflowsAndDelegates/Tests.cs b/test/integration/Elsa.IntegrationTests/Serialization/ProgrammaticWorkflowsAndDelegates/Tests.cs new file mode 100644 index 000000000..63b7cd54b --- /dev/null +++ b/test/integration/Elsa.IntegrationTests/Serialization/ProgrammaticWorkflowsAndDelegates/Tests.cs @@ -0,0 +1,40 @@ +using Elsa.Extensions; +using Elsa.Testing.Shared; +using Elsa.Workflows.Contracts; +using Microsoft.Extensions.DependencyInjection; +using Xunit; +using Xunit.Abstractions; + +namespace Elsa.IntegrationTests.Serialization.ProgrammaticWorkflowsAndDelegates; + +/// +/// Contains tests for variable expressions serialization. +/// +public class Tests +{ + private readonly IActivitySerializer _activitySerializer; + private readonly IWorkflowBuilder _workflowBuilder; + + /// + /// Initializes a new instance of the class. + /// + public Tests(ITestOutputHelper testOutputHelper) + { + var serviceProvider = new TestApplicationBuilder(testOutputHelper).Build(); + _activitySerializer = serviceProvider.GetRequiredService(); + var workflowBuilderFactory = serviceProvider.GetRequiredService(); + _workflowBuilder = workflowBuilderFactory.CreateBuilder(); + } + + /// + /// Tests that programmatic workflows with inputs using delegates do not throw exceptions when serialized. + /// + [Fact(DisplayName = "Programmatic workflows with inputs using delegates do not throw exceptions when serialized")] + public async Task Test1() + { + var workflow = await _workflowBuilder.BuildWorkflowAsync(); + var serializedWorkflow = _activitySerializer.Serialize(workflow); + + // If it reached here, the test passed + } +} \ No newline at end of file diff --git a/test/integration/Elsa.IntegrationTests/Serialization/ProgrammaticWorkflowsAndDelegates/Workflows.cs b/test/integration/Elsa.IntegrationTests/Serialization/ProgrammaticWorkflowsAndDelegates/Workflows.cs new file mode 100644 index 000000000..e813908ee --- /dev/null +++ b/test/integration/Elsa.IntegrationTests/Serialization/ProgrammaticWorkflowsAndDelegates/Workflows.cs @@ -0,0 +1,20 @@ +using Elsa.Extensions; +using Elsa.Workflows; +using Elsa.Workflows.Activities; +using Elsa.Workflows.Contracts; + +namespace Elsa.IntegrationTests.Serialization.ProgrammaticWorkflowsAndDelegates; + +/// +public class GreeterWorkflow : WorkflowBase +{ + /// + protected override void Build(IWorkflowBuilder builder) + { + var messageInput = builder.WithInput("Message", "The message to write to the console."); + + builder.Name = "Greeter Workflow"; + builder.Inputs.Add(messageInput); + builder.Root = new WriteLine(context => context.GetInput(messageInput)); + } +} \ No newline at end of file