From ab121e10ad33043cf9b701d205b9991300786d3f Mon Sep 17 00:00:00 2001 From: axeleron007 Date: Wed, 7 Jul 2021 16:13:32 +0300 Subject: [PATCH 1/6] Refactor appsettings.json #1226 Refactor appsettings.json #1226 --- .../EntityFrameworkWebhookStartupBase.cs | 4 +- .../Startup.cs | 2 +- .../Startup.cs | 2 +- .../Startup.cs | 2 +- .../Startup.cs | 2 +- .../Startup.cs | 6 +- .../Startups.cs | 12 +- .../Extensions/ElsaOptionBuilderExtensions.cs | 46 +++++-- src/core/Elsa.Core/FeatureOptions.cs | 12 ++ .../EntityFrameworkCoreStartupBase.cs | 4 +- .../Startup.cs | 2 +- .../Startup.cs | 2 +- .../Startup.cs | 2 +- .../Startup.cs | 2 +- .../Elsa.Persistence.MongoDb/Startup.cs | 6 +- .../Elsa.Persistence.YesSql/Startups.cs | 12 +- .../server/Elsa.Samples.Server.Host/README.md | 112 +++++++----------- .../Elsa.Samples.Server.Host/appsettings.json | 11 +- 18 files changed, 128 insertions(+), 113 deletions(-) create mode 100644 src/core/Elsa.Core/FeatureOptions.cs diff --git a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/EntityFrameworkWebhookStartupBase.cs b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/EntityFrameworkWebhookStartupBase.cs index 0edc56e5c..ff72dea8d 100644 --- a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/EntityFrameworkWebhookStartupBase.cs +++ b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Core/EntityFrameworkWebhookStartupBase.cs @@ -16,8 +16,8 @@ namespace Elsa.Webhooks.Persistence.EntityFramework.Core public override void ConfigureElsa(ElsaOptionsBuilder elsa, IConfiguration configuration) { var services = elsa.Services; - var section = configuration.GetSection($"Elsa:Features:WebhooksPersistenceEntityFrameworkCore{ProviderName}"); - var connectionStringName = section.GetValue("ConnectionStringName"); + var section = configuration.GetSection($"Elsa:Features:Webhooks"); + var connectionStringName = section.GetValue("ConnectionStringIdentifier"); var connectionString = section.GetValue("ConnectionString"); if (string.IsNullOrWhiteSpace(connectionString)) diff --git a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.MySql/Startup.cs b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.MySql/Startup.cs index c30ea6c88..6f7617395 100644 --- a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.MySql/Startup.cs +++ b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.MySql/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.Webhooks.Persistence.EntityFramework.MySql { - [Feature("WebhooksPersistenceEntityFrameworkCoreMySql")] + [Feature("Webhooks:EntityFrameworkCore:MySql")] public class Startup : EntityFrameworkWebhookStartupBase { protected override string ProviderName => "MySql"; diff --git a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.PostgreSql/Startup.cs b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.PostgreSql/Startup.cs index 62079ecce..d14c68a05 100644 --- a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.PostgreSql/Startup.cs +++ b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.PostgreSql/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.Webhooks.Persistence.EntityFramework.PostgreSql { - [Feature("WebhooksPersistenceEntityFrameworkCorePostgreSql")] + [Feature("Webhooks:EntityFrameworkCore:PostgreSql")] public class Startup : EntityFrameworkWebhookStartupBase { protected override string ProviderName => "PostgreSql"; diff --git a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.SqlServer/Startup.cs b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.SqlServer/Startup.cs index 58034b97c..c8903cdbf 100644 --- a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.SqlServer/Startup.cs +++ b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.SqlServer/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.Webhooks.Persistence.EntityFramework.SqlServer { - [Feature("WebhooksPersistenceEntityFrameworkCoreSqlServer")] + [Feature("Webhooks:EntityFrameworkCore:SqlServer")] public class Startup : EntityFrameworkWebhookStartupBase { protected override string ProviderName => "SqlServer"; diff --git a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Sqlite/Startup.cs b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Sqlite/Startup.cs index f2e28ec36..149013da0 100644 --- a/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Sqlite/Startup.cs +++ b/src/activities/webhooks/Elsa.Webhooks.Persistence.EntityFramework.Sqlite/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.Webhooks.Persistence.EntityFramework.Sqlite { - [Feature("WebhooksPersistenceEntityFrameworkCoreSqlite")] + [Feature("Webhooks:EntityFrameworkCore:Sqlite")] public class Startup : EntityFrameworkWebhookStartupBase { protected override string ProviderName => "Sqlite"; diff --git a/src/activities/webhooks/Elsa.Webhooks.Persistence.MongoDb/Startup.cs b/src/activities/webhooks/Elsa.Webhooks.Persistence.MongoDb/Startup.cs index 5fae7725f..5a6055119 100644 --- a/src/activities/webhooks/Elsa.Webhooks.Persistence.MongoDb/Startup.cs +++ b/src/activities/webhooks/Elsa.Webhooks.Persistence.MongoDb/Startup.cs @@ -8,14 +8,14 @@ using Microsoft.Extensions.DependencyInjection; namespace Elsa.Webhooks.Persistence.MongoDb { - [Feature("WebhooksPersistenceMongoDb")] + [Feature("Webhooks:MongoDb")] public class Startup : StartupBase { public override void ConfigureElsa(ElsaOptionsBuilder elsa, IConfiguration configuration) { var services = elsa.Services; - var section = configuration.GetSection($"Elsa:Features:WebhooksPersistenceMongoDb"); - var connectionStringName = section.GetValue("ConnectionStringName"); + var section = configuration.GetSection($"Elsa:Features:Webhooks"); + var connectionStringName = section.GetValue("ConnectionStringIdentifier"); var connectionString = section.GetValue("ConnectionString"); if (string.IsNullOrWhiteSpace(connectionString)) diff --git a/src/activities/webhooks/Elsa.Webhooks.Persistence.YesSql/Startups.cs b/src/activities/webhooks/Elsa.Webhooks.Persistence.YesSql/Startups.cs index 52a47fe7a..1cb00224b 100644 --- a/src/activities/webhooks/Elsa.Webhooks.Persistence.YesSql/Startups.cs +++ b/src/activities/webhooks/Elsa.Webhooks.Persistence.YesSql/Startups.cs @@ -13,7 +13,7 @@ using YesSql.Provider.SqlServer; namespace Elsa.Webhooks.Persistence.YesSql { - [Feature("WebhooksPersistenceYesSqlSqlite")] + [Feature("Webhooks:YesSql:Sqlite")] public class SqliteStartup : YesSqlStartupBase { protected override string ProviderName => "Sqlite"; @@ -21,21 +21,21 @@ namespace Elsa.Webhooks.Persistence.YesSql protected override void Configure(global::YesSql.IConfiguration options, string connectionString) => options.UseSqLite(connectionString); } - [Feature("WebhooksPersistenceYesSqlSqlServer")] + [Feature("Webhooks:YesSql:SqlServer")] public class SqlServerStartup : YesSqlStartupBase { protected override string ProviderName => "SqlServer"; protected override void Configure(global::YesSql.IConfiguration options, string connectionString) => options.UseSqlServer(connectionString); } - [Feature("WebhooksPersistenceYesSqlMySql")] + [Feature("Webhooks:YesSql:MySql")] public class MySqlStartup : YesSqlStartupBase { protected override string ProviderName => "MySql"; protected override void Configure(global::YesSql.IConfiguration options, string connectionString) => options.UseMySql(connectionString); } - [Feature("WebhooksPersistenceYesSqlPostgreSql")] + [Feature("Webhooks:YesSql:PostgreSql")] public class PostgreSqlStartup : YesSqlStartupBase { protected override string ProviderName => "PostgreSql"; @@ -49,8 +49,8 @@ namespace Elsa.Webhooks.Persistence.YesSql public override void ConfigureElsa(ElsaOptionsBuilder elsa, IConfiguration configuration) { var services = elsa.Services; - var section = configuration.GetSection($"Elsa:Features:WebhooksPersistenceYesSql{ProviderName}"); - var connectionStringName = section.GetValue("ConnectionStringName"); + var section = configuration.GetSection($"Elsa:Features:Webhooks"); + var connectionStringName = section.GetValue("ConnectionStringIdentifier"); var connectionString = section.GetValue("ConnectionString"); if (string.IsNullOrWhiteSpace(connectionString)) diff --git a/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs index 081375ee9..9467dcea9 100644 --- a/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs @@ -44,29 +44,51 @@ namespace Elsa { var elsaFeaturesSection = "Elsa:Features"; var features = configuration.GetSection(elsaFeaturesSection).AsEnumerable(); + var enabledFeatures = new List(); - return - from feature in features - let isEnabled = ParseFeatureFlag(configuration, feature.Key) - where isEnabled - select feature.Key.Replace($"{elsaFeaturesSection}:", string.Empty); + foreach (var feature in features) + { + var featureOptions = ParseFeatureFlag(configuration, feature.Key); + if (!featureOptions.Enabled) continue; + + var key = feature.Key.Replace($"{elsaFeaturesSection}:", string.Empty); + enabledFeatures.Add(key); + + var hasFramework = !string.IsNullOrWhiteSpace(featureOptions.Framework); + var hasConnection = !string.IsNullOrWhiteSpace(featureOptions.ConnectionStringIdentifier); + + if (hasFramework) + { + enabledFeatures.Add($"{key}:{featureOptions.Framework}"); + } + + if (hasConnection) + { + enabledFeatures.Add(hasFramework + ? $"{key}:{featureOptions.Framework}:{featureOptions.ConnectionStringIdentifier}" + : $"{key}:{featureOptions.ConnectionStringIdentifier}"); + } + } + + return enabledFeatures; } - private static bool ParseFeatureFlag(IConfiguration configuration, string feature) + private static FeatureOptions ParseFeatureFlag(IConfiguration configuration, string feature) { + var options = new FeatureOptions(); + if (configuration.GetSection($"{feature}:Enabled").Exists()) - { - bool.TryParse(configuration.GetValue($"{feature}:Enabled"), out var enabled); - return enabled; + { + configuration.GetSection(feature).Bind(options); + return options; } if (!feature.EndsWith(":Enabled")) { bool.TryParse(configuration.GetValue($"{feature}"), out var enabled); - return enabled; + options.Enabled = enabled; } - - return false; + return options; } private static IEnumerable GetAssemblies(IEnumerable assemblyMarkerTypes) => assemblyMarkerTypes.Select(x => x.Assembly).Distinct(); diff --git a/src/core/Elsa.Core/FeatureOptions.cs b/src/core/Elsa.Core/FeatureOptions.cs new file mode 100644 index 000000000..26f7085ab --- /dev/null +++ b/src/core/Elsa.Core/FeatureOptions.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; + +namespace Elsa +{ + public class FeatureOptions + { + public bool Enabled { get; set; } + public string? Framework { get; set; } + public string? ConnectionStringIdentifier { get; set; } + public Dictionary? Options { get; set; } + } +} diff --git a/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.Core/EntityFrameworkCoreStartupBase.cs b/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.Core/EntityFrameworkCoreStartupBase.cs index f04a62421..6d25aedb1 100644 --- a/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.Core/EntityFrameworkCoreStartupBase.cs +++ b/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.Core/EntityFrameworkCoreStartupBase.cs @@ -12,8 +12,8 @@ namespace Elsa.Persistence.EntityFramework.Core public override void ConfigureElsa(ElsaOptionsBuilder elsa, IConfiguration configuration) { - var section = configuration.GetSection($"Elsa:Features:PersistenceEntityFrameworkCore{ProviderName}"); - var connectionStringName = section.GetValue("ConnectionStringName"); + var section = configuration.GetSection($"Elsa:Features:DefaultPersistence"); + var connectionStringName = section.GetValue("ConnectionStringIdentifier"); var connectionString = section.GetValue("ConnectionString"); if (string.IsNullOrWhiteSpace(connectionString)) diff --git a/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.MySql/Startup.cs b/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.MySql/Startup.cs index de3266838..596bf07e1 100644 --- a/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.MySql/Startup.cs +++ b/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.MySql/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EntityFramework.MySql { - [Feature("PersistenceEntityFrameworkCoreMySql")] + [Feature("DefaultPersistence:EntityFrameworkCore:MySql")] public class Startup : EntityFrameworkCoreStartupBase { protected override string ProviderName => "MySql"; diff --git a/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.PostgreSql/Startup.cs b/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.PostgreSql/Startup.cs index 738895ce3..ca7e577f7 100644 --- a/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.PostgreSql/Startup.cs +++ b/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.PostgreSql/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EntityFramework.PostgreSql { - [Feature("PersistenceEntityFrameworkCorePostgreSql")] + [Feature("DefaultPersistence:EntityFrameworkCore:PostgreSql")] public class Startup : EntityFrameworkCoreStartupBase { protected override string ProviderName => "PostgreSql"; diff --git a/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.SqlServer/Startup.cs b/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.SqlServer/Startup.cs index e59049f12..fd2bb613a 100644 --- a/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.SqlServer/Startup.cs +++ b/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.SqlServer/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EntityFramework.SqlServer { - [Feature("PersistenceEntityFrameworkCoreSqlServer")] + [Feature("DefaultPersistence:EntityFrameworkCore:SqlServer")] public class Startup : EntityFrameworkCoreStartupBase { protected override string ProviderName => "SqlServer"; diff --git a/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.Sqlite/Startup.cs b/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.Sqlite/Startup.cs index 67f34d1ce..9c97436b8 100644 --- a/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.Sqlite/Startup.cs +++ b/src/persistence/Elsa.Persistence.EntityFramework/Elsa.Persistence.EntityFramework.Sqlite/Startup.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.Persistence.EntityFramework.Sqlite { - [Feature("PersistenceEntityFrameworkCoreSqlite")] + [Feature("DefaultPersistence:EntityFrameworkCore:Sqlite")] public class Startup : EntityFrameworkCoreStartupBase { protected override string ProviderName => "Sqlite"; diff --git a/src/persistence/Elsa.Persistence.MongoDb/Startup.cs b/src/persistence/Elsa.Persistence.MongoDb/Startup.cs index ee07c205d..638ba9db4 100644 --- a/src/persistence/Elsa.Persistence.MongoDb/Startup.cs +++ b/src/persistence/Elsa.Persistence.MongoDb/Startup.cs @@ -4,13 +4,13 @@ using Microsoft.Extensions.Configuration; namespace Elsa.Persistence.MongoDb { - [Feature("PersistenceMongoDb")] + [Feature("DefaultPersistence:MongoDb")] public class Startup : StartupBase { public override void ConfigureElsa(ElsaOptionsBuilder elsa, IConfiguration configuration) { - var section = configuration.GetSection($"Elsa:Features:PersistenceMongoDb"); - var connectionStringName = section.GetValue("ConnectionStringName"); + var section = configuration.GetSection($"Elsa:Features:DefaultPersistence"); + var connectionStringName = section.GetValue("ConnectionStringIdentifier"); var connectionString = section.GetValue("ConnectionString"); if (string.IsNullOrWhiteSpace(connectionString)) diff --git a/src/persistence/Elsa.Persistence.YesSql/Startups.cs b/src/persistence/Elsa.Persistence.YesSql/Startups.cs index ab63c8e00..649ad9a1b 100644 --- a/src/persistence/Elsa.Persistence.YesSql/Startups.cs +++ b/src/persistence/Elsa.Persistence.YesSql/Startups.cs @@ -9,7 +9,7 @@ using YesSql.Provider.SqlServer; namespace Elsa.Persistence.YesSql { - [Feature("PersistenceYesSqlSqlite")] + [Feature("DefaultPersistence:YesSql:Sqlite")] public class SqliteStartup : YesSqlStartupBase { protected override string ProviderName => "Sqlite"; @@ -17,21 +17,21 @@ namespace Elsa.Persistence.YesSql protected override void Configure(global::YesSql.IConfiguration options, string connectionString) => options.UseSqLite(connectionString); } - [Feature("PersistenceYesSqlSqlServer")] + [Feature("DefaultPersistence:YesSql:SqlServer")] public class SqlServerStartup : YesSqlStartupBase { protected override string ProviderName => "SqlServer"; protected override void Configure(global::YesSql.IConfiguration options, string connectionString) => options.UseSqlServer(connectionString); } - [Feature("PersistenceYesSqlMySql")] + [Feature("DefaultPersistence:YesSql:MySql")] public class MySqlStartup : YesSqlStartupBase { protected override string ProviderName => "MySql"; protected override void Configure(global::YesSql.IConfiguration options, string connectionString) => options.UseMySql(connectionString); } - [Feature("PersistenceYesSqlPostgreSql")] + [Feature("DefaultPersistence:YesSql:PostgreSql")] public class PostgreSqlStartup : YesSqlStartupBase { protected override string ProviderName => "PostgreSql"; @@ -44,8 +44,8 @@ namespace Elsa.Persistence.YesSql public override void ConfigureElsa(ElsaOptionsBuilder elsa, IConfiguration configuration) { - var section = configuration.GetSection($"Elsa:Features:PersistenceYesSql{ProviderName}"); - var connectionStringName = section.GetValue("ConnectionStringName"); + var section = configuration.GetSection($"Elsa:Features:DefaultPersistence"); + var connectionStringName = section.GetValue("ConnectionStringIdentifier"); var connectionString = section.GetValue("ConnectionString"); if (string.IsNullOrWhiteSpace(connectionString)) diff --git a/src/samples/server/Elsa.Samples.Server.Host/README.md b/src/samples/server/Elsa.Samples.Server.Host/README.md index f4bcacab5..e453e07c1 100644 --- a/src/samples/server/Elsa.Samples.Server.Host/README.md +++ b/src/samples/server/Elsa.Samples.Server.Host/README.md @@ -1,14 +1,24 @@ ### Configure persistence for Elsa Core in the appsettings.json -In order to specify the persistence it's required to set enabled status and connection string name in Features node +A simple way to enable a feature without persitence: + +``` +"Elsa": { + "Features": { + "Console": true + ... +``` + +In order to specify persistence for a feature it's required to set enabled status, framework (has to be an empty space for MongoDb) and connection string identifier: EF Sqlite ``` "Elsa": { "Features": { - "PersistenceEntityFrameworkCoreSqlite": { - "Enabled": true, - "ConnectionStringName": "Sqlite" + "DefaultPersistence": { + "Enabled": true, + "Framework": "EntityFrameworkCore", + "ConnectionStringIdentifier": "Sqlite" }, ... ``` @@ -17,9 +27,10 @@ EF MySQL ``` "Elsa": { "Features": { - "PersistenceEntityFrameworkCoreMySql": { - "Enabled": true, - "ConnectionStringName": "MySql" + "DefaultPersistence": { + "Enabled": true, + "Framework": "EntityFrameworkCore", + "ConnectionStringIdentifier": "MySql" }, ... ``` @@ -28,9 +39,10 @@ EF SQL Server ``` "Elsa": { "Features": { - "PersistenceEntityFrameworkCoreSqlServer": { - "Enabled": true, - "ConnectionStringName": "SqlServer" + "DefaultPersistence": { + "Enabled": true, + "Framework": "EntityFrameworkCore", + "ConnectionStringIdentifier": "SqlServer" }, ... ``` @@ -39,20 +51,22 @@ EF Postgre ``` "Elsa": { "Features": { - "PersistenceEntityFrameworkCorePostgreSql": { - "Enabled": "true", - "ConnectionStringName": "PostgreSql" + "DefaultPersistence": { + "Enabled": "true", + "Framework": "EntityFrameworkCore", + "ConnectionStringIdentifier": "PostgreSql" }, ... ``` -EF MongoDB +MongoDB ``` "Elsa": { "Features": { - "PersistenceMongoDb": { - "Enabled": true, - "ConnectionStringName": "MongoDb" + "DefaultPersistence": { + "Enabled": true, + "Framework": "", + "ConnectionStringIdentifier": "MongoDb" }, ... ``` @@ -61,9 +75,10 @@ YesSql Sqlite ``` "Elsa": { "Features": { - "PersistenceYesSqlSqlite": { - "Enabled": true, - "ConnectionStringName": "Sqlite" + "DefaultPersistence": { + "Enabled": true, + "Framework": "YesSql", + "ConnectionStringIdentifier": "Sqlite" }, ... ``` @@ -72,9 +87,10 @@ YesSql MySQL ``` "Elsa": { "Features": { - "PersistenceYesSqlMySql": { - "Enabled": true, - "ConnectionStringName": "MySql" + "DefaultPersistence": { + "Enabled": true, + "Framework": "YesSql", + "ConnectionStringIdentifier": "MySql" }, ... ``` @@ -83,9 +99,10 @@ YesSql SQL Server ``` "Elsa": { "Features": { - "PersistenceYesSqlSqlServer": { - "Enabled": true, - "ConnectionStringName": "SqlServer" + "DefaultPersistence": { + "Enabled": true, + "Framework": "YesSql", + "ConnectionStringIdentifier": "SqlServer" }, ... ``` @@ -94,48 +111,11 @@ YesSql Postgre ``` "Elsa": { "Features": { - "PersistenceYesSqlPostgreSql": { - "Enabled": true, - "ConnectionStringName": "PostgreSql" - }, - ... -``` - -### Configure modular activity providers such as Webhooks -It is required to add modular activity provider and additional persistence feature for your modular activity provider. -Two examples below are given to set up Elsa Core and Webhooks persistence as follow. - -EF Sqlite -``` -"Elsa": { - "Features": { - "PersistenceEntityFrameworkCoreSqlite": { + "DefaultPersistence": { "Enabled": true, - "ConnectionStringName": "Sqlite" + "Framework": "YesSql", + "ConnectionStringIdentifier": "PostgreSql" }, - "Webhooks": true - }, - "WebhooksPersistenceEntityFrameworkCoreSqlite": { - "Enabled": true, - "ConnectionStringName": "Sqlite" - } - ... -``` - -EF MongoDB -``` -"Elsa": { - "Features": { - "PersistenceMongoDb": { - "Enabled": true, - "ConnectionStringName": "MongoDb" - }, - "Webhooks": true - }, - "WebhooksPersistenceMongoDb": { - "Enabled": true, - "ConnectionStringName": "MongoDb" - } ... ``` diff --git a/src/samples/server/Elsa.Samples.Server.Host/appsettings.json b/src/samples/server/Elsa.Samples.Server.Host/appsettings.json index df5d8659a..476ea05df 100644 --- a/src/samples/server/Elsa.Samples.Server.Host/appsettings.json +++ b/src/samples/server/Elsa.Samples.Server.Host/appsettings.json @@ -19,9 +19,10 @@ }, "Elsa": { "Features": { - "PersistenceEntityFrameworkCoreSqlite": { + "DefaultPersistence": { "Enabled": true, - "ConnectionStringName": "Sqlite" + "Framework": "EntityFrameworkCore", + "ConnectionStringIdentifier": "Sqlite" }, "DispatcherHangfire": true, "Console": true, @@ -32,10 +33,10 @@ "UserTask": true, "Conductor": true, "Telnyx": true, - "Webhooks": true, - "WebhooksPersistenceEntityFrameworkCoreSqlite": { + "Webhooks": { "Enabled": true, - "ConnectionStringName": "Sqlite" + "Framework": "EntityFrameworkCore", + "ConnectionStringIdentifier": "Sqlite" } }, "WorkflowChannels": { From b0a57eb1482af281ae9ec5683eb7fc7739157481 Mon Sep 17 00:00:00 2001 From: axeleron007 Date: Thu, 8 Jul 2021 13:22:49 +0300 Subject: [PATCH 2/6] Code refactoring Code refactoring --- .../Extensions/ElsaOptionBuilderExtensions.cs | 65 ++++++++++++++----- src/core/Elsa.Core/FeatureOptions.cs | 3 +- .../server/Elsa.Samples.Server.Host/README.md | 2 +- .../Elsa.Samples.Server.Host/appsettings.json | 2 +- 4 files changed, 51 insertions(+), 21 deletions(-) diff --git a/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs index 9467dcea9..86a3ece13 100644 --- a/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs @@ -54,19 +54,12 @@ namespace Elsa var key = feature.Key.Replace($"{elsaFeaturesSection}:", string.Empty); enabledFeatures.Add(key); - var hasFramework = !string.IsNullOrWhiteSpace(featureOptions.Framework); - var hasConnection = !string.IsNullOrWhiteSpace(featureOptions.ConnectionStringIdentifier); + if (featureOptions.Items == null) continue; - if (hasFramework) + foreach (var item in featureOptions.Items) { - enabledFeatures.Add($"{key}:{featureOptions.Framework}"); - } - - if (hasConnection) - { - enabledFeatures.Add(hasFramework - ? $"{key}:{featureOptions.Framework}:{featureOptions.ConnectionStringIdentifier}" - : $"{key}:{featureOptions.ConnectionStringIdentifier}"); + key += $":{item.Value}"; + enabledFeatures.Add(key); } } @@ -75,20 +68,58 @@ namespace Elsa private static FeatureOptions ParseFeatureFlag(IConfiguration configuration, string feature) { - var options = new FeatureOptions(); + var featureOptions = new FeatureOptions(); if (configuration.GetSection($"{feature}:Enabled").Exists()) - { - configuration.GetSection(feature).Bind(options); - return options; + { + var featureItems = configuration.GetSection($"{feature}").AsEnumerable(); + + ParseFeatureItems(feature, featureOptions, featureItems); + ParseFeatureOptions(configuration, feature, featureOptions); + + return featureOptions; } if (!feature.EndsWith(":Enabled")) { bool.TryParse(configuration.GetValue($"{feature}"), out var enabled); - options.Enabled = enabled; + featureOptions.Enabled = enabled; + } + return featureOptions; + } + + private static void ParseFeatureItems(string feature, FeatureOptions featureOptions, IEnumerable> featureItems) + { + featureOptions.Items = new Dictionary(); + + foreach (var featureItem in featureItems) + { + if (featureItem.Value == null) continue; + + var itemKey = featureItem.Key.Replace($"{feature}:", string.Empty); + + if (itemKey.Contains(":")) continue; + + if (itemKey == "Enabled") + { + bool.TryParse(featureItem.Value, out var enabled); + featureOptions.Enabled = enabled; + } + else + { + featureOptions.Items.Add(itemKey, featureItem.Value); + } + } + } + + private static void ParseFeatureOptions(IConfiguration configuration, string feature, FeatureOptions featureOptions) + { + if (configuration.GetSection($"{feature}:Options").Exists()) + { + var options = new Dictionary(); + configuration.GetSection($"{feature}:Options").Bind(options); + featureOptions.Options = options; } - return options; } private static IEnumerable GetAssemblies(IEnumerable assemblyMarkerTypes) => assemblyMarkerTypes.Select(x => x.Assembly).Distinct(); diff --git a/src/core/Elsa.Core/FeatureOptions.cs b/src/core/Elsa.Core/FeatureOptions.cs index 26f7085ab..8be485f63 100644 --- a/src/core/Elsa.Core/FeatureOptions.cs +++ b/src/core/Elsa.Core/FeatureOptions.cs @@ -5,8 +5,7 @@ namespace Elsa public class FeatureOptions { public bool Enabled { get; set; } - public string? Framework { get; set; } - public string? ConnectionStringIdentifier { get; set; } + public Dictionary? Items { get; set; } public Dictionary? Options { get; set; } } } diff --git a/src/samples/server/Elsa.Samples.Server.Host/README.md b/src/samples/server/Elsa.Samples.Server.Host/README.md index e453e07c1..2a4ac4aa7 100644 --- a/src/samples/server/Elsa.Samples.Server.Host/README.md +++ b/src/samples/server/Elsa.Samples.Server.Host/README.md @@ -1,6 +1,6 @@ ### Configure persistence for Elsa Core in the appsettings.json -A simple way to enable a feature without persitence: +A simple way to enable a feature without persistence: ``` "Elsa": { diff --git a/src/samples/server/Elsa.Samples.Server.Host/appsettings.json b/src/samples/server/Elsa.Samples.Server.Host/appsettings.json index 476ea05df..42ba89f69 100644 --- a/src/samples/server/Elsa.Samples.Server.Host/appsettings.json +++ b/src/samples/server/Elsa.Samples.Server.Host/appsettings.json @@ -20,7 +20,7 @@ "Elsa": { "Features": { "DefaultPersistence": { - "Enabled": true, + "Enabled": "true", "Framework": "EntityFrameworkCore", "ConnectionStringIdentifier": "Sqlite" }, From bf5190b94fdcf36104c7a1a921b1bb6e923b69ce Mon Sep 17 00:00:00 2001 From: axeleron007 Date: Thu, 8 Jul 2021 13:52:24 +0300 Subject: [PATCH 3/6] Code clean up Code clean up --- src/samples/server/Elsa.Samples.Server.Host/appsettings.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/samples/server/Elsa.Samples.Server.Host/appsettings.json b/src/samples/server/Elsa.Samples.Server.Host/appsettings.json index 42ba89f69..476ea05df 100644 --- a/src/samples/server/Elsa.Samples.Server.Host/appsettings.json +++ b/src/samples/server/Elsa.Samples.Server.Host/appsettings.json @@ -20,7 +20,7 @@ "Elsa": { "Features": { "DefaultPersistence": { - "Enabled": "true", + "Enabled": true, "Framework": "EntityFrameworkCore", "ConnectionStringIdentifier": "Sqlite" }, From b2f6f093acc60cb85ef9ebcd44b394a905627d57 Mon Sep 17 00:00:00 2001 From: axeleron007 Date: Thu, 8 Jul 2021 15:31:01 +0300 Subject: [PATCH 4/6] Permutations for all feature key/values added Permutations for all feature key/values except Enabled and Options added --- .../Extensions/ElsaOptionBuilderExtensions.cs | 37 ++++++++++++++++--- 1 file changed, 32 insertions(+), 5 deletions(-) diff --git a/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs index 86a3ece13..fba344571 100644 --- a/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs @@ -56,16 +56,43 @@ namespace Elsa if (featureOptions.Items == null) continue; - foreach (var item in featureOptions.Items) - { - key += $":{item.Value}"; - enabledFeatures.Add(key); - } + //Permutations = new List(); + var values = featureOptions.Items.Values.ToArray(); + GetPermutations(key, enabledFeatures, values, 0, values.Length - 1); } return enabledFeatures; } + private static void GetPermutations(string feature, ICollection enabledFeatures, string[] values, int start, int end) + { + if (start == end) + { + foreach (var item in values) + { + feature += $":{item}"; + if (enabledFeatures.Contains(feature)) continue; + enabledFeatures.Add(feature); + } + } + + for (int i = start; i <= end; i++) + { + Swap(ref values[start], ref values[i]); + GetPermutations(feature, enabledFeatures, values, start + 1, end); + Swap(ref values[start], ref values[i]); + } + } + + private static void Swap(ref string item1, ref string item2) + { + if (item1 == item2) return; + + var temp = item1; + item1 = item2; + item2 = temp; + } + private static FeatureOptions ParseFeatureFlag(IConfiguration configuration, string feature) { var featureOptions = new FeatureOptions(); From 6f1b479ffc7192d2099fcdab4de1b8875410d37d Mon Sep 17 00:00:00 2001 From: axeleron007 Date: Thu, 8 Jul 2021 15:32:28 +0300 Subject: [PATCH 5/6] Code clean up Code clean up --- .../Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs index fba344571..241b99726 100644 --- a/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs @@ -56,15 +56,14 @@ namespace Elsa if (featureOptions.Items == null) continue; - //Permutations = new List(); var values = featureOptions.Items.Values.ToArray(); - GetPermutations(key, enabledFeatures, values, 0, values.Length - 1); + GetPermutations(key, values, enabledFeatures, 0, values.Length - 1); } return enabledFeatures; } - private static void GetPermutations(string feature, ICollection enabledFeatures, string[] values, int start, int end) + private static void GetPermutations(string feature, string[] values, ICollection enabledFeatures, int start, int end) { if (start == end) { @@ -79,7 +78,7 @@ namespace Elsa for (int i = start; i <= end; i++) { Swap(ref values[start], ref values[i]); - GetPermutations(feature, enabledFeatures, values, start + 1, end); + GetPermutations(feature, values, enabledFeatures, start + 1, end); Swap(ref values[start], ref values[i]); } } From cb8e398a8f89fcfdf210cb02633cb98ed1e1a40e Mon Sep 17 00:00:00 2001 From: axeleron007 Date: Fri, 9 Jul 2021 10:13:15 +0300 Subject: [PATCH 6/6] Refactoring and comments added Refactoring and comments added --- .../Extensions/ElsaOptionBuilderExtensions.cs | 167 ++++++++++++------ src/core/Elsa.Core/FeatureOptions.cs | 11 -- 2 files changed, 109 insertions(+), 69 deletions(-) delete mode 100644 src/core/Elsa.Core/FeatureOptions.cs diff --git a/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs index 241b99726..cbf7068d7 100644 --- a/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ElsaOptionBuilderExtensions.cs @@ -12,11 +12,19 @@ namespace Elsa { public static ElsaOptionsBuilder AddFeatures(this ElsaOptionsBuilder builder, IEnumerable assemblyMarkerTypes, IConfiguration configuration) => AddFeatures(builder, GetAssemblies(assemblyMarkerTypes), configuration); + /// + /// Parse all features from the appsettings.json, filter only enabled features, + /// find all start up classes with matching attribute and create their instances. + /// + /// ElsaOptionsBuilder + /// Available assembly collection + /// IConfiguration + /// ElsaOptionsBuilder public static ElsaOptionsBuilder AddFeatures(this ElsaOptionsBuilder builder, IEnumerable assemblies, IConfiguration configuration) { var enabledFeatures = ParseFeatures(configuration); - if (enabledFeatures == null!) // Null when configuration binding finds an empty array. + if (enabledFeatures == null!) return builder; enabledFeatures = enabledFeatures.ToHashSet(); @@ -40,6 +48,11 @@ namespace Elsa return builder; } + /// + /// Parse all features from the appsettings.json and popualte enabled feature collection + /// + /// IConfiguration + /// Enabled feature collection private static IEnumerable ParseFeatures(IConfiguration configuration) { var elsaFeaturesSection = "Elsa:Features"; @@ -48,7 +61,7 @@ namespace Elsa foreach (var feature in features) { - var featureOptions = ParseFeatureFlag(configuration, feature.Key); + var featureOptions = ParseFeatureSection(configuration, feature.Key); if (!featureOptions.Enabled) continue; var key = feature.Key.Replace($"{elsaFeaturesSection}:", string.Empty); @@ -63,6 +76,88 @@ namespace Elsa return enabledFeatures; } + /// + /// Parse single feature section from the appsettings.json and popualte feature model + /// + /// IConfiguration + /// Feature name + /// Populated feature model + private static FeatureModel ParseFeatureSection(IConfiguration configuration, string feature) + { + var featureModel = new FeatureModel(); + + if (configuration.GetSection($"{feature}:Enabled").Exists()) + { + var featureItems = configuration.GetSection($"{feature}").AsEnumerable(); + + ParseFeatureItems(feature, featureModel, featureItems); + ParseFeatureOptions(configuration, feature, featureModel); + + return featureModel; + } + + if (!feature.EndsWith(":Enabled")) + { + bool.TryParse(configuration.GetValue($"{feature}"), out var enabled); + featureModel.Enabled = enabled; + } + return featureModel; + } + + /// + /// Parse feature section key/value collection except Enabled and Options keys from the appsetting.json and popualte feature model + /// + /// Feature name + /// Feature model + /// Feature section kay/value collection + private static void ParseFeatureItems(string feature, FeatureModel featureModel, IEnumerable> featureItems) + { + featureModel.Items = new Dictionary(); + + foreach (var featureItem in featureItems) + { + if (featureItem.Value == null) continue; + + var itemKey = featureItem.Key.Replace($"{feature}:", string.Empty); + + if (itemKey.Contains(":")) continue; + + if (itemKey == "Enabled") + { + bool.TryParse(featureItem.Value, out var enabled); + featureModel.Enabled = enabled; + } + else + { + featureModel.Items.Add(itemKey, featureItem.Value); + } + } + } + + /// + /// Parse feature Options section from the appsetting.json and populate feature model + /// + /// IConfiguration + /// Feature name + /// Feature model + private static void ParseFeatureOptions(IConfiguration configuration, string feature, FeatureModel featureModel) + { + if (configuration.GetSection($"{feature}:Options").Exists()) + { + var options = new Dictionary(); + configuration.GetSection($"{feature}:Options").Bind(options); + featureModel.Options = options; + } + } + + /// + /// Permutate all possible order combinations in Feature section key/value collection + /// + /// Feature name + /// Feature values array to permutate + /// Enabled feature collection + /// Start index + /// End index private static void GetPermutations(string feature, string[] values, ICollection enabledFeatures, int start, int end) { if (start == end) @@ -83,6 +178,11 @@ namespace Elsa } } + /// + /// Swap two string values + /// + /// + /// private static void Swap(ref string item1, ref string item2) { if (item1 == item2) return; @@ -92,62 +192,13 @@ namespace Elsa item2 = temp; } - private static FeatureOptions ParseFeatureFlag(IConfiguration configuration, string feature) - { - var featureOptions = new FeatureOptions(); - - if (configuration.GetSection($"{feature}:Enabled").Exists()) - { - var featureItems = configuration.GetSection($"{feature}").AsEnumerable(); - - ParseFeatureItems(feature, featureOptions, featureItems); - ParseFeatureOptions(configuration, feature, featureOptions); - - return featureOptions; - } - - if (!feature.EndsWith(":Enabled")) - { - bool.TryParse(configuration.GetValue($"{feature}"), out var enabled); - featureOptions.Enabled = enabled; - } - return featureOptions; - } - - private static void ParseFeatureItems(string feature, FeatureOptions featureOptions, IEnumerable> featureItems) - { - featureOptions.Items = new Dictionary(); - - foreach (var featureItem in featureItems) - { - if (featureItem.Value == null) continue; - - var itemKey = featureItem.Key.Replace($"{feature}:", string.Empty); - - if (itemKey.Contains(":")) continue; - - if (itemKey == "Enabled") - { - bool.TryParse(featureItem.Value, out var enabled); - featureOptions.Enabled = enabled; - } - else - { - featureOptions.Items.Add(itemKey, featureItem.Value); - } - } - } - - private static void ParseFeatureOptions(IConfiguration configuration, string feature, FeatureOptions featureOptions) - { - if (configuration.GetSection($"{feature}:Options").Exists()) - { - var options = new Dictionary(); - configuration.GetSection($"{feature}:Options").Bind(options); - featureOptions.Options = options; - } - } - private static IEnumerable GetAssemblies(IEnumerable assemblyMarkerTypes) => assemblyMarkerTypes.Select(x => x.Assembly).Distinct(); + + private class FeatureModel + { + public bool Enabled { get; set; } + public Dictionary? Items { get; set; } + public Dictionary? Options { get; set; } + } } } \ No newline at end of file diff --git a/src/core/Elsa.Core/FeatureOptions.cs b/src/core/Elsa.Core/FeatureOptions.cs deleted file mode 100644 index 8be485f63..000000000 --- a/src/core/Elsa.Core/FeatureOptions.cs +++ /dev/null @@ -1,11 +0,0 @@ -using System.Collections.Generic; - -namespace Elsa -{ - public class FeatureOptions - { - public bool Enabled { get; set; } - public Dictionary? Items { get; set; } - public Dictionary? Options { get; set; } - } -}