diff --git a/src/api/Elsa.Api/Endpoints/WorkflowInstances/Get.cs b/src/api/Elsa.Api/Endpoints/WorkflowInstances/Get.cs index 7fee63a3c..9a3afdbe7 100644 --- a/src/api/Elsa.Api/Endpoints/WorkflowInstances/Get.cs +++ b/src/api/Elsa.Api/Endpoints/WorkflowInstances/Get.cs @@ -1,8 +1,8 @@ using System.Threading; using System.Threading.Tasks; -using Elsa.Management.Serialization; using Elsa.Mediator.Contracts; using Elsa.Persistence.Requests; +using Elsa.Serialization; using Microsoft.AspNetCore.Http; namespace Elsa.Api.Endpoints.WorkflowInstances; diff --git a/src/api/Elsa.Api/Endpoints/WorkflowInstances/List.cs b/src/api/Elsa.Api/Endpoints/WorkflowInstances/List.cs index 886a5cea8..c92698b5b 100644 --- a/src/api/Elsa.Api/Endpoints/WorkflowInstances/List.cs +++ b/src/api/Elsa.Api/Endpoints/WorkflowInstances/List.cs @@ -1,9 +1,9 @@ using System.Threading; using System.Threading.Tasks; -using Elsa.Management.Serialization; using Elsa.Mediator.Contracts; using Elsa.Persistence.Entities; using Elsa.Persistence.Requests; +using Elsa.Serialization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/src/api/Elsa.Api/Endpoints/Workflows/Get.cs b/src/api/Elsa.Api/Endpoints/Workflows/Get.cs index d344654d5..a9ba6b8dc 100644 --- a/src/api/Elsa.Api/Endpoints/Workflows/Get.cs +++ b/src/api/Elsa.Api/Endpoints/Workflows/Get.cs @@ -1,9 +1,9 @@ using System.Threading; using System.Threading.Tasks; -using Elsa.Management.Serialization; using Elsa.Mediator.Contracts; using Elsa.Persistence.Models; using Elsa.Persistence.Requests; +using Elsa.Serialization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/src/api/Elsa.Api/Endpoints/Workflows/GetMany.cs b/src/api/Elsa.Api/Endpoints/Workflows/GetMany.cs index d08b898b6..f60c65704 100644 --- a/src/api/Elsa.Api/Endpoints/Workflows/GetMany.cs +++ b/src/api/Elsa.Api/Endpoints/Workflows/GetMany.cs @@ -1,10 +1,10 @@ using System; using System.Threading; using System.Threading.Tasks; -using Elsa.Management.Serialization; using Elsa.Mediator.Contracts; using Elsa.Persistence.Models; using Elsa.Persistence.Requests; +using Elsa.Serialization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/src/api/Elsa.Api/Endpoints/Workflows/List.cs b/src/api/Elsa.Api/Endpoints/Workflows/List.cs index 8267b2d3a..e0d2a2bf8 100644 --- a/src/api/Elsa.Api/Endpoints/Workflows/List.cs +++ b/src/api/Elsa.Api/Endpoints/Workflows/List.cs @@ -1,9 +1,9 @@ using System.Threading; using System.Threading.Tasks; -using Elsa.Management.Serialization; using Elsa.Mediator.Contracts; using Elsa.Persistence.Models; using Elsa.Persistence.Requests; +using Elsa.Serialization; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; diff --git a/src/api/Elsa.Api/Endpoints/Workflows/Post.cs b/src/api/Elsa.Api/Endpoints/Workflows/Post.cs index a9c735daa..68772b6e1 100644 --- a/src/api/Elsa.Api/Endpoints/Workflows/Post.cs +++ b/src/api/Elsa.Api/Endpoints/Workflows/Post.cs @@ -3,8 +3,8 @@ using System.Threading.Tasks; using Elsa.Activities; using Elsa.Contracts; using Elsa.Management.Contracts; -using Elsa.Management.Serialization; using Elsa.Persistence.Mappers; +using Elsa.Serialization; using Microsoft.AspNetCore.Http; namespace Elsa.Api.Endpoints.Workflows; diff --git a/src/core/Elsa.Management/Contracts/ISerializationOptionsConfigurator.cs b/src/core/Elsa.Core/Contracts/ISerializationOptionsConfigurator.cs similarity index 77% rename from src/core/Elsa.Management/Contracts/ISerializationOptionsConfigurator.cs rename to src/core/Elsa.Core/Contracts/ISerializationOptionsConfigurator.cs index e90e9d2b6..14ccde39d 100644 --- a/src/core/Elsa.Management/Contracts/ISerializationOptionsConfigurator.cs +++ b/src/core/Elsa.Core/Contracts/ISerializationOptionsConfigurator.cs @@ -1,6 +1,6 @@ using System.Text.Json; -namespace Elsa.Management.Contracts; +namespace Elsa.Contracts; public interface ISerializationOptionsConfigurator { diff --git a/src/core/Elsa.Core/Elsa.Core.csproj b/src/core/Elsa.Core/Elsa.Core.csproj index d9bbeb2d2..46a3bda7a 100644 --- a/src/core/Elsa.Core/Elsa.Core.csproj +++ b/src/core/Elsa.Core/Elsa.Core.csproj @@ -9,10 +9,15 @@ + + + + + diff --git a/src/core/Elsa.Core/Extensions/PropertyAccessorExtensions.cs b/src/core/Elsa.Core/Extensions/PropertyAccessorExtensions.cs index a468c5afc..295ba4545 100644 --- a/src/core/Elsa.Core/Extensions/PropertyAccessorExtensions.cs +++ b/src/core/Elsa.Core/Extensions/PropertyAccessorExtensions.cs @@ -1,4 +1,3 @@ -using System; using System.Linq.Expressions; using System.Reflection; diff --git a/src/core/Elsa.Core/Helpers/ObjectConverter.cs b/src/core/Elsa.Core/Helpers/ObjectConverter.cs index 039cca57b..02a57522f 100644 --- a/src/core/Elsa.Core/Helpers/ObjectConverter.cs +++ b/src/core/Elsa.Core/Helpers/ObjectConverter.cs @@ -1,5 +1,11 @@ using System.ComponentModel; +using System.Text.Json; +using System.Text.Json.Serialization; +using Dahomey.Json; +using Dahomey.Json.Serialization.Conventions; using Elsa.Exceptions; +using DahomeyJsonObject = System.Text.Json.JsonObject; +using DahomeyJsonNode = System.Text.Json.JsonNode; namespace Elsa.Helpers; @@ -11,17 +17,34 @@ public static class ObjectConverter { if (value == null) return default!; - + var sourceType = value.GetType(); if (sourceType == targetType) return value; + var options = new JsonSerializerOptions(); + options.SetupExtensions().SetReferenceHandling(ReferenceHandling.Preserve); + var registry = options.GetDiscriminatorConventionRegistry(); + registry.ClearConventions(); + registry.RegisterConvention(new DefaultDiscriminatorConvention(options, "_type")); + + options.PropertyNamingPolicy = JsonNamingPolicy.CamelCase; + options.ReferenceHandler = ReferenceHandler.Preserve; + options.PropertyNameCaseInsensitive = true; + options.Converters.Add(new JsonStringEnumConverter()); + + if (value is DahomeyJsonNode { ValueKind: JsonValueKind.Object } dahomyJsonObject) + return ToObject(dahomyJsonObject, targetType, options); + + if (value is JsonElement { ValueKind: JsonValueKind.Object } jsonObject) + return jsonObject.Deserialize(targetType, options); + var underlyingTargetType = Nullable.GetUnderlyingType(targetType) ?? targetType; - + if (targetType == typeof(object)) return value; - + if (underlyingTargetType.IsInstanceOfType(value)) return value; @@ -29,7 +52,7 @@ public static class ObjectConverter if (underlyingSourceType == underlyingTargetType) return value; - + var targetTypeConverter = TypeDescriptor.GetConverter(underlyingTargetType); if (targetTypeConverter.CanConvertFrom(underlyingSourceType)) @@ -55,4 +78,10 @@ public static class ObjectConverter throw new TypeConversionException($"Failed to convert an object of type {sourceType} to {underlyingTargetType}", value, underlyingTargetType, e); } } + + private static object? ToObject(this DahomeyJsonNode node, Type type, JsonSerializerOptions? options = null) + { + using var arrayBufferWriter = new Dahomey.Json.Util.ArrayBufferWriter(); + return JsonSerializer.Deserialize(node.ToString(), type, options); + } } \ No newline at end of file diff --git a/src/core/Elsa.Core/Models/Variable.cs b/src/core/Elsa.Core/Models/Variable.cs index 42197f2e6..c7ec48501 100644 --- a/src/core/Elsa.Core/Models/Variable.cs +++ b/src/core/Elsa.Core/Models/Variable.cs @@ -20,7 +20,7 @@ public class Variable : RegisterLocationReference } public string? Name { get; set; } - public object? DefaultValue { get; } + public object? DefaultValue { get; set; } public override RegisterLocation Declare() => new(DefaultValue); } diff --git a/src/core/Elsa.Core/Serialization/CustomDiscriminatorConvention.cs b/src/core/Elsa.Core/Serialization/CustomDiscriminatorConvention.cs new file mode 100644 index 000000000..eb8ca0003 --- /dev/null +++ b/src/core/Elsa.Core/Serialization/CustomDiscriminatorConvention.cs @@ -0,0 +1,35 @@ +using System.Text; +using System.Text.Json; +using System.Text.Json.Serialization; +using Dahomey.Json; +using Dahomey.Json.Serialization.Conventions; + +namespace Elsa.Serialization; + +public class CustomDiscriminatorConvention : IDiscriminatorConvention +{ + private readonly JsonSerializerOptions _options; + private readonly JsonConverter _jsonConverter; + + public CustomDiscriminatorConvention(JsonSerializerOptions options) + { + _options = options; + _jsonConverter = options.GetConverter(); + } + + public ReadOnlySpan MemberName => Encoding.UTF8.GetBytes("$type"); + + public bool TryRegisterType(Type type) => true; + + public Type ReadDiscriminator(ref Utf8JsonReader reader) + { + var discriminator = _jsonConverter.Read(ref reader, typeof(string), _options)!; + return Type.GetType(discriminator)!; + } + + public void WriteDiscriminator(Utf8JsonWriter writer, Type actualType) + { + var discriminator = actualType.AssemblyQualifiedName!; + _jsonConverter.Write(writer, discriminator, _options); + } +} \ No newline at end of file diff --git a/src/core/Elsa.Management/Serialization/WorkflowSerializerOptionsProvider.cs b/src/core/Elsa.Core/Serialization/WorkflowSerializerOptionsProvider.cs similarity index 54% rename from src/core/Elsa.Management/Serialization/WorkflowSerializerOptionsProvider.cs rename to src/core/Elsa.Core/Serialization/WorkflowSerializerOptionsProvider.cs index 62397648f..81cb97838 100644 --- a/src/core/Elsa.Management/Serialization/WorkflowSerializerOptionsProvider.cs +++ b/src/core/Elsa.Core/Serialization/WorkflowSerializerOptionsProvider.cs @@ -1,39 +1,47 @@ using System.Text.Json; using System.Text.Json.Serialization; -using Elsa.Management.Contracts; -using Elsa.Management.Serialization.Converters; +using System.Text.Json.Serialization.Metadata; +using Dahomey.Json; +using Dahomey.Json.Attributes; +using Dahomey.Json.Serialization.Conventions; +using Elsa.Contracts; using Microsoft.Extensions.DependencyInjection; -namespace Elsa.Management.Serialization; +namespace Elsa.Serialization; public class WorkflowSerializerOptionsProvider { private readonly IEnumerable _configurators; private readonly IServiceProvider _serviceProvider; - + public WorkflowSerializerOptionsProvider(IEnumerable configurators, IServiceProvider serviceProvider) { _configurators = configurators; _serviceProvider = serviceProvider; } - public JsonSerializerOptions CreateApiOptions() => CreateDefaultOptions(); - public JsonSerializerOptions CreatePersistenceOptions() => CreateDefaultOptions(ReferenceHandler.Preserve); - - public JsonSerializerOptions CreateDefaultOptions(ReferenceHandler? referenceHandler = default) + public JsonSerializerOptions CreateApiOptions() => CreateDefaultOptions(ReferenceHandling.Ignore); + public JsonSerializerOptions CreatePersistenceOptions() => CreateDefaultOptions(ReferenceHandling.Preserve); + + public JsonSerializerOptions CreateDefaultOptions(ReferenceHandling referenceHandling) { - var options = new JsonSerializerOptions() + var options = new JsonSerializerOptions { PropertyNamingPolicy = JsonNamingPolicy.CamelCase, - ReferenceHandler = referenceHandler, - Converters = - { - Create(), - Create(), - Create(), - Create() - } + PropertyNameCaseInsensitive = true }; + + options.Converters.Add(Create()); + options.Converters.Add(JsonMetadataServices.TimeSpanConverter); + + // Dahomey. + options.SetupExtensions(); + options.SetReferenceHandling(referenceHandling); + + // Setup polymorphic serialization. + var registry = options.GetDiscriminatorConventionRegistry(); + registry.RegisterConvention(new DefaultDiscriminatorConvention(options)); + registry.DiscriminatorPolicy = DiscriminatorPolicy.Auto; // Give external packages a chance to further configure the serializer options. E.g. to add additional converters. foreach (var configurator in _configurators) configurator.Configure(options); diff --git a/src/core/Elsa.Management/Elsa.Management.csproj b/src/core/Elsa.Management/Elsa.Management.csproj index eb3c36847..29ac35e76 100644 --- a/src/core/Elsa.Management/Elsa.Management.csproj +++ b/src/core/Elsa.Management/Elsa.Management.csproj @@ -7,6 +7,7 @@ + diff --git a/src/core/Elsa.Management/Extensions/ServiceCollectionExtensions.cs b/src/core/Elsa.Management/Extensions/ServiceCollectionExtensions.cs index 3d86b2617..5e1e9ae4a 100644 --- a/src/core/Elsa.Management/Extensions/ServiceCollectionExtensions.cs +++ b/src/core/Elsa.Management/Extensions/ServiceCollectionExtensions.cs @@ -4,6 +4,7 @@ using Elsa.Management.Options; using Elsa.Management.Providers; using Elsa.Management.Serialization; using Elsa.Management.Services; +using Elsa.Serialization; using Microsoft.Extensions.DependencyInjection; namespace Elsa.Management.Extensions; @@ -26,6 +27,7 @@ public static class ServiceCollectionExtensions .AddSingleton() .AddSingleton() .AddSingleton() + .AddSingleton() .AddSingleton(); } diff --git a/src/core/Elsa.Management/Serialization/SerializationOptionsConfigurator.cs b/src/core/Elsa.Management/Serialization/SerializationOptionsConfigurator.cs new file mode 100644 index 000000000..719f61cdf --- /dev/null +++ b/src/core/Elsa.Management/Serialization/SerializationOptionsConfigurator.cs @@ -0,0 +1,25 @@ +using System.Text.Json; +using Elsa.Contracts; +using Elsa.Management.Serialization.Converters; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Management.Serialization; + +public class SerializationOptionsConfigurator : ISerializationOptionsConfigurator +{ + private readonly IServiceProvider _serviceProvider; + + public SerializationOptionsConfigurator(IServiceProvider serviceProvider) + { + _serviceProvider = serviceProvider; + } + + public void Configure(JsonSerializerOptions options) + { + options.Converters.Add(Create()); + options.Converters.Add(Create()); + options.Converters.Add(Create()); + } + + private T Create() => ActivatorUtilities.CreateInstance(_serviceProvider); +} \ No newline at end of file diff --git a/src/modules/Elsa.Modules.Activities/Configurators/CustomSerializationOptionConfigurator.cs b/src/modules/Elsa.Modules.Activities/Configurators/CustomSerializationOptionConfigurator.cs index bb5921200..ea87fc7b8 100644 --- a/src/modules/Elsa.Modules.Activities/Configurators/CustomSerializationOptionConfigurator.cs +++ b/src/modules/Elsa.Modules.Activities/Configurators/CustomSerializationOptionConfigurator.cs @@ -1,6 +1,6 @@ using System.Text.Json; using System.Text.Json.Serialization; -using Elsa.Management.Contracts; +using Elsa.Contracts; using Elsa.Modules.Activities.Converters; using Microsoft.Extensions.DependencyInjection; diff --git a/src/modules/Elsa.Modules.Activities/Elsa.Modules.Activities.csproj b/src/modules/Elsa.Modules.Activities/Elsa.Modules.Activities.csproj index afddfe6dc..45d71909b 100644 --- a/src/modules/Elsa.Modules.Activities/Elsa.Modules.Activities.csproj +++ b/src/modules/Elsa.Modules.Activities/Elsa.Modules.Activities.csproj @@ -7,6 +7,7 @@ + diff --git a/src/modules/Elsa.Modules.Activities/Extensions/ServiceCollectionExtensions.cs b/src/modules/Elsa.Modules.Activities/Extensions/ServiceCollectionExtensions.cs index c1161d8b6..0897f2e5b 100644 --- a/src/modules/Elsa.Modules.Activities/Extensions/ServiceCollectionExtensions.cs +++ b/src/modules/Elsa.Modules.Activities/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,4 @@ using Elsa.Contracts; -using Elsa.Management.Contracts; using Elsa.Modules.Activities.Configurators; using Elsa.Modules.Activities.Resolvers; using Microsoft.Extensions.DependencyInjection; diff --git a/src/modules/Elsa.Modules.Scheduling/Handlers/ScheduleWorkflows.cs b/src/modules/Elsa.Modules.Scheduling/Handlers/ScheduleWorkflows.cs index 3d067493c..a2e2b5740 100644 --- a/src/modules/Elsa.Modules.Scheduling/Handlers/ScheduleWorkflows.cs +++ b/src/modules/Elsa.Modules.Scheduling/Handlers/ScheduleWorkflows.cs @@ -2,7 +2,6 @@ using System.Threading; using System.Threading.Tasks; using Elsa.Mediator.Contracts; using Elsa.Modules.Scheduling.Contracts; -using Elsa.Runtime.Middleware; using Elsa.Runtime.Models; using Elsa.Runtime.Notifications; diff --git a/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowDefinitionSerializer.cs b/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowDefinitionSerializer.cs index 83302e5a0..f1730a67b 100644 --- a/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowDefinitionSerializer.cs +++ b/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowDefinitionSerializer.cs @@ -1,9 +1,9 @@ using System.Text.Json; using Elsa.Contracts; -using Elsa.Management.Serialization; using Elsa.Models; using Elsa.Persistence.Entities; using Elsa.Persistence.EntityFrameworkCore.Contracts; +using Elsa.Serialization; namespace Elsa.Persistence.EntityFrameworkCore.Handlers.Serialization; diff --git a/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowExecutionLogRecordSerializer.cs b/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowExecutionLogRecordSerializer.cs index 2f015438e..55ee5f7a7 100644 --- a/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowExecutionLogRecordSerializer.cs +++ b/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowExecutionLogRecordSerializer.cs @@ -1,7 +1,7 @@ using System.Text.Json; -using Elsa.Management.Serialization; using Elsa.Persistence.Entities; using Elsa.Persistence.EntityFrameworkCore.Contracts; +using Elsa.Serialization; namespace Elsa.Persistence.EntityFrameworkCore.Handlers.Serialization; diff --git a/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowInstanceSerializer.cs b/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowInstanceSerializer.cs index f67346924..9e15c99f6 100644 --- a/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowInstanceSerializer.cs +++ b/src/persistence/Elsa.Persistence.EntityFrameworkCore/Handlers/Serialization/WorkflowInstanceSerializer.cs @@ -1,8 +1,8 @@ using System.Text.Json; -using Elsa.Management.Serialization; using Elsa.Persistence.Entities; using Elsa.Persistence.EntityFrameworkCore.Contracts; using Elsa.Persistence.Models; +using Elsa.Serialization; using Elsa.State; namespace Elsa.Persistence.EntityFrameworkCore.Handlers.Serialization; diff --git a/src/runtime/Elsa.Runtime.ProtoActor/Actors/WorkflowInstanceActor.cs b/src/runtime/Elsa.Runtime.ProtoActor/Actors/WorkflowInstanceActor.cs index a4d496061..480d1815f 100644 --- a/src/runtime/Elsa.Runtime.ProtoActor/Actors/WorkflowInstanceActor.cs +++ b/src/runtime/Elsa.Runtime.ProtoActor/Actors/WorkflowInstanceActor.cs @@ -48,7 +48,10 @@ public class WorkflowInstanceActor : IActor private PID GetWorkflowOperatorPid(IContext context, string workflowInstanceId) { var actorName = $"workflow-operator:{workflowInstanceId}"; - var pid = context.System.ProcessRegistry.SearchByName(actorName).FirstOrDefault(); + var pid = context.System.ProcessRegistry.Find(x => + { + return x == actorName; + }).FirstOrDefault(); if (pid != null) return pid; diff --git a/src/runtime/Elsa.Runtime.ProtoActor/Actors/WorkflowOperatorActor.cs b/src/runtime/Elsa.Runtime.ProtoActor/Actors/WorkflowOperatorActor.cs index 4ee575dab..95b204317 100644 --- a/src/runtime/Elsa.Runtime.ProtoActor/Actors/WorkflowOperatorActor.cs +++ b/src/runtime/Elsa.Runtime.ProtoActor/Actors/WorkflowOperatorActor.cs @@ -5,7 +5,6 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; using Elsa.Contracts; -using Elsa.Management.Serialization; using Elsa.Mediator.Contracts; using Elsa.Models; using Elsa.Persistence.Models; @@ -13,6 +12,7 @@ using Elsa.Persistence.Requests; using Elsa.Runtime.Contracts; using Elsa.Runtime.ProtoActor.Extensions; using Elsa.Runtime.ProtoActor.Messages; +using Elsa.Serialization; using Elsa.State; using Proto; using Bookmark = Elsa.Runtime.ProtoActor.Messages.Bookmark; diff --git a/src/samples/aspnet/Elsa.Samples.Web1/Models/Order.cs b/src/samples/aspnet/Elsa.Samples.Web1/Models/Order.cs new file mode 100644 index 000000000..738678f44 --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.Web1/Models/Order.cs @@ -0,0 +1,4 @@ +namespace Elsa.Samples.Web1.Models; + +public record Order(string Id, int Number, string CustomerId, OrderItem[] Items); +public record OrderItem(string ProductId, int Quantity); \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.Web1/Program.cs b/src/samples/aspnet/Elsa.Samples.Web1/Program.cs index cb3b5b014..de8825039 100644 --- a/src/samples/aspnet/Elsa.Samples.Web1/Program.cs +++ b/src/samples/aspnet/Elsa.Samples.Web1/Program.cs @@ -1,11 +1,15 @@ +using System; +using System.Text.Json; using Elsa.Activities; using Elsa.Api.Extensions; +using Elsa.Contracts; using Elsa.Extensions; using Elsa.Jobs.Extensions; using Elsa.Management.Contracts; using Elsa.Management.Extensions; using Elsa.Modules.Activities.Activities.Console; using Elsa.Modules.Activities.Activities.Workflows; +using Elsa.Modules.Activities.Configurators; using Elsa.Modules.AzureServiceBus.Activities; using Elsa.Modules.AzureServiceBus.Extensions; using Elsa.Modules.Hangfire.Services; @@ -14,6 +18,7 @@ using Elsa.Modules.Http.Extensions; using Elsa.Modules.JavaScript.Activities; using Elsa.Modules.Quartz.Services; using Elsa.Modules.Scheduling.Activities; +using Elsa.Modules.Scheduling.Extensions; using Elsa.Modules.WorkflowContexts.Extensions; using Elsa.Persistence.EntityFrameworkCore.Extensions; using Elsa.Persistence.EntityFrameworkCore.Sqlite; @@ -21,9 +26,12 @@ using Elsa.Pipelines.WorkflowExecution.Components; using Elsa.Runtime.Extensions; using Elsa.Runtime.ProtoActor.Extensions; using Elsa.Samples.Web1.Activities; +using Elsa.Samples.Web1.Models; +using Elsa.Samples.Web1.Serialization; using Elsa.Samples.Web1.Workflows; using Elsa.Scripting.JavaScript.Extensions; using Elsa.Scripting.Liquid.Extensions; +using Elsa.Serialization; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; @@ -45,13 +53,14 @@ services .IndexWorkflowTriggers() .AddElsaManagement() .AddJobServices(new QuartzJobSchedulerProvider(), new HangfireJobQueueProvider(sqlServerConnectionString)) + .AddSchedulingServices() .AddHttpActivityServices() .AddAzureServiceBusServices(options => configuration.GetSection("AzureServiceBus").Bind(options)) .ConfigureWorkflowRuntime(options => { // Register workflows. options.Workflows.Add(); - options.Workflows.Add(); + //options.Workflows.Add(); options.Workflows.Add(); options.Workflows.Add(); options.Workflows.Add(); @@ -60,6 +69,8 @@ services options.Workflows.Add(); options.Workflows.Add(); options.Workflows.Add(); + options.Workflows.Add(); + options.Workflows.Add(); }); // Testing only: allow client app to connect from anywhere. @@ -86,6 +97,10 @@ services .AddJavaScriptExpressions() .AddLiquidExpressions(); +// Register serialization configurator for configuring what types to allow to be serialized. +services.AddSingleton(); +services.AddSingleton(); + // Configure middleware pipeline. var app = builder.Build(); var serviceProvider = app.Services; @@ -97,6 +112,11 @@ wellKnownTypeRegistry.RegisterType("float"); wellKnownTypeRegistry.RegisterType("boolean"); wellKnownTypeRegistry.RegisterType("string"); +var order = new Order("order-1", 1, "customer-1", new[] { new OrderItem("product-i1", 2) }); +var serializationOptions = serviceProvider.GetRequiredService().CreatePersistenceOptions(); +var json = JsonSerializer.Serialize(order, serializationOptions); +Console.WriteLine(json); + // Configure workflow engine execution pipeline. serviceProvider.ConfigureDefaultWorkflowExecutionPipeline(pipeline => pipeline diff --git a/src/samples/aspnet/Elsa.Samples.Web1/Serialization/SerializationOptionsConfigurator.cs b/src/samples/aspnet/Elsa.Samples.Web1/Serialization/SerializationOptionsConfigurator.cs new file mode 100644 index 000000000..77380978b --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.Web1/Serialization/SerializationOptionsConfigurator.cs @@ -0,0 +1,19 @@ +using System.Text.Json; +using Dahomey.Json; +using Dahomey.Json.Attributes; +using Elsa.Contracts; +using Elsa.Samples.Web1.Models; + +namespace Elsa.Samples.Web1.Serialization; + +public class SerializationOptionsConfigurator : ISerializationOptionsConfigurator +{ + public void Configure(JsonSerializerOptions options) + { + options.GetObjectMappingRegistry().Register(objectMapping => objectMapping + .AutoMap() + .SetDiscriminator("Order") + .SetDiscriminatorPolicy(DiscriminatorPolicy.Always) + .AddDiscriminatorMapping()); + } +} \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.Web1/Workflows/DelayWorkflow.cs b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/DelayWorkflow.cs new file mode 100644 index 000000000..4f8108bd1 --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/DelayWorkflow.cs @@ -0,0 +1,23 @@ +using Elsa.Activities; +using Elsa.Contracts; +using Elsa.Modules.Activities.Activities.Console; +using Elsa.Modules.Scheduling.Activities; +using Elsa.Runtime.Contracts; + +namespace Elsa.Samples.Web1.Workflows; + +public class DelayWorkflow : IWorkflow +{ + public void Build(IWorkflowDefinitionBuilder workflow) + { + workflow.WithRoot(new Sequence + { + Activities = + { + new WriteLine("Sleeping for 5 seconds..."), + Delay.FromSeconds(5), + new WriteLine(context => $"Continuing at {context.GetRequiredService().UtcNow}") + } + }); + } +} \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.Web1/Workflows/OrderProcessingWorkflow.cs b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/OrderProcessingWorkflow.cs new file mode 100644 index 000000000..5f4ea90cf --- /dev/null +++ b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/OrderProcessingWorkflow.cs @@ -0,0 +1,29 @@ +using Elsa.Activities; +using Elsa.Contracts; +using Elsa.Models; +using Elsa.Modules.Activities.Activities.Console; +using Elsa.Modules.Scheduling.Activities; +using Elsa.Runtime.Contracts; +using Elsa.Samples.Web1.Models; + +namespace Elsa.Samples.Web1.Workflows; + +public class OrderProcessingWorkflow : IWorkflow +{ + public void Build(IWorkflowDefinitionBuilder workflow) + { + var orderVariable = new Variable(); + + workflow.WithRoot(new Sequence + { + Variables = { orderVariable }, + Activities = + { + new WriteLine("Creating order..."), + new SetVariable(orderVariable, new Order("order-1", 1, "customer-1", new[] { new OrderItem("product-1", 3) })), + Delay.FromSeconds(5), + new WriteLine(context => $"Shipping order {orderVariable.Get(context)!.Id}."), + } + }); + } +} \ No newline at end of file diff --git a/src/samples/aspnet/Elsa.Samples.Web1/Workflows/SubmitJobWorkflow.cs b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/SubmitJobWorkflow.cs index 848e0fcec..42e350aae 100644 --- a/src/samples/aspnet/Elsa.Samples.Web1/Workflows/SubmitJobWorkflow.cs +++ b/src/samples/aspnet/Elsa.Samples.Web1/Workflows/SubmitJobWorkflow.cs @@ -6,6 +6,7 @@ using Elsa.Contracts; using Elsa.Jobs.Abstractions; using Elsa.Jobs.Contracts; using Elsa.Jobs.Models; +using Elsa.Modules.Activities.Activities.Console; using Elsa.Runtime.Contracts; namespace Elsa.Samples.Web1.Workflows; @@ -14,12 +15,20 @@ public class SubmitJobWorkflow : IWorkflow { public void Build(IWorkflowDefinitionBuilder workflow) { - workflow.WithRoot(new Inline(async context => + workflow.WithRoot(new Sequence { - var jobQueue = context.GetRequiredService(); - var job = new ReadTheInternetJob(); - await jobQueue.SubmitJobAsync(job, cancellationToken: context.CancellationToken); - })); + Activities = + { + new WriteLine("Downloading..."), + new Inline(async context => + { + var jobQueue = context.GetRequiredService(); + var job = new ReadTheInternetJob(); + await jobQueue.SubmitJobAsync(job, cancellationToken: context.CancellationToken); + }) + // TODO: Implement a "job" activity that blocks the workflow until the job completes. + } + }); } } diff --git a/src/samples/console/Elsa.Samples.Console1/Program.cs b/src/samples/console/Elsa.Samples.Console1/Program.cs index 1de0055ba..e52e7027e 100644 --- a/src/samples/console/Elsa.Samples.Console1/Program.cs +++ b/src/samples/console/Elsa.Samples.Console1/Program.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Threading.Tasks; using Elsa.Contracts; using Elsa.Extensions; -using Elsa.Mediator.Extensions; using Elsa.Models; using Elsa.Persistence.InMemory.Extensions; using Elsa.Pipelines.ActivityExecution.Components;