From 1b4ce452eed73b84e25175b6d218bbb08594688c Mon Sep 17 00:00:00 2001 From: rosca-sabina <43028000+rosca-sabina@users.noreply.github.com> Date: Sat, 12 Oct 2024 13:16:09 +0300 Subject: [PATCH 01/10] Add MongoDB collection naming strategy (#6005) --- .../Common/PersistenceFeatureBase.cs | 15 ++++++++++----- .../Contracts/ICollectionNamingStrategy.cs | 12 ++++++++++++ .../Elsa.MongoDb/Features/MongoDbFeature.cs | 11 +++++++++++ .../NamingStrategies/DefaultNamingStrategy.cs | 17 +++++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) create mode 100644 src/modules/Elsa.MongoDb/Contracts/ICollectionNamingStrategy.cs create mode 100644 src/modules/Elsa.MongoDb/NamingStrategies/DefaultNamingStrategy.cs diff --git a/src/modules/Elsa.MongoDb/Common/PersistenceFeatureBase.cs b/src/modules/Elsa.MongoDb/Common/PersistenceFeatureBase.cs index e08af18c4b..3cad801721 100644 --- a/src/modules/Elsa.MongoDb/Common/PersistenceFeatureBase.cs +++ b/src/modules/Elsa.MongoDb/Common/PersistenceFeatureBase.cs @@ -1,5 +1,6 @@ using Elsa.Features.Abstractions; using Elsa.Features.Services; +using Elsa.MongoDb.Contracts; using Microsoft.Extensions.DependencyInjection; using MongoDB.Driver; @@ -24,8 +25,7 @@ protected void AddStore() where TDocument : class where TStor { Services .AddScoped>() - .AddScoped() - ; + .AddScoped(); } /// @@ -35,8 +35,13 @@ protected void AddStore() where TDocument : class where TStor /// The document type of the collection. protected void AddCollection(string collectionName) where TDocument : class { - Services.AddScoped( - sp => sp.GetRequiredService() - .GetCollection(collectionName)); + Services.AddScoped(sp => + { + var collectionNamingStrategy = sp.GetRequiredService(); + var formattedCollectionName = collectionNamingStrategy.GetCollectionName(collectionName); + + return sp.GetRequiredService() + .GetCollection(formattedCollectionName); + }); } } \ No newline at end of file diff --git a/src/modules/Elsa.MongoDb/Contracts/ICollectionNamingStrategy.cs b/src/modules/Elsa.MongoDb/Contracts/ICollectionNamingStrategy.cs new file mode 100644 index 0000000000..ae25b868d4 --- /dev/null +++ b/src/modules/Elsa.MongoDb/Contracts/ICollectionNamingStrategy.cs @@ -0,0 +1,12 @@ +namespace Elsa.MongoDb.Contracts; + +/// +/// Represents a naming strategy to use when creating the name of a MongoDB collection. +/// +public interface ICollectionNamingStrategy +{ + /// + /// Returns a collection name from the specified base collection name. + /// + string GetCollectionName(string collectionName); +} diff --git a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs index 2b80149749..a0addcb268 100644 --- a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs +++ b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs @@ -2,11 +2,14 @@ using Elsa.Features.Abstractions; using Elsa.Features.Services; using Elsa.KeyValues.Entities; +using Elsa.MongoDb.Contracts; +using Elsa.MongoDb.NamingStrategies; using Elsa.MongoDb.Options; using Elsa.MongoDb.Serializers; using Elsa.Workflows.Memory; using Elsa.Workflows.Runtime.Entities; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; using MongoDB.Bson; using MongoDB.Bson.Serialization; @@ -36,12 +39,20 @@ public MongoDbFeature(IModule module) : base(module) /// public Action Options { get; set; } = _ => { }; + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func CollectionNamingStrategy { get; set; } = sp => sp.GetRequiredService(); + /// public override void Apply() { Services.Configure(Options); Services.AddScoped(sp => CreateDatabase(sp, ConnectionString)); + + Services.TryAddScoped(); + Services.AddScoped(CollectionNamingStrategy); RegisterSerializers(); RegisterClassMaps(); diff --git a/src/modules/Elsa.MongoDb/NamingStrategies/DefaultNamingStrategy.cs b/src/modules/Elsa.MongoDb/NamingStrategies/DefaultNamingStrategy.cs new file mode 100644 index 0000000000..e266d4cbf9 --- /dev/null +++ b/src/modules/Elsa.MongoDb/NamingStrategies/DefaultNamingStrategy.cs @@ -0,0 +1,17 @@ +using Elsa.MongoDb.Contracts; + +namespace Elsa.MongoDb.NamingStrategies; + +/// +/// Returns the same collection name, without modifying it. +/// +public class DefaultNamingStrategy : ICollectionNamingStrategy +{ + /// + /// Returns the same collection name, without modifying it. + /// + public string GetCollectionName(string collectionName) + { + return collectionName; + } +} From 090a3fd1b0701b210268ec1784bbbcb47be4fb89 Mon Sep 17 00:00:00 2001 From: rosca-sabina <43028000+rosca-sabina@users.noreply.github.com> Date: Sat, 12 Oct 2024 13:21:39 +0300 Subject: [PATCH 02/10] Register MongoDb client as singleton (#6013) Co-authored-by: Sipke Schoorstra --- .../Elsa.MongoDb/Features/MongoDbFeature.cs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs index a0addcb268..b0aabc67d1 100644 --- a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs +++ b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs @@ -49,7 +49,9 @@ public override void Apply() { Services.Configure(Options); - Services.AddScoped(sp => CreateDatabase(sp, ConnectionString)); + var mongoUrl = new MongoUrl(ConnectionString); + Services.AddSingleton(sp => CreateMongoClient(sp, mongoUrl)); + Services.AddScoped(sp => CreateDatabase(sp, mongoUrl)); Services.TryAddScoped(); Services.AddScoped(CollectionNamingStrategy); @@ -95,11 +97,10 @@ private static void TryRegisterSerializerOrSkipWhenExist(Type type, IBsonSeriali } } - private static IMongoDatabase CreateDatabase(IServiceProvider sp, string connectionString) + private static IMongoClient CreateMongoClient(IServiceProvider sp, MongoUrl mongoUrl) { var options = sp.GetRequiredService>().Value; - var mongoUrl = new MongoUrl(connectionString); var settings = MongoClientSettings.FromUrl(mongoUrl); settings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber()); @@ -111,8 +112,13 @@ private static IMongoDatabase CreateDatabase(IServiceProvider sp, string connect settings.RetryWrites = options.RetryWrites; settings.SslSettings = options.SslSettings; - var mongoClient = new MongoClient(settings); - return mongoClient.GetDatabase(mongoUrl.DatabaseName); + return new MongoClient(settings); + } + + private static IMongoDatabase CreateDatabase(IServiceProvider sp, MongoUrl mongoUrl) + { + var client = sp.GetRequiredService(); + return client.GetDatabase(mongoUrl.DatabaseName); } private static string GetApplicationName(MongoClientSettings settings) => From 190591824725feb81142f7a7990439f0312c32ca Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 12 Oct 2024 12:27:54 +0200 Subject: [PATCH 03/10] Update `System.Text.Json` package reference to use property variable Switched from a hardcoded version to a property variable `$(SystemTextJsonVersion)` for the `System.Text.Json` package reference in multiple project files. This change centralizes version management and ensures consistency across all projects. --- Directory.Build.props | 3 ++- src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj | 2 +- .../Elsa.ServerAndStudio.Web/Elsa.ServerAndStudio.Web.csproj | 2 +- src/bundles/Elsa.Studio.Web/Elsa.Studio.Web.csproj | 2 +- src/clients/Elsa.Api.Client/Elsa.Api.Client.csproj | 2 +- .../Elsa.EntityFrameworkCore.Common.csproj | 2 +- .../Elsa.EntityFrameworkCore.MySql.csproj | 2 +- .../Elsa.EntityFrameworkCore.PostgreSql.csproj | 2 +- .../Elsa.EntityFrameworkCore.SqlServer.csproj | 2 +- .../Elsa.EntityFrameworkCore.Sqlite.csproj | 2 +- .../Elsa.EntityFrameworkCore/Elsa.EntityFrameworkCore.csproj | 2 +- src/modules/Elsa.FileStorage/Elsa.FileStorage.csproj | 2 +- src/modules/Elsa.Http/Elsa.Http.csproj | 2 +- src/modules/Elsa.MongoDb/Elsa.MongoDb.csproj | 2 +- .../Elsa.Quartz.EntityFrameworkCore.MySql.csproj | 2 +- .../Elsa.Quartz.EntityFrameworkCore.PostgreSql.csproj | 2 +- .../Elsa.Quartz.EntityFrameworkCore.SqlServer.csproj | 2 +- .../Elsa.Quartz.EntityFrameworkCore.Sqlite.csproj | 2 +- src/modules/Elsa.Telnyx/Elsa.Telnyx.csproj | 2 +- .../Elsa.WorkflowProviders.BlobStorage.csproj | 2 +- src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj | 2 +- 21 files changed, 22 insertions(+), 21 deletions(-) diff --git a/Directory.Build.props b/Directory.Build.props index c24271ac03..645da893e4 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -27,6 +27,7 @@ true - 3.2.0-rc4.473 + 3.2.1-preview.558 + 8.0.5 \ No newline at end of file diff --git a/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj b/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj index 71cb87c965..ba521fe466 100644 --- a/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj +++ b/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj @@ -56,7 +56,7 @@ - + diff --git a/src/bundles/Elsa.ServerAndStudio.Web/Elsa.ServerAndStudio.Web.csproj b/src/bundles/Elsa.ServerAndStudio.Web/Elsa.ServerAndStudio.Web.csproj index 23d1886394..e2d1c97f86 100644 --- a/src/bundles/Elsa.ServerAndStudio.Web/Elsa.ServerAndStudio.Web.csproj +++ b/src/bundles/Elsa.ServerAndStudio.Web/Elsa.ServerAndStudio.Web.csproj @@ -42,7 +42,7 @@ - + \ No newline at end of file diff --git a/src/bundles/Elsa.Studio.Web/Elsa.Studio.Web.csproj b/src/bundles/Elsa.Studio.Web/Elsa.Studio.Web.csproj index 432ff7c6c1..2cb27ad135 100644 --- a/src/bundles/Elsa.Studio.Web/Elsa.Studio.Web.csproj +++ b/src/bundles/Elsa.Studio.Web/Elsa.Studio.Web.csproj @@ -18,7 +18,7 @@ - + diff --git a/src/clients/Elsa.Api.Client/Elsa.Api.Client.csproj b/src/clients/Elsa.Api.Client/Elsa.Api.Client.csproj index 7ce828980d..5358e76f35 100644 --- a/src/clients/Elsa.Api.Client/Elsa.Api.Client.csproj +++ b/src/clients/Elsa.Api.Client/Elsa.Api.Client.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/modules/Elsa.EntityFrameworkCore.Common/Elsa.EntityFrameworkCore.Common.csproj b/src/modules/Elsa.EntityFrameworkCore.Common/Elsa.EntityFrameworkCore.Common.csproj index eab7ce6b21..26a175db6a 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Common/Elsa.EntityFrameworkCore.Common.csproj +++ b/src/modules/Elsa.EntityFrameworkCore.Common/Elsa.EntityFrameworkCore.Common.csproj @@ -19,7 +19,7 @@ - + diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Elsa.EntityFrameworkCore.MySql.csproj b/src/modules/Elsa.EntityFrameworkCore.MySql/Elsa.EntityFrameworkCore.MySql.csproj index 5df8320c1f..6ab40ff8f1 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Elsa.EntityFrameworkCore.MySql.csproj +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Elsa.EntityFrameworkCore.MySql.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Elsa.EntityFrameworkCore.PostgreSql.csproj b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Elsa.EntityFrameworkCore.PostgreSql.csproj index ce9f15c011..078b0ff953 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Elsa.EntityFrameworkCore.PostgreSql.csproj +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Elsa.EntityFrameworkCore.PostgreSql.csproj @@ -14,7 +14,7 @@ - + diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Elsa.EntityFrameworkCore.SqlServer.csproj b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Elsa.EntityFrameworkCore.SqlServer.csproj index 2a69f65aea..26f28d9a28 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Elsa.EntityFrameworkCore.SqlServer.csproj +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Elsa.EntityFrameworkCore.SqlServer.csproj @@ -21,6 +21,6 @@ - + \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Elsa.EntityFrameworkCore.Sqlite.csproj b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Elsa.EntityFrameworkCore.Sqlite.csproj index 6580755a31..10feafd593 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Elsa.EntityFrameworkCore.Sqlite.csproj +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Elsa.EntityFrameworkCore.Sqlite.csproj @@ -15,7 +15,7 @@ - + diff --git a/src/modules/Elsa.EntityFrameworkCore/Elsa.EntityFrameworkCore.csproj b/src/modules/Elsa.EntityFrameworkCore/Elsa.EntityFrameworkCore.csproj index 3dd2e4a6a6..1a05f43205 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Elsa.EntityFrameworkCore.csproj +++ b/src/modules/Elsa.EntityFrameworkCore/Elsa.EntityFrameworkCore.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/modules/Elsa.FileStorage/Elsa.FileStorage.csproj b/src/modules/Elsa.FileStorage/Elsa.FileStorage.csproj index 0393bc6450..648f20efca 100644 --- a/src/modules/Elsa.FileStorage/Elsa.FileStorage.csproj +++ b/src/modules/Elsa.FileStorage/Elsa.FileStorage.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/modules/Elsa.Http/Elsa.Http.csproj b/src/modules/Elsa.Http/Elsa.Http.csproj index 1024756e2c..26f35e9b53 100644 --- a/src/modules/Elsa.Http/Elsa.Http.csproj +++ b/src/modules/Elsa.Http/Elsa.Http.csproj @@ -13,7 +13,7 @@ - + diff --git a/src/modules/Elsa.MongoDb/Elsa.MongoDb.csproj b/src/modules/Elsa.MongoDb/Elsa.MongoDb.csproj index 39d38e218d..2a84690bf0 100644 --- a/src/modules/Elsa.MongoDb/Elsa.MongoDb.csproj +++ b/src/modules/Elsa.MongoDb/Elsa.MongoDb.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/modules/Elsa.Quartz.EntityFrameworkCore.MySql/Elsa.Quartz.EntityFrameworkCore.MySql.csproj b/src/modules/Elsa.Quartz.EntityFrameworkCore.MySql/Elsa.Quartz.EntityFrameworkCore.MySql.csproj index a2e95de323..6773932594 100644 --- a/src/modules/Elsa.Quartz.EntityFrameworkCore.MySql/Elsa.Quartz.EntityFrameworkCore.MySql.csproj +++ b/src/modules/Elsa.Quartz.EntityFrameworkCore.MySql/Elsa.Quartz.EntityFrameworkCore.MySql.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/modules/Elsa.Quartz.EntityFrameworkCore.PostgreSql/Elsa.Quartz.EntityFrameworkCore.PostgreSql.csproj b/src/modules/Elsa.Quartz.EntityFrameworkCore.PostgreSql/Elsa.Quartz.EntityFrameworkCore.PostgreSql.csproj index 8cca959012..9b887cfcd9 100644 --- a/src/modules/Elsa.Quartz.EntityFrameworkCore.PostgreSql/Elsa.Quartz.EntityFrameworkCore.PostgreSql.csproj +++ b/src/modules/Elsa.Quartz.EntityFrameworkCore.PostgreSql/Elsa.Quartz.EntityFrameworkCore.PostgreSql.csproj @@ -22,7 +22,7 @@ - + diff --git a/src/modules/Elsa.Quartz.EntityFrameworkCore.SqlServer/Elsa.Quartz.EntityFrameworkCore.SqlServer.csproj b/src/modules/Elsa.Quartz.EntityFrameworkCore.SqlServer/Elsa.Quartz.EntityFrameworkCore.SqlServer.csproj index f33bc9af8b..6a8f00321b 100644 --- a/src/modules/Elsa.Quartz.EntityFrameworkCore.SqlServer/Elsa.Quartz.EntityFrameworkCore.SqlServer.csproj +++ b/src/modules/Elsa.Quartz.EntityFrameworkCore.SqlServer/Elsa.Quartz.EntityFrameworkCore.SqlServer.csproj @@ -24,6 +24,6 @@ - + \ No newline at end of file diff --git a/src/modules/Elsa.Quartz.EntityFrameworkCore.Sqlite/Elsa.Quartz.EntityFrameworkCore.Sqlite.csproj b/src/modules/Elsa.Quartz.EntityFrameworkCore.Sqlite/Elsa.Quartz.EntityFrameworkCore.Sqlite.csproj index 283ff1ffb7..1a5ad7e07d 100644 --- a/src/modules/Elsa.Quartz.EntityFrameworkCore.Sqlite/Elsa.Quartz.EntityFrameworkCore.Sqlite.csproj +++ b/src/modules/Elsa.Quartz.EntityFrameworkCore.Sqlite/Elsa.Quartz.EntityFrameworkCore.Sqlite.csproj @@ -16,7 +16,7 @@ - + diff --git a/src/modules/Elsa.Telnyx/Elsa.Telnyx.csproj b/src/modules/Elsa.Telnyx/Elsa.Telnyx.csproj index ae23a59157..79b745a667 100644 --- a/src/modules/Elsa.Telnyx/Elsa.Telnyx.csproj +++ b/src/modules/Elsa.Telnyx/Elsa.Telnyx.csproj @@ -24,7 +24,7 @@ - + diff --git a/src/modules/Elsa.WorkflowProviders.BlobStorage/Elsa.WorkflowProviders.BlobStorage.csproj b/src/modules/Elsa.WorkflowProviders.BlobStorage/Elsa.WorkflowProviders.BlobStorage.csproj index c6c83beb60..779e52c497 100644 --- a/src/modules/Elsa.WorkflowProviders.BlobStorage/Elsa.WorkflowProviders.BlobStorage.csproj +++ b/src/modules/Elsa.WorkflowProviders.BlobStorage/Elsa.WorkflowProviders.BlobStorage.csproj @@ -17,6 +17,6 @@ - + diff --git a/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj b/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj index 95079450f4..c41a9e2d39 100644 --- a/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj +++ b/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj @@ -17,6 +17,6 @@ - + From f6eccde968e1e16aff3a0ed617fff1167ead98e3 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 18 Oct 2024 10:54:23 +0200 Subject: [PATCH 04/10] Switch to project references from package references Replaced package references for various Elsa modules with direct project references. This change aims to streamline dependency management and avoid issues related to differing package versions. Updated the solution file and Directory.Build.props accordingly to reflect the new paths. --- build/_build.csproj | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/build/_build.csproj b/build/_build.csproj index 4e4b70e2db..3ae4c9750f 100644 --- a/build/_build.csproj +++ b/build/_build.csproj @@ -17,13 +17,12 @@ - + - - + From a2004128964ecf7010994528b185dbfadeb2a78b Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 19 Oct 2024 10:44:28 +0200 Subject: [PATCH 05/10] Update package versions to fix vulnerabilities Upgrade various packages to their latest versions to address vulnerabilities and improve stability. This includes updates to Microsoft.Extensions, EntityFrameworkCore, and Npgsql among others. --- Directory.Packages.props | 44 ++++++++++++++++++++-------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/Directory.Packages.props b/Directory.Packages.props index ab8552aff1..0820a6e1da 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -138,33 +138,33 @@ - - - - - - - - - - - + + + + + + + + + + + - + - - - - - - - + + + + + + + - - + + - + \ No newline at end of file From b46d43257910baebbd60ea9b75fa2d1494354dd0 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Sat, 19 Oct 2024 11:56:50 +0200 Subject: [PATCH 06/10] Add JsonNode BSON serializer (#6043) Introduce JsonNodeBsonConverter for BSON serialization of JsonNode objects. Update MongoDbFeature to register the new serializer, enhancing JSON handling within MongoDB serialization context. --- .../Elsa.MongoDb/Features/MongoDbFeature.cs | 3 + .../Serializers/JsonNodeBsonSerializer.cs | 135 ++++++++++++++++++ 2 files changed, 138 insertions(+) create mode 100644 src/modules/Elsa.MongoDb/Serializers/JsonNodeBsonSerializer.cs diff --git a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs index b0aabc67d1..e168e37cfc 100644 --- a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs +++ b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs @@ -1,4 +1,5 @@ using System.Text.Json; +using System.Text.Json.Nodes; using Elsa.Features.Abstractions; using Elsa.Features.Services; using Elsa.KeyValues.Entities; @@ -8,6 +9,7 @@ using Elsa.MongoDb.Serializers; using Elsa.Workflows.Memory; using Elsa.Workflows.Runtime.Entities; +using Elsa.Workflows.Services; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; @@ -67,6 +69,7 @@ private static void RegisterSerializers() TryRegisterSerializerOrSkipWhenExist(typeof(Variable), new VariableSerializer()); TryRegisterSerializerOrSkipWhenExist(typeof(Version), new VersionSerializer()); TryRegisterSerializerOrSkipWhenExist(typeof(JsonElement), new JsonElementSerializer()); + TryRegisterSerializerOrSkipWhenExist(typeof(JsonNode), new JsonNodeBsonConverter()); } private static void RegisterClassMaps() diff --git a/src/modules/Elsa.MongoDb/Serializers/JsonNodeBsonSerializer.cs b/src/modules/Elsa.MongoDb/Serializers/JsonNodeBsonSerializer.cs new file mode 100644 index 0000000000..51d7932984 --- /dev/null +++ b/src/modules/Elsa.MongoDb/Serializers/JsonNodeBsonSerializer.cs @@ -0,0 +1,135 @@ +using System.Text.Json.Nodes; +using MongoDB.Bson; +using MongoDB.Bson.IO; +using MongoDB.Bson.Serialization; + +namespace Elsa.MongoDb.Serializers; + +/// +/// Serializes a . +/// +public class JsonNodeBsonConverter : IBsonSerializer +{ + /// + public Type ValueType => typeof(JsonNode); + + /// + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, JsonNode value) + { + if (value == null!) + { + context.Writer.WriteNull(); + return; + } + + context.Writer.WriteStartDocument(); + context.Writer.WriteName("type"); + + switch (value) + { + case JsonObject jsonObject: + context.Writer.WriteString("JsonObject"); + context.Writer.WriteName("value"); + context.Writer.WriteString(jsonObject.ToJsonString()); + break; + + case JsonArray jsonArray: + context.Writer.WriteString("JsonArray"); + context.Writer.WriteName("value"); + context.Writer.WriteString(jsonArray.ToJsonString()); + break; + + case JsonValue jsonValue: + context.Writer.WriteString("JsonValue"); + context.Writer.WriteName("value"); + if (jsonValue.TryGetValue(out string? stringValue)) + context.Writer.WriteString(stringValue); + else if (jsonValue.TryGetValue(out int intValue)) + context.Writer.WriteInt32(intValue); + else if (jsonValue.TryGetValue(out int longValue)) + context.Writer.WriteInt64(longValue); + else if (jsonValue.TryGetValue(out double doubleValue)) + context.Writer.WriteDouble(doubleValue); + else if (jsonValue.TryGetValue(out bool boolValue)) + context.Writer.WriteBoolean(boolValue); + else if (jsonValue.TryGetValue(out DateTimeOffset dateTimeOffsetValue)) + context.Writer.WriteDateTime(dateTimeOffsetValue.ToUnixTimeMilliseconds()); + else if (jsonValue.TryGetValue(out DateTime dateTimeValue)) + context.Writer.WriteDateTime(new DateTimeOffset(dateTimeValue).ToUnixTimeMilliseconds()); + else + throw new BsonSerializationException("Unsupported JsonValue type"); + break; + + default: + throw new BsonSerializationException($"Unexpected JsonNode type: {value.GetType()}"); + } + + context.Writer.WriteEndDocument(); + } + + public JsonNode Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + context.Reader.ReadStartDocument(); + var type = context.Reader.ReadString(); + context.Reader.ReadName(Utf8NameDecoder.Instance); + + JsonNode result; + switch (type) + { + case "JsonObject": + var jsonObjectString = context.Reader.ReadString(); + result = JsonNode.Parse(jsonObjectString); + break; + + case "JsonArray": + var jsonArrayString = context.Reader.ReadString(); + result = JsonNode.Parse(jsonArrayString); + break; + + case "JsonValue": + var bsonType = context.Reader.GetCurrentBsonType(); + switch (bsonType) + { + case BsonType.String: + result = JsonValue.Create(context.Reader.ReadString()); + break; + case BsonType.Int32: + result = JsonValue.Create(context.Reader.ReadInt32()); + break; + case BsonType.Int64: + result = JsonValue.Create(context.Reader.ReadInt64()); + break; + case BsonType.Double: + result = JsonValue.Create(context.Reader.ReadDouble()); + break; + case BsonType.Boolean: + result = JsonValue.Create(context.Reader.ReadBoolean()); + break; + case BsonType.DateTime: + result = JsonValue.Create(context.Reader.ReadDateTime()); + break; + default: + throw new BsonSerializationException($"Unsupported BSON type: {bsonType}"); + } + break; + + default: + throw new BsonSerializationException($"Unsupported JsonNode type: {type}"); + } + + context.Reader.ReadEndDocument(); + return result!; + } + + /// + public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) + { + Serialize(context, args, (JsonNode)value); + } + + object IBsonSerializer.Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) + { + return Deserialize(context, args); + } +} + From fefe36f49a615ca79624e03455fe853bc0f825c8 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 25 Oct 2024 14:51:27 +0200 Subject: [PATCH 07/10] Include $schema when exporting workflow definition (#6056) * Refactor AdditionalConvertersConfigurator constructor Simplify AdditionalConvertersConfigurator by using constructor injection directly in the class declaration. Also, annotate the class with [UsedImplicitly] to improve code clarity and maintainability. * Update workflow serialization to be asynchronous Refactor the workflow definition serialization to use asynchronous operations, improving efficiency and handling cancellation tokens. This change ensures the schema is correctly included in the serialized output. --- .../WorkflowDefinitions/Export/Endpoint.cs | 25 +++++++++++++++---- .../AdditionalConvertersConfigurator.cs | 16 +++--------- 2 files changed, 24 insertions(+), 17 deletions(-) diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Export/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Export/Endpoint.cs index 1c0baac307..d5deb003f2 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Export/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Export/Endpoint.cs @@ -74,7 +74,7 @@ private async Task DownloadMultipleWorkflowsAsync(ICollection ids, Cance foreach (var definition in definitions) { var model = await CreateWorkflowModelAsync(definition, cancellationToken); - var binaryJson = SerializeWorkflowDefinition(model); + var binaryJson = await SerializeWorkflowDefinitionAsync(model, cancellationToken); var fileName = GetFileName(model); var entry = zipArchive.CreateEntry(fileName, CompressionLevel.Optimal); await using var entryStream = entry.Open(); @@ -103,7 +103,7 @@ private async Task DownloadSingleWorkflowAsync(string definitionId, string? vers } var model = await CreateWorkflowModelAsync(definition, cancellationToken); - var binaryJson = SerializeWorkflowDefinition(model); + var binaryJson = await SerializeWorkflowDefinitionAsync(model, cancellationToken); var fileName = GetFileName(model); await SendBytesAsync(binaryJson, fileName, cancellation: cancellationToken); @@ -117,10 +117,25 @@ private string GetFileName(WorkflowDefinitionModel definition) return fileName; } - private byte[] SerializeWorkflowDefinition(WorkflowDefinitionModel model) + private async Task SerializeWorkflowDefinitionAsync(WorkflowDefinitionModel model, CancellationToken cancellationToken) { - JsonSerializerOptions serializerOptions = _serializer.GetOptions(); - var binaryJson = JsonSerializer.SerializeToUtf8Bytes(model, serializerOptions); + var serializerOptions = _serializer.GetOptions(); + var document = JsonSerializer.SerializeToDocument(model, serializerOptions); + var rootElement = document.RootElement; + + using var output = new MemoryStream(); + await using var writer = new Utf8JsonWriter(output); + + writer.WriteStartObject(); + writer.WriteString("$schema", "https://elsaworkflows.io/schemas/workflow-definition/v3.0.0/schema.json"); + + foreach (var property in rootElement.EnumerateObject()) + property.WriteTo(writer); + + writer.WriteEndObject(); + + await writer.FlushAsync(cancellationToken); + var binaryJson = output.ToArray(); return binaryJson; } diff --git a/src/modules/Elsa.Workflows.Core/Serialization/Configurators/AdditionalConvertersConfigurator.cs b/src/modules/Elsa.Workflows.Core/Serialization/Configurators/AdditionalConvertersConfigurator.cs index 6a5404312f..da96c7e305 100644 --- a/src/modules/Elsa.Workflows.Core/Serialization/Configurators/AdditionalConvertersConfigurator.cs +++ b/src/modules/Elsa.Workflows.Core/Serialization/Configurators/AdditionalConvertersConfigurator.cs @@ -1,6 +1,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using Elsa.Workflows.Serialization.Converters; +using JetBrains.Annotations; using Microsoft.Extensions.DependencyInjection; namespace Elsa.Workflows.Serialization.Configurators; @@ -8,23 +9,14 @@ namespace Elsa.Workflows.Serialization.Configurators; /// /// Add additional objects. /// -public class AdditionalConvertersConfigurator : SerializationOptionsConfiguratorBase +[UsedImplicitly] +public class AdditionalConvertersConfigurator(IServiceProvider serviceProvider) : SerializationOptionsConfiguratorBase { - private readonly IServiceProvider _serviceProvider; - - /// - /// Initializes a new instance of the class. - /// - public AdditionalConvertersConfigurator(IServiceProvider serviceProvider) - { - _serviceProvider = serviceProvider; - } - /// public override void Configure(JsonSerializerOptions options) { options.Converters.Add(Create()); } - private T Create() => ActivatorUtilities.CreateInstance(_serviceProvider); + private T Create() => ActivatorUtilities.CreateInstance(serviceProvider); } \ No newline at end of file From c3b6decf669cb1bfbc6740b71e7aafe0f5dcabf1 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 25 Oct 2024 22:49:49 +0200 Subject: [PATCH 08/10] Update default version to 3.2.2 in workflows Changed the fallback version from 3.2.1-preview to 3.2.2-preview in the GitHub Actions workflow configuration. This ensures that new preview versions are correctly labeled with the latest version number. --- .github/workflows/packages.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index c4461693c0..39eb047a62 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -62,7 +62,7 @@ jobs: TAG_NAME=${TAG_NAME#refs/tags/} # remove the refs/tags/ prefix echo "VERSION=${TAG_NAME}" >> $GITHUB_ENV else - echo "VERSION=3.2.1-preview.${{github.run_number}}" >> $GITHUB_ENV + echo "VERSION=3.2.2-preview.${{github.run_number}}" >> $GITHUB_ENV fi - name: Set up JDK 17 uses: actions/setup-java@v2 From 91b7e0c800c06afbdb3324453d29ebb7acf5306f Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 31 Oct 2024 19:53:26 +0100 Subject: [PATCH 09/10] Add authorization to WorkflowInstanceHub This commit decorates the WorkflowInstanceHub class with the [Authorize] attribute to ensure that only authorized users can connect to the SignalR hub. This change improves the security of the workflow event notification system. --- .../Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs b/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs index 835a955f7c..3426c07ef2 100644 --- a/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs +++ b/src/modules/Elsa.Workflows.Api/RealTime/Hubs/WorkflowInstanceHub.cs @@ -1,6 +1,7 @@ using Elsa.Workflows.Api.RealTime.Contracts; using Elsa.Workflows.Runtime.Contracts; using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.SignalR; namespace Elsa.Workflows.Api.RealTime.Hubs; @@ -9,6 +10,7 @@ namespace Elsa.Workflows.Api.RealTime.Hubs; /// Represents a SignalR hub for receiving workflow events on the client. /// [PublicAPI] +[Authorize] public class WorkflowInstanceHub : Hub { private readonly IWorkflowRuntime _workflowRuntime; From b3c73e99eab12587336e6d40b8bd87168d6749b8 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 31 Oct 2024 19:59:42 +0100 Subject: [PATCH 10/10] Disable SignalR in Elsa Server Updated `useSignalR` flag to false in `Program.cs` due to Elsa Studio's current inability to send authenticated requests to the SignalR hub. This change ensures better security and stability until the necessary update is implemented. --- src/bundles/Elsa.Server.Web/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bundles/Elsa.Server.Web/Program.cs b/src/bundles/Elsa.Server.Web/Program.cs index 33328dacef..209451473b 100644 --- a/src/bundles/Elsa.Server.Web/Program.cs +++ b/src/bundles/Elsa.Server.Web/Program.cs @@ -48,7 +48,7 @@ const bool useMemoryStores = false; const bool useCaching = true; const bool useReadOnlyMode = false; -const bool useSignalR = true; +const bool useSignalR = false; // Disable until Elsa Studio is updated to send authenticated requests to the SignalR hub. const bool useAzureServiceBus = false; const DistributedCachingTransport distributedCachingTransport = DistributedCachingTransport.MassTransit; const MassTransitBroker useMassTransitBroker = MassTransitBroker.Memory;