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 e08af18c4..3cad80172 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 @@ public abstract class PersistenceFeatureBase : FeatureBase { Services .AddScoped>() - .AddScoped() - ; + .AddScoped(); } /// @@ -35,8 +35,13 @@ public abstract class PersistenceFeatureBase : FeatureBase /// 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 000000000..ae25b868d --- /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 2b8014974..a0addcb26 100644 --- a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs +++ b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs @@ -2,11 +2,14 @@ using System.Text.Json; 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 class MongoDbFeature : FeatureBase /// 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 000000000..e266d4cbf --- /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 a0addcb26..b0aabc67d 100644 --- a/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs +++ b/src/modules/Elsa.MongoDb/Features/MongoDbFeature.cs @@ -49,7 +49,9 @@ public class MongoDbFeature : FeatureBase { 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 @@ public class MongoDbFeature : FeatureBase } } - 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 @@ public class MongoDbFeature : FeatureBase 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 c24271ac0..645da893e 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 71cb87c96..ba521fe46 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 23d188639..e2d1c97f8 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 432ff7c6c..2cb27ad13 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 7ce828980..5358e76f3 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 eab7ce6b2..26a175db6 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 5df8320c1..6ab40ff8f 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 ce9f15c01..078b0ff95 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 2a69f65ae..26f28d9a2 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 6580755a3..10feafd59 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 3dd2e4a6a..1a05f4320 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 0393bc645..648f20efc 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 1024756e2..26f35e9b5 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 39d38e218..2a84690bf 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 a2e95de32..677393259 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 8cca95901..9b887cfcd 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 f33bc9af8..6a8f00321 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 283ff1ffb..1a5ad7e07 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 ae23a5915..79b745a66 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 c6c83beb6..779e52c49 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 95079450f..c41a9e2d3 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 4e4b70e2d..3ae4c9750 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 ab8552aff..0820a6e1d 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 b0aabc67d..e168e37cf 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.Options; 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 @@ public class MongoDbFeature : FeatureBase 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 000000000..51d793298 --- /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 1c0baac30..d5deb003f 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 @@ internal class Export : ElsaEndpoint 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 @@ internal class Export : ElsaEndpoint } 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 @@ internal class Export : ElsaEndpoint 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 6a5404312..da96c7e30 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 c4461693c..39eb047a6 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 835a955f7..3426c07ef 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 33328dace..209451473 100644 --- a/src/bundles/Elsa.Server.Web/Program.cs +++ b/src/bundles/Elsa.Server.Web/Program.cs @@ -48,7 +48,7 @@ const bool runEFCoreMigrations = true; 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;