diff --git a/Directory.Packages.props b/Directory.Packages.props index ca4477da27..8709d39471 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,6 +5,9 @@ + + + @@ -31,18 +34,21 @@ - - - + + + + + + @@ -94,14 +100,17 @@ + + + + + + - - - diff --git a/Elsa.sln b/Elsa.sln index 6602771a47..c043709eed 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -327,6 +327,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issue_templates", "issue_te EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dsl", "dsl", "{477C2416-312D-46AE-BCD6-8FA1FAB43624}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.ModularServer.Web", "src\apps\Elsa.ModularServer.Web\Elsa.ModularServer.Web.csproj", "{E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Resilience.Core.UnitTests", "test\unit\Elsa.Resilience.Core.UnitTests\Elsa.Resilience.Core.UnitTests.csproj", "{B8006D70-1630-43DB-A043-FA89FAC70F37}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Common.UnitTests", "test\unit\Elsa.Common.UnitTests\Elsa.Common.UnitTests.csproj", "{A3C07D5B-2A30-494E-B9BC-4B1594B31ABC}" @@ -591,6 +593,10 @@ Global {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48}.Debug|Any CPU.Build.0 = Debug|Any CPU {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48}.Release|Any CPU.Build.0 = Release|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Release|Any CPU.Build.0 = Release|Any CPU {B8006D70-1630-43DB-A043-FA89FAC70F37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B8006D70-1630-43DB-A043-FA89FAC70F37}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8006D70-1630-43DB-A043-FA89FAC70F37}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -708,6 +714,7 @@ Global {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48} = {B08B4E00-C2AB-48F3-8389-449F42AEF179} {477C2416-312D-46AE-BCD6-8FA1FAB43624} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79} {874F5A44-DB06-47AB-A18C-2D13942E0147} = {477C2416-312D-46AE-BCD6-8FA1FAB43624} + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC} = {D92BEAB2-60D6-4BB4-885A-6BA681C6CCF1} {B8006D70-1630-43DB-A043-FA89FAC70F37} = {18453B51-25EB-4317-A4B3-B10518252E92} {A3C07D5B-2A30-494E-B9BC-4B1594B31ABC} = {18453B51-25EB-4317-A4B3-B10518252E92} {8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E} = {1B8D5897-902E-4632-8698-E89CAF3DDF54} diff --git a/NuGet.Config b/NuGet.Config index 54f245bd31..d188f6e5a0 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -4,10 +4,15 @@ + + + + + \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj new file mode 100644 index 0000000000..d2f791b735 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/apps/Elsa.ModularServer.Web/FodyWeavers.xml b/src/apps/Elsa.ModularServer.Web/FodyWeavers.xml new file mode 100644 index 0000000000..00e1d9a1c1 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/Program.cs b/src/apps/Elsa.ModularServer.Web/Program.cs new file mode 100644 index 0000000000..a5251a00ff --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/Program.cs @@ -0,0 +1,25 @@ +using CShells.AspNetCore.Configuration; +using CShells.AspNetCore.Extensions; + +var builder = WebApplication.CreateBuilder(args); +var services = builder.Services; + +// Configure CShells for multi-tenancy with ASP.NET Core integration +// This automatically registers shell-aware authentication and authorization providers +builder.AddShells(shells => shells + .WithWebRouting(options => options.EnablePathRouting = true) + .WithAuthenticationAndAuthorization()); +services.AddHealthChecks(); + +// Add minimal authentication and authorization services in root +// These are required for middleware validation - shells provide the actual configurations +services.AddAuthentication(); +services.AddAuthorization(); + +var app = builder.Build(); + +app.MapHealthChecks("/"); +app.MapShells(); // Sets HttpContext.RequestServices to shell's scoped provider +app.UseAuthentication(); // Runs after MapShells to access shell-specific auth schemes +app.UseAuthorization(); // Runs after MapShells to access shell-specific policies +app.Run(); \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json b/src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json new file mode 100644 index 0000000000..49af15d915 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7293;http://localhost:5002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.Development.json b/src/apps/Elsa.ModularServer.Web/appsettings.Development.json new file mode 100644 index 0000000000..0c208ae918 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.Example.json b/src/apps/Elsa.ModularServer.Web/appsettings.Example.json new file mode 100644 index 0000000000..86ca822c4c --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/appsettings.Example.json @@ -0,0 +1,76 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "CShells": { + "Shells": [ + { + "Name": "Default", + "Properties": { + "WebRouting": { + "Path": "" + } + }, + "Settings": { + "FastEndpoints": { + "GlobalRoutePrefix": "elsa/api" + }, + "SqliteWorkflowDefinitionPersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + }, + "SqliteWorkflowInstancePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared" + }, + "SqliteWorkflowRuntimePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared" + } + }, + "Features": [ + "Elsa", + "WorkflowsApi", + "Identity", + "DefaultAuthentication", + "SqliteWorkflowDefinitionPersistence", + "SqliteWorkflowInstancePersistence", + "SqliteWorkflowRuntimePersistence" + ] + }, + { + "Name": "Tenant1", + "Properties": { + "WebRouting": { + "Path": "tenant1" + } + }, + "Settings": { + "FastEndpoints": { + "GlobalRoutePrefix": "api" + }, + "SqlServerWorkflowDefinitionPersistence": { + "ConnectionString": "${ConnectionStrings:Tenant1}" + }, + "SqlServerWorkflowInstancePersistence": { + "ConnectionString": "${ConnectionStrings:Tenant1}" + } + }, + "Features": [ + "Elsa", + "WorkflowsApi", + "SqlServerWorkflowDefinitionPersistence", + "SqlServerWorkflowInstancePersistence" + ] + } + ] + }, + "ConnectionStrings": { + "Tenant1": "Server=localhost;Database=ElsaTenant1;Integrated Security=true;TrustServerCertificate=true" + } +} diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json new file mode 100644 index 0000000000..e3ca4a7be2 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -0,0 +1,55 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "CShells": { + "Shells": [ + { + "Name": "Default", + "Properties": { + "WebRouting": { + "Path": "foo" + } + }, + "Settings": { + "FastEndpoints": { + "GlobalRoutePrefix": "elsa/api" + }, + "SqliteWorkflowDefinitionPersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + }, + "SqliteWorkflowInstancePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + }, + "SqliteWorkflowRuntimePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + } + }, + "Features": [ + "Elsa", + "WorkflowsApi", + "Identity", + "DefaultAuthentication", + "Resilience", + "SqliteWorkflowPersistence" + ] + } + ] + } +} diff --git a/src/apps/Elsa.Server.Web/Program.cs b/src/apps/Elsa.Server.Web/Program.cs index a1a8de9c7e..c020fcbfba 100644 --- a/src/apps/Elsa.Server.Web/Program.cs +++ b/src/apps/Elsa.Server.Web/Program.cs @@ -42,7 +42,6 @@ var identitySection = configuration.GetSection("Identity"); var identityTokenSection = identitySection.GetSection("Tokens"); -// Add Elsa services. services .AddElsa(elsa => { diff --git a/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj b/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj index 8288f16c23..ff63d15173 100644 --- a/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj +++ b/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj @@ -9,6 +9,8 @@ + + diff --git a/src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs b/src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs new file mode 100644 index 0000000000..947399a206 --- /dev/null +++ b/src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs @@ -0,0 +1,50 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; +using CShells.FastEndpoints.Contracts; +using Elsa.Workflows; +using FastEndpoints; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.FastEndpointConfigurators; + +/// +/// Configures FastEndpoints with Elsa-specific serialization options. +/// Uses the same serialization settings as . +/// +[UsedImplicitly] +public class ElsaFastEndpointsConfigurator : IFastEndpointsConfigurator +{ + /// + public void Configure(Config config) + { + config.Serializer.RequestDeserializer = DeserializeRequestAsync; + config.Serializer.ResponseSerializer = SerializeResponseAsync; + + config.Binding.ValueParserFor(s => + new(DateTimeOffset.TryParse(s.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result), result)); + } + + private static ValueTask DeserializeRequestAsync(HttpRequest httpRequest, Type modelType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken) + { + var serializer = httpRequest.HttpContext.RequestServices.GetRequiredService(); + var options = serializer.GetOptions(); + + return serializerContext == null + ? JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, options, cancellationToken) + : JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, serializerContext, cancellationToken); + } + + private static Task SerializeResponseAsync(HttpResponse httpResponse, object? dto, string contentType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken) + { + var serializer = httpResponse.HttpContext.RequestServices.GetRequiredService(); + var options = serializer.GetOptions(); + + httpResponse.ContentType = contentType; + return serializerContext == null + ? JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto?.GetType() ?? typeof(object), options, cancellationToken) + : JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto?.GetType() ?? typeof(object), serializerContext, cancellationToken); + } +} diff --git a/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs b/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs new file mode 100644 index 0000000000..3782b9ad23 --- /dev/null +++ b/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs @@ -0,0 +1,22 @@ +using CShells.FastEndpoints.Contracts; +using CShells.Features; +using Elsa.FastEndpointConfigurators; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.ShellFeatures; + +/// +/// Registers the Elsa-specific FastEndpoints configurator. +/// This feature should be enabled alongside the CShells FastEndpointsFeature to apply Elsa's serialization settings. +/// +[ShellFeature(DependsOn = ["FastEndpoints"])] +[UsedImplicitly] +public class ElsaFastEndpointsFeature : IShellFeature +{ + /// + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + } +} diff --git a/src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs b/src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs new file mode 100644 index 0000000000..926b377dc8 --- /dev/null +++ b/src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs @@ -0,0 +1,28 @@ +using Elsa.Features.Models; + +namespace Elsa.Features.Contracts; + +/// +/// Provides read-only access to installed features. +/// +/// +/// This interface is used to query the list of features that are installed and available in the application. +/// Unlike , this interface does not provide write operations, +/// making it suitable for implementations where features are discovered automatically (e.g., from shell features) +/// rather than manually registered. +/// +public interface IInstalledFeatureProvider +{ + /// + /// Gets all installed features. + /// + /// All installed features. + IEnumerable List(); + + /// + /// Finds a feature descriptor by its full name. + /// + /// The full name of the feature. + /// The feature descriptor or null if not found. + FeatureDescriptor? Find(string fullName); +} diff --git a/src/common/Elsa.Features/Elsa.Features.csproj b/src/common/Elsa.Features/Elsa.Features.csproj index 10a3eaeb81..1f20784103 100644 --- a/src/common/Elsa.Features/Elsa.Features.csproj +++ b/src/common/Elsa.Features/Elsa.Features.csproj @@ -8,6 +8,7 @@ + diff --git a/src/common/Elsa.Features/Services/InstalledFeatureProvider.cs b/src/common/Elsa.Features/Services/InstalledFeatureProvider.cs new file mode 100644 index 0000000000..bbba02882b --- /dev/null +++ b/src/common/Elsa.Features/Services/InstalledFeatureProvider.cs @@ -0,0 +1,30 @@ +using Elsa.Features.Contracts; +using Elsa.Features.Models; + +namespace Elsa.Features.Services; + +/// +/// Legacy implementation of that delegates to . +/// +/// +/// This implementation is used when features are manually registered using the legacy feature system. +/// For shell-based feature discovery, use ShellInstalledFeatureProvider instead. +/// +public class InstalledFeatureProvider : IInstalledFeatureProvider +{ + private readonly IInstalledFeatureRegistry _registry; + + /// + /// Initializes a new instance of the class. + /// + public InstalledFeatureProvider(IInstalledFeatureRegistry registry) + { + _registry = registry; + } + + /// + public IEnumerable List() => _registry.List(); + + /// + public FeatureDescriptor? Find(string fullName) => _registry.Find(fullName); +} diff --git a/src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs b/src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs new file mode 100644 index 0000000000..4b27ab1fee --- /dev/null +++ b/src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs @@ -0,0 +1,69 @@ +using CShells.Features; +using Elsa.Features.Contracts; +using Elsa.Features.Models; + +namespace Elsa.Features.Services; + +/// +/// Shell-based implementation of that reads from CShells feature descriptors. +/// +/// +/// This implementation automatically discovers features from the shell's feature descriptors, +/// mapping them to Elsa's model. Features are read from the +/// shell's DI container where they were registered during shell initialization. +/// +public class ShellInstalledFeatureProvider : IInstalledFeatureProvider +{ + private readonly IEnumerable _shellFeatures; + + /// + /// Initializes a new instance of the class. + /// + public ShellInstalledFeatureProvider(IEnumerable shellFeatures) + { + _shellFeatures = shellFeatures; + } + + /// + public IEnumerable List() + { + return _shellFeatures + .Where(sf => sf.StartupType != null) + .Select(MapToElsaFeatureDescriptor); + } + + /// + public FeatureDescriptor? Find(string fullName) + { + var shellFeature = _shellFeatures.FirstOrDefault(sf => + MapToFullName(sf) == fullName); + + return shellFeature != null ? MapToElsaFeatureDescriptor(shellFeature) : null; + } + + private static FeatureDescriptor MapToElsaFeatureDescriptor(ShellFeatureDescriptor shell) + { + // Extract namespace and name from shell feature + var type = shell.StartupType!; + var ns = type.Namespace ?? "Unknown"; + var name = shell.Id; + + // Try to get display name and description from metadata + // These can be set via ShellFeatureAttribute properties + var displayName = shell.Metadata.TryGetValue("DisplayName", out var dn) + ? dn.ToString() ?? name + : name; + + var description = shell.Metadata.TryGetValue("Description", out var desc) + ? desc.ToString() ?? string.Empty + : string.Empty; + + return new FeatureDescriptor(name, ns, displayName, description); + } + + private static string MapToFullName(ShellFeatureDescriptor shell) + { + var ns = shell.StartupType?.Namespace ?? "Unknown"; + return $"{ns}.{shell.Id}"; + } +} diff --git a/src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs b/src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs new file mode 100644 index 0000000000..cb8674b42f --- /dev/null +++ b/src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs @@ -0,0 +1,56 @@ +using CShells.Features; +using Elsa.Alterations.Core.Contracts; +using Elsa.Alterations.Core.Entities; +using Elsa.Alterations.Core.Extensions; +using Elsa.Alterations.Core.Stores; +using Elsa.Alterations.Extensions; +using Elsa.Alterations.Services; +using Elsa.Extensions; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Alterations.ShellFeatures; + +/// +/// Adds the Elsa alterations services. +/// +[ShellFeature( + DisplayName = "Alterations", + Description = "Provides workflow alteration capabilities for modifying running workflow instances")] +[UsedImplicitly] +public class AlterationsFeature : IShellFeature +{ + /// + /// Gets or sets the factory for the alteration plan store. + /// + public Func AlterationPlanStoreFactory { get; set; } = sp => sp.GetRequiredService(); + + /// + /// Gets or sets the factory for the alteration job store. + /// + public Func AlterationJobStoreFactory { get; set; } = sp => sp.GetRequiredService(); + + /// + /// Gets or sets the factory for the alteration job dispatcher. + /// + public Func AlterationJobDispatcherFactory { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddAlterations(); + services.AddAlterationsCore(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddMemoryStore(); + services.AddMemoryStore(); + + services.AddScoped(AlterationPlanStoreFactory); + services.AddScoped(AlterationJobStoreFactory); + services.AddScoped(AlterationJobDispatcherFactory); + } +} + diff --git a/src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs b/src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs new file mode 100644 index 0000000000..6564c76dcd --- /dev/null +++ b/src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs @@ -0,0 +1,23 @@ +using CShells.Features; +using Elsa.Caching.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Caching.ShellFeatures; + +/// +/// Configures the MemoryCache. +/// +[ShellFeature] +[UsedImplicitly] +public class MemoryCacheFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryCache() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + } +} diff --git a/src/modules/Elsa.Common/Elsa.Common.csproj b/src/modules/Elsa.Common/Elsa.Common.csproj index 8aaef8d1a8..7767da9750 100644 --- a/src/modules/Elsa.Common/Elsa.Common.csproj +++ b/src/modules/Elsa.Common/Elsa.Common.csproj @@ -15,6 +15,7 @@ + diff --git a/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs b/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs index b4c8a42b29..473aa01e4a 100644 --- a/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs +++ b/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs @@ -12,43 +12,46 @@ namespace Elsa.Extensions; /// public static class DependencyInjectionExtensions { - /// - /// Adds a memory store for the specified entity. - /// - public static IServiceCollection AddMemoryStore(this IServiceCollection services) where TStore : class + extension(IServiceCollection services) { - services.TryAddSingleton>(); - services.TryAddScoped(); - return services; - } + /// + /// Adds a memory store for the specified entity. + /// + public IServiceCollection AddMemoryStore() where TStore : class + { + services.TryAddSingleton>(); + services.TryAddScoped(); + return services; + } - /// - /// Adds a serialization options configurator for the specified type. - /// - public static IServiceCollection AddSerializationOptionsConfigurator(this IServiceCollection services) where T : class, ISerializationOptionsConfigurator - { - services.AddSingleton(); - return services; - } - - public static IServiceCollection AddStartupTask(this IServiceCollection services) where T : class, IStartupTask - { - return services.AddScoped(); - } - - public static IServiceCollection AddBackgroundTask(this IServiceCollection services) where T : class, IBackgroundTask - { - return services.AddScoped(); - } - - public static IServiceCollection AddRecurringTask(this IServiceCollection services) where T : class, IRecurringTask - { - return services.AddScoped(); - } - - public static IServiceCollection AddRecurringTask(this IServiceCollection services, TimeSpan interval) where T : class, IRecurringTask - { - services.Configure(options => options.Schedule.ConfigureTask(interval)); - return services.AddRecurringTask(); + /// + /// Adds a serialization options configurator for the specified type. + /// + public IServiceCollection AddSerializationOptionsConfigurator() where T : class, ISerializationOptionsConfigurator + { + services.AddSingleton(); + return services; + } + + public IServiceCollection AddStartupTask() where T : class, IStartupTask + { + return services.AddScoped(); + } + + public IServiceCollection AddBackgroundTask() where T : class, IBackgroundTask + { + return services.AddScoped(); + } + + public IServiceCollection AddRecurringTask() where T : class, IRecurringTask + { + return services.AddScoped(); + } + + public IServiceCollection AddRecurringTask(TimeSpan interval) where T : class, IRecurringTask + { + services.Configure(options => options.Schedule.ConfigureTask(interval)); + return services.AddRecurringTask(); + } } } \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs b/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs new file mode 100644 index 0000000000..930bfb1a87 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs @@ -0,0 +1,16 @@ +using System.ComponentModel; +using CShells.Features; +using Elsa.Common.Serialization; +using Elsa.Common.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +public class DefaultFormattersFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + TypeDescriptor.AddAttributes(typeof(Type), new TypeConverterAttribute(typeof(TypeTypeConverter))); + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs b/src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs new file mode 100644 index 0000000000..f4f355b4c5 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs @@ -0,0 +1,17 @@ +using CShells.Features; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +/// +/// Adds and configures the Mediator feature. +/// +public class MediatorFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddMediator() + .AddMediatorHostedServices(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs new file mode 100644 index 0000000000..7e43f4e5e6 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs @@ -0,0 +1,41 @@ +using CShells.Features; +using CShells.Hosting; +using Elsa.Common.Multitenancy; +using Elsa.Common.Multitenancy.EventHandlers; +using Elsa.Common.Multitenancy.HostedServices; +using Elsa.Common.RecurringTasks; +using Elsa.Common.ShellHandlers; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +public class MultitenancyFeature : IShellFeature +{ + private readonly Func _tenantsProviderFactory = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + + // TenantTaskManager handles all task lifecycle in the correct order + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) + + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(_tenantsProviderFactory) + + .AddSingleton() + .AddSingleton() + ; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs b/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs new file mode 100644 index 0000000000..3d75e87b30 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using Elsa.Common.Codecs; +using Elsa.Common.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +[UsedImplicitly] +public class StringCompressionFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs b/src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs new file mode 100644 index 0000000000..93dd9831e7 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs @@ -0,0 +1,16 @@ +using CShells.Features; +using Elsa.Common.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +/// +/// Configures the system clock. +/// +public class SystemClockFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs b/src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs new file mode 100644 index 0000000000..39f0fd0f77 --- /dev/null +++ b/src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs @@ -0,0 +1,19 @@ +using CShells.Hosting; +using Elsa.Common.Multitenancy; +using JetBrains.Annotations; + +namespace Elsa.Common.ShellHandlers; + +[UsedImplicitly] +public class ActivateShellTenants(ITenantService tenantService) : IShellActivatedHandler, IShellDeactivatingHandler +{ + public Task OnActivatedAsync(CancellationToken cancellationToken = default) + { + return tenantService.ActivateTenantsAsync(cancellationToken); + } + + public Task OnDeactivatingAsync(CancellationToken cancellationToken = default) + { + return tenantService.DeactivateTenantsAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs b/src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs new file mode 100644 index 0000000000..7d3959f677 --- /dev/null +++ b/src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs @@ -0,0 +1,28 @@ +using CShells.Features; +using Elsa.Dsl.ElsaScript.Compiler; +using Elsa.Dsl.ElsaScript.Contracts; +using Elsa.Dsl.ElsaScript.Materializers; +using Elsa.Dsl.ElsaScript.Parser; +using Elsa.Workflows.Management; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Dsl.ElsaScript.ShellFeatures; + +/// +/// Feature for ElsaScript DSL support. +/// +[ShellFeature( + DisplayName = "ElsaScript DSL", + Description = "Provides ElsaScript DSL support for defining workflows")] +[UsedImplicitly] +public class ElsaScriptFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + } +} + diff --git a/src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs b/src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs new file mode 100644 index 0000000000..04a8e34fbf --- /dev/null +++ b/src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs @@ -0,0 +1,46 @@ +using CShells.Features; +using Elsa.Expressions.CSharp.Activities; +using Elsa.Expressions.CSharp.Contracts; +using Elsa.Expressions.CSharp.Options; +using Elsa.Expressions.CSharp.Providers; +using Elsa.Expressions.CSharp.Services; +using Elsa.Extensions; +using Elsa.Workflows; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.CSharp.ShellFeatures; + +/// +/// Installs C# integration. +/// +[ShellFeature( + DisplayName = "C# Expressions", + Description = "Provides C# expression evaluation capabilities for workflows", + DependsOn = ["Mediator", "Expressions", "MemoryCache"])] +[UsedImplicitly] +public class CSharpFeature : IShellFeature +{ + /// + /// Configures the . + /// + public Action CSharpOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(CSharpOptions); + + // C# services. + services + .AddExpressionDescriptorProvider() + .AddScoped(); + + // Handlers. + services.AddNotificationHandlersFrom(); + + // UI property handlers. + services.AddScoped(); + } +} + + diff --git a/src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs b/src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs new file mode 100644 index 0000000000..c62bdc6d59 --- /dev/null +++ b/src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs @@ -0,0 +1,76 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Expressions.JavaScript.Options; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.JavaScript.Libraries.ShellFeatures; + +/// +/// Base class for JavaScript library shell features. +/// +public abstract class ScriptModuleShellFeatureBase : IShellFeature +{ + private readonly string _moduleName; + + protected ScriptModuleShellFeatureBase(string moduleName) + { + _moduleName = moduleName; + } + + public void ConfigureServices(IServiceCollection services) + { + var moduleName = _moduleName; + services.Configure(options => + { + options.ConfigureEngine((engine, context) => + { + var resourceName = $"Elsa.Expressions.JavaScript.Libraries.ClientLib.dist.{moduleName}.js"; + using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)!; + using var reader = new StreamReader(stream); + var script = reader.ReadToEnd(); + engine.Execute(script); + }); + }); + } +} + +/// +/// Adds Lodash library support to JavaScript expressions. +/// +[ShellFeature( + DisplayName = "Lodash JavaScript Library", + Description = "Provides Lodash utility library for JavaScript expressions", + DependsOn = ["JavaScript"])] +[UsedImplicitly] +public class LodashFeature : ScriptModuleShellFeatureBase +{ + public LodashFeature() : base("lodash") { } +} + +/// +/// Adds Lodash FP library support to JavaScript expressions. +/// +[ShellFeature( + DisplayName = "Lodash FP JavaScript Library", + Description = "Provides Lodash FP (functional programming) utility library for JavaScript expressions", + DependsOn = ["JavaScript"])] +[UsedImplicitly] +public class LodashFpFeature : ScriptModuleShellFeatureBase +{ + public LodashFpFeature() : base("lodashFp") { } +} + +/// +/// Adds Moment.js library support to JavaScript expressions. +/// +[ShellFeature( + DisplayName = "Moment JavaScript Library", + Description = "Provides Moment.js date/time library for JavaScript expressions", + DependsOn = ["JavaScript"])] +[UsedImplicitly] +public class MomentFeature : ScriptModuleShellFeatureBase +{ + public MomentFeature() : base("moment") { } +} + diff --git a/src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs b/src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs new file mode 100644 index 0000000000..4f06cffef9 --- /dev/null +++ b/src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs @@ -0,0 +1,71 @@ +using CShells.Features; +using Elsa.Expressions.JavaScript.Activities; +using Elsa.Expressions.JavaScript.Contracts; +using Elsa.Expressions.JavaScript.Extensions; +using Elsa.Expressions.JavaScript.HostedServices; +using Elsa.Expressions.JavaScript.Options; +using Elsa.Expressions.JavaScript.Providers; +using Elsa.Expressions.JavaScript.Services; +using Elsa.Expressions.JavaScript.TypeDefinitions.Contracts; +using Elsa.Expressions.JavaScript.TypeDefinitions.Services; +using Elsa.Extensions; +using Elsa.Workflows; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.JavaScript.ShellFeatures; + +/// +/// Installs JavaScript integration. +/// +[ShellFeature( + DisplayName = "JavaScript Expressions", + Description = "Provides JavaScript expression evaluation capabilities for workflows", + DependsOn = ["Mediator", "Expressions", "MemoryCache"])] +[UsedImplicitly] +public class JavaScriptFeature : IShellFeature +{ + /// + /// Configures the Jint options. + /// + public Action JintOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(JintOptions); + + // JavaScript services. + services + .AddScoped() + .AddScoped() + .AddExpressionDescriptorProvider(); + + // Type definition services. + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddFunctionDefinitionProvider() + .AddFunctionDefinitionProvider() + .AddFunctionDefinitionProvider() + .AddTypeDefinitionProvider() + .AddTypeDefinitionProvider() + .AddTypeDefinitionProvider() + .AddVariableDefinitionProvider(); + + // Handlers. + services.AddNotificationHandlersFrom(); + + // Type Script definitions. + services.AddFunctionDefinitionProvider(); + + // UI property handlers. + services.AddScoped(); + + // Hosted services. + services.AddHostedService(); + } +} + + diff --git a/src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs b/src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs new file mode 100644 index 0000000000..6702aa2d11 --- /dev/null +++ b/src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs @@ -0,0 +1,50 @@ +using CShells.Features; +using Elsa.Expressions.Liquid.Contracts; +using Elsa.Expressions.Liquid.Filters; +using Elsa.Expressions.Liquid.Handlers; +using Elsa.Expressions.Liquid.Options; +using Elsa.Expressions.Liquid.Providers; +using Elsa.Expressions.Liquid.Services; +using Elsa.Extensions; +using Fluid.Filters; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.Liquid.ShellFeatures; + +/// +/// Configures Liquid functionality. +/// +[ShellFeature( + DisplayName = "Liquid Expressions", + Description = "Provides Liquid template expression evaluation capabilities for workflows", + DependsOn = ["MemoryCache", "Mediator", "Expressions"])] +[UsedImplicitly] +public class LiquidFeature : IShellFeature +{ + /// + /// Configures the Fluid options. + /// + public Action FluidOptions { get; set; } = options => + { + options.ConfigureFilters = context => context.Options.Filters + .WithArrayFilters() + .WithStringFilters() + .WithNumberFilters() + .WithMiscFilters(); + }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(FluidOptions); + + services + .AddHandlersFrom() + .AddScoped() + .AddScoped() + .AddExpressionDescriptorProvider() + .AddLiquidFilter("base64") + .AddLiquidFilter("keys"); + } +} + diff --git a/src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs b/src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs new file mode 100644 index 0000000000..7b5b386282 --- /dev/null +++ b/src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs @@ -0,0 +1,50 @@ +using CShells.Features; +using Elsa.Expressions.Python.Activities; +using Elsa.Expressions.Python.Contracts; +using Elsa.Expressions.Python.HostedServices; +using Elsa.Expressions.Python.Options; +using Elsa.Expressions.Python.Providers; +using Elsa.Expressions.Python.Services; +using Elsa.Extensions; +using Elsa.Workflows; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.Python.ShellFeatures; + +/// +/// Installs Python integration. +/// +[ShellFeature( + DisplayName = "Python Expressions", + Description = "Provides Python expression evaluation capabilities for workflows", + DependsOn = ["Mediator", "Expressions"])] +[UsedImplicitly] +public class PythonFeature : IShellFeature +{ + /// + /// Configures the . + /// + public Action PythonOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(PythonOptions); + + // Python services. + services + .AddScoped() + .AddExpressionDescriptorProvider(); + + // Handlers. + services.AddNotificationHandlersFrom(); + + // UI property handlers. + services.AddScoped(); + + // Hosted services. + services.AddHostedService(); + } +} + + diff --git a/src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs b/src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs new file mode 100644 index 0000000000..94032e21d8 --- /dev/null +++ b/src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs @@ -0,0 +1,19 @@ +using CShells.Features; +using Elsa.Expressions.Contracts; +using Elsa.Expressions.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.ShellFeatures; + +/// +/// Installs and configures the expressions feature. +/// +public class ExpressionsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddScoped() + .AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs b/src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs new file mode 100644 index 0000000000..fc6eae7f46 --- /dev/null +++ b/src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs @@ -0,0 +1,42 @@ +using CShells.Features; +using Elsa.Hosting.Management.Contracts; +using Elsa.Hosting.Management.HostedServices; +using Elsa.Hosting.Management.Options; +using Elsa.Hosting.Management.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Hosting.Management.ShellFeatures; + +/// +/// Installs and configures the clustering feature. +/// +[ShellFeature( + DisplayName = "Clustering", + Description = "Provides clustering and heartbeat capabilities for distributed Elsa deployments")] +[UsedImplicitly] +public class ClusteringFeature : IShellFeature +{ + /// + /// A factory that instantiates an . + /// + public Func InstanceNameProvider { get; set; } = sp => + { + return ActivatorUtilities.CreateInstance(sp); + }; + + /// + /// Represents the options for heartbeat feature. + /// + public Action HeartbeatOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(HeartbeatOptions) + .AddSingleton(InstanceNameProvider) + .AddSingleton() + .AddHostedService() + .AddHostedService(); + } +} + diff --git a/src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs b/src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs new file mode 100644 index 0000000000..beb7d69d2b --- /dev/null +++ b/src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs @@ -0,0 +1,28 @@ +using CShells.Features; +using Elsa.Http.Handlers; +using Elsa.Http.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Http.ShellFeatures; + +/// +/// Installs services related to HTTP caching. +/// +[ShellFeature( + DisplayName = "HTTP Cache", + Description = "Provides HTTP workflow caching for improved performance", + DependsOn = ["Http", "CachingWorkflowDefinitions"])] +[UsedImplicitly] +public class HttpCacheFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .Decorate() + .AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs b/src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs new file mode 100644 index 0000000000..519590aadd --- /dev/null +++ b/src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs @@ -0,0 +1,211 @@ +using CShells.Features; +using Elsa.Expressions.Options; +using Elsa.Extensions; +using Elsa.Http.Bookmarks; +using Elsa.Http.ContentWriters; +using Elsa.Http.DownloadableContentHandlers; +using Elsa.Http.FileCaches; +using Elsa.Http.Handlers; +using Elsa.Http.Options; +using Elsa.Http.Parsers; +using Elsa.Http.PortResolvers; +using Elsa.Http.Selectors; +using Elsa.Http.Services; +using Elsa.Http.Tasks; +using Elsa.Http.TriggerPayloadValidators; +using Elsa.Http.UIHints; +using Elsa.Workflows; +using FluentStorage; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Elsa.Http.ShellFeatures; + +/// +/// Installs services related to HTTP services and activities. +/// +[ShellFeature( + DisplayName = "HTTP", + Description = "Provides HTTP-related activities and services for workflow execution", + DependsOn = ["HttpJavaScript", "Resilience"])] +[UsedImplicitly] +public class HttpFeature : IShellFeature +{ + /// + /// A delegate to configure . + /// + public Action? ConfigureHttpOptions { get; set; } + + /// + /// A delegate to configure . + /// + public Action? ConfigureHttpFileCacheOptions { get; set; } + + /// + /// A delegate that is invoked when authorizing an inbound HTTP request. + /// + public Func HttpEndpointAuthorizationHandler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that is invoked when an HTTP workflow faults. + /// + public Func HttpEndpointWorkflowFaultHandler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate to configure the . + /// + public Func ContentTypeProvider { get; set; } = _ => new FileExtensionContentTypeProvider(); + + /// + /// A delegate to configure the . + /// + public Func FileCache { get; set; } = sp => + { + var options = sp.GetRequiredService>().Value; + var blobStorage = StorageFactory.Blobs.DirectoryFiles(options.LocalCacheDirectory); + return new BlobFileCacheStorageProvider(blobStorage); + }; + + /// + /// A delegate to configure the used when by the and activities. + /// + public Action HttpClient { get; set; } = (_, _) => { }; + + /// + /// A delegate to configure the for . + /// + public Action HttpClientBuilder { get; set; } = _ => { }; + + /// + /// A list of types to register with the service collection. + /// + public ICollection HttpCorrelationIdSelectorTypes { get; } = new List + { + typeof(HeaderHttpCorrelationIdSelector), + typeof(QueryStringHttpCorrelationIdSelector) + }; + + /// + /// A list of types to register with the service collection. + /// + public ICollection HttpWorkflowInstanceIdSelectorTypes { get; } = new List + { + typeof(HeaderHttpWorkflowInstanceIdSelector), + typeof(QueryStringHttpWorkflowInstanceIdSelector) + }; + + public void ConfigureServices(IServiceCollection services) + { + var configureOptions = ConfigureHttpOptions ?? (options => + { + options.BasePath = "/workflows"; + options.BaseUrl = new Uri("http://localhost"); + }); + + var configureFileCacheOptions = ConfigureHttpFileCacheOptions ?? (options => { options.TimeToLive = TimeSpan.FromDays(7); }); + + services.Configure(configureOptions); + services.Configure(configureFileCacheOptions); + + var httpClientBuilder = services.AddHttpClient(HttpClient); + HttpClientBuilder(httpClientBuilder); + + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(ContentTypeProvider) + .AddHttpContextAccessor() + + // Handlers. + .AddNotificationHandler() + + // Content parsers. + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + + // HTTP content factories. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Activity property options providers. + .AddScoped() + .AddScoped() + + // Default providers. + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()) + + // Port resolvers. + .AddScoped() + + // HTTP endpoint handlers. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(HttpEndpointWorkflowFaultHandler) + .AddScoped(HttpEndpointAuthorizationHandler) + .AddScoped(sp => sp.GetRequiredService()) + + // Startup tasks. + .AddStartupTask() + + // Downloadable content handlers. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + //Trigger payload validators. + .AddTriggerPayloadValidator() + + // File caches. + .AddScoped(FileCache) + .AddScoped() + + // AuthenticationBasedHttpEndpointAuthorizationHandler requires Authorization services. + .AddAuthorization(); + + // HTTP clients. + services.AddHttpClient(); + + // Add selectors. + foreach (var httpCorrelationIdSelectorType in HttpCorrelationIdSelectorTypes) + services.AddScoped(typeof(IHttpCorrelationIdSelector), httpCorrelationIdSelectorType); + + foreach (var httpWorkflowInstanceIdSelectorType in HttpWorkflowInstanceIdSelectorTypes) + services.AddScoped(typeof(IHttpWorkflowInstanceIdSelector), httpWorkflowInstanceIdSelectorType); + + services.Configure(options => + { + options.AddTypeAlias("HttpRequest"); + options.AddTypeAlias("HttpResponse"); + options.AddTypeAlias("HttpResponseMessage"); + options.AddTypeAlias("HttpHeaders"); + options.AddTypeAlias("RouteData"); + options.AddTypeAlias("FormFile"); + options.AddTypeAlias("FormFile[]"); + options.AddTypeAlias("HttpFile"); + options.AddTypeAlias("HttpFile[]"); + options.AddTypeAlias("Downloadable"); + options.AddTypeAlias("Downloadable[]"); + }); + } +} + diff --git a/src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs b/src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs new file mode 100644 index 0000000000..05d73b0ebc --- /dev/null +++ b/src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs @@ -0,0 +1,23 @@ +using CShells.Features; +using Elsa.Http.Scripting.JavaScript; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Http.ShellFeatures; + +/// +/// Provides JavaScript integration for HTTP features. +/// +[ShellFeature( + DisplayName = "HTTP JavaScript", + Description = "Provides JavaScript integration for HTTP activities")] +[UsedImplicitly] +public class HttpJavaScriptFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Identity/Elsa.Identity.csproj b/src/modules/Elsa.Identity/Elsa.Identity.csproj index 7f1bbc5e34..76ecf8edc2 100644 --- a/src/modules/Elsa.Identity/Elsa.Identity.csproj +++ b/src/modules/Elsa.Identity/Elsa.Identity.csproj @@ -9,6 +9,7 @@ + diff --git a/src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs b/src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs new file mode 100644 index 0000000000..c317830572 --- /dev/null +++ b/src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs @@ -0,0 +1,73 @@ +using AspNetCore.Authentication.ApiKey; +using CShells.Features; +using Elsa.Extensions; +using Elsa.Identity.Providers; +using Elsa.Requirements; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Identity.ShellFeatures; + +/// +/// Provides an authorization feature that configures the system with JWT bearer and API key authentication. +/// +[ShellFeature( + DisplayName = "Default Authentication", + Description = "Provides JWT bearer and API key authentication", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class DefaultAuthenticationFeature : IShellFeature +{ + private const string MultiScheme = "Jwt-or-ApiKey"; + + /// + /// Gets or sets the API key provider type. + /// + public Type ApiKeyProviderType { get; set; } = typeof(DefaultApiKeyProvider); + + /// + /// Gets or sets whether to require localhost for the security root policy. + /// + public bool RequireLocalHost { get; set; } = true; + + public void ConfigureServices(IServiceCollection services) + { + services.ConfigureOptions(); + services.ConfigureOptions(); + + var authBuilder = services + .AddAuthentication(MultiScheme) + .AddPolicyScheme(MultiScheme, MultiScheme, options => + { + options.ForwardDefaultSelector = context => + { + return context.Request.Headers.Authorization.Any(x => x!.Contains(ApiKeyDefaults.AuthenticationScheme)) + ? ApiKeyDefaults.AuthenticationScheme + : JwtBearerDefaults.AuthenticationScheme; + }; + }) + .AddJwtBearer(); + + // Configure API key authorization based on provider type + if (ApiKeyProviderType == typeof(AdminApiKeyProvider)) + authBuilder.AddApiKeyInAuthorizationHeader(); + else + authBuilder.AddApiKeyInAuthorizationHeader(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(ApiKeyProviderType); + services.AddScoped(sp => (IApiKeyProvider)sp.GetRequiredService(ApiKeyProviderType)); + + services.AddAuthorization(options => + { + if (RequireLocalHost) + options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.AddRequirements(new LocalHostPermissionRequirement())); + else + options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.RequireAuthenticatedUser()); + }); + } +} diff --git a/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs b/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs new file mode 100644 index 0000000000..d03b496ee3 --- /dev/null +++ b/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs @@ -0,0 +1,119 @@ +using AspNetCore.Authentication.ApiKey; +using CShells.FastEndpoints.Features; +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Extensions; +using Elsa.Identity.Contracts; +using Elsa.Identity.Entities; +using Elsa.Identity.Multitenancy; +using Elsa.Identity.Options; +using Elsa.Identity.Providers; +using Elsa.Identity.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Identity.ShellFeatures; + +/// +/// Provides identity feature to authenticate & authorize API requests. +/// +[ShellFeature( + DisplayName = "Identity", + Description = "Provides identity management, authentication and authorization capabilities", + DependsOn = ["SystemClock"])] +[UsedImplicitly] +public class IdentityFeature : IFastEndpointsShellFeature +{ + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func UserStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func ApplicationStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func RoleStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func UserProvider { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func ApplicationProvider { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func RoleProvider { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + // Configure options with defaults + services.Configure(options => { options.SigningKey = "A really long signing key that should be replaced with something more secure."; }); + services.Configure(ApiKeyDefaults.AuthenticationScheme, options => + { + options.Realm = "Elsa Workflows"; + options.KeyName = "ApiKey"; + }); + services.Configure(_ => { }); + services.Configure(_ => { }); + services.Configure(_ => { }); + + // Memory stores. + services + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore(); + + // User providers. + services + .AddScoped() + .AddScoped() + .AddScoped(); + + // Application providers. + services + .AddScoped() + .AddScoped(); + + // Role providers. + services + .AddScoped() + .AddScoped() + .AddScoped(); + + // Tenant resolution strategies. + services + .AddScoped() + .AddScoped(); + + // Services. + services + .AddScoped(UserStore) + .AddScoped(ApplicationStore) + .AddScoped(RoleStore) + .AddScoped(UserProvider) + .AddScoped(ApplicationProvider) + .AddScoped(RoleProvider) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()) + .AddScoped(sp => sp.GetRequiredService()) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddHttpContextAccessor() + ; + } +} diff --git a/src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs b/src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs new file mode 100644 index 0000000000..7cbb1ce595 --- /dev/null +++ b/src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs @@ -0,0 +1,27 @@ +using CShells.Features; +using Elsa.KeyValues.Contracts; +using Elsa.KeyValues.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.KeyValues.ShellFeatures; + +/// +/// Installs and configures key-value store features. +/// +[ShellFeature( + DisplayName = "Key-Value Store", + Description = "Provides key-value storage capabilities for workflows")] +[UsedImplicitly] +public class KeyValueFeature : IShellFeature +{ + /// + /// A factory that instantiates an . + /// + public Func KeyValueStore { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(KeyValueStore); + } +} diff --git a/src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs b/src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs new file mode 100644 index 0000000000..2c67715ea1 --- /dev/null +++ b/src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs @@ -0,0 +1,42 @@ +using CShells.Features; +using Elsa.Extensions; +using Elsa.Labels.Contracts; +using Elsa.Labels.Entities; +using Elsa.Labels.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Labels.ShellFeatures; + +/// +/// Enables functionality to tag workflows with labels. +/// +[ShellFeature( + DisplayName = "Labels", + Description = "Enables functionality to tag workflows with labels", + DependsOn = ["Mediator"])] +[UsedImplicitly] +public class LabelsFeature : IShellFeature +{ + /// + /// A delegate that provides an instance of an implementation of . + /// + public Func LabelStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that provides an instance of an implementation of . + /// + public Func WorkflowDefinitionLabelStore { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryStore() + .AddMemoryStore() + .AddScoped(LabelStore) + .AddScoped(WorkflowDefinitionLabelStore); + + services.AddNotificationHandlersFrom(GetType()); + } +} + diff --git a/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs b/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs new file mode 100644 index 0000000000..4da028d57c --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs @@ -0,0 +1,123 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Common.Entities; +using Elsa.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.DependencyInjection; + +// ReSharper disable once CheckNamespace +namespace Elsa.Persistence.EFCore; + +public abstract class PersistenceShellFeatureBase : IShellFeature + where TDbContext : ElsaDbContextBase +{ + /// + /// Gets or sets a value indicating whether to use context pooling. + /// + public bool UseContextPooling { get; set; } + + /// + /// Gets or sets a value indicating whether to run migrations. + /// + public bool RunMigrations { get; set; } = true; + + /// + /// Gets or sets the lifetime of the . Defaults to . + /// + public ServiceLifetime DbContextFactoryLifetime { get; set; } = ServiceLifetime.Scoped; + + /// + /// Gets or sets the connection string to use for the database. + /// + public string ConnectionString { get; set; } = null!; + + /// + /// Gets or sets additional options to configure the database context. + /// + public ElsaDbContextOptions? DbContextOptions { get; set; } + + /// + /// Gets or sets the callback used to configure the . + /// + protected virtual Action DbContextOptionsBuilder { get; set; } = (_, _) => { }; + + public void ConfigureServices(IServiceCollection services) + { + Action setup = (sp, opts) => + { + opts.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); + + // Configure the database provider + var migrationsAssembly = GetMigrationsAssembly(); + ConfigureProvider(opts, migrationsAssembly, ConnectionString, DbContextOptions); + + // Allow derived classes to further configure + DbContextOptionsBuilder(sp, opts); + }; + + if (UseContextPooling) + services.AddPooledDbContextFactory(setup); + else + services.AddDbContextFactory(setup, DbContextFactoryLifetime); + + services.Decorate, TenantAwareDbContextFactory>(); + + services.Configure(options => + { + options.RunMigrations[typeof(TDbContext)] = RunMigrations; + }); + + services.AddStartupTask>(); + OnConfiguring(services); + } + + /// + /// Gets the assembly containing migrations for this provider. + /// By default, returns the assembly of the concrete feature type. + /// + protected virtual Assembly GetMigrationsAssembly() => GetType().Assembly; + + /// + /// Configures the database provider for the specified . + /// + /// The options builder to configure. + /// The assembly containing migrations. + /// The connection string to use. + /// Additional options to configure the database context. + protected abstract void ConfigureProvider( + DbContextOptionsBuilder builder, + Assembly migrationsAssembly, + string connectionString, + ElsaDbContextOptions? options); + + protected virtual void OnConfiguring(IServiceCollection services) + { + } + + /// + /// Adds a store to the service collection. + /// + /// The type of the entity. + /// The type of the store. + protected void AddStore(IServiceCollection services) where TEntity : class, new() where TStore : class + { + services + .AddScoped>() + .AddScoped() + ; + } + + /// + /// Adds an entity store to the service collection. + /// + /// The type of the entity. + /// The type of the store. + protected void AddEntityStore(IServiceCollection services) where TEntity : Entity, new() where TStore : class + { + services + .AddScoped>() + .AddScoped() + ; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs new file mode 100644 index 0000000000..62a740dfb2 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Alterations Persistence", + Description = "Provides MySql persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class MySqlAlterationsPersistenceShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs new file mode 100644 index 0000000000..b9d0184ecc --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Identity Persistence", + Description = "Provides MySql persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class MySqlIdentityPersistenceShellFeature + : EFCoreIdentityPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs new file mode 100644 index 0000000000..167bff57e6 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Label Persistence", + Description = "Provides MySql persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class MySqlLabelPersistenceShellFeature + : EFCoreLabelPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..25a075c237 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Management; + +/// +/// Configures the management feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Definition Persistence", + Description = "Provides MySql persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class MySqlWorkflowDefinitionPersistenceShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..986a02f09b --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Management; + +/// +/// Configures the management feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Instance Persistence", + Description = "Provides MySql persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class MySqlWorkflowInstancePersistenceShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs new file mode 100644 index 0000000000..67dff18b86 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures; + +/// +/// Configures the persistence feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Persistence", + Description = "Provides MySql persistence for workflow definitions and runtime data", + DependsOn = ["MySqlWorkflowDefinitionPersistence", "MySqlWorkflowInstancePersistence", "MySqlWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class MySqlWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..179a2b964d --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Runtime Persistence", + Description = "Provides MySql persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class MySqlWorkflowRuntimePersistenceShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs new file mode 100644 index 0000000000..c5b7d14172 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Tenant Persistence", + Description = "Provides MySql persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class MySqlTenantPersistenceShellFeature + : EFCoreTenantManagementShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs new file mode 100644 index 0000000000..e5417957c1 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Alterations Persistence", + Description = "Provides Oracle persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class OracleAlterationsPersistenceShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs new file mode 100644 index 0000000000..7b77d93071 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Identity Persistence", + Description = "Provides Oracle persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class OracleIdentityPersistenceShellFeature + : EFCoreIdentityPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs new file mode 100644 index 0000000000..566efe2ef6 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Label Persistence", + Description = "Provides Oracle persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class OracleLabelPersistenceShellFeature + : EFCoreLabelPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..a218bf9874 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Management; + +/// +/// Configures the management feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Definition Persistence", + Description = "Provides Oracle persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class OracleWorkflowDefinitionPersistenceShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..6c7ab5f961 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Management; + +/// +/// Configures the management feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Instance Persistence", + Description = "Provides Oracle persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class OracleWorkflowInstancePersistenceShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs new file mode 100644 index 0000000000..4ff31945eb --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures; + +/// +/// Configures the persistence feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Persistence", + Description = "Provides Oracle persistence for workflow definitions and runtime data", + DependsOn = ["OracleWorkflowDefinitionPersistence", "OracleWorkflowInstancePersistence", "OracleWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class OracleWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..4547d14862 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Runtime Persistence", + Description = "Provides Oracle persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class OracleWorkflowRuntimePersistenceShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs new file mode 100644 index 0000000000..59ced9697c --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Tenant Persistence", + Description = "Provides Oracle persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class OracleTenantPersistenceShellFeature + : EFCoreTenantManagementShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs new file mode 100644 index 0000000000..ad341f083d --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Alterations Persistence", + Description = "Provides PostgreSql persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class PostgreSqlAlterationsPersistenceShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs new file mode 100644 index 0000000000..5544db1910 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Identity Persistence", + Description = "Provides PostgreSql persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class PostgreSqlIdentityPersistenceShellFeature + : EFCoreIdentityPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs new file mode 100644 index 0000000000..53df538475 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Label Persistence", + Description = "Provides PostgreSql persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class PostgreSqlLabelPersistenceShellFeature + : EFCoreLabelPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..c79f66d215 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Management; + +/// +/// Configures the management feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Definition Persistence", + Description = "Provides PostgreSql persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class PostgreSqlWorkflowDefinitionPersistenceShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..4ec10667ad --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Management; + +/// +/// Configures the management feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Instance Persistence", + Description = "Provides PostgreSql persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class PostgreSqlWorkflowInstancePersistenceShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs new file mode 100644 index 0000000000..590a5c4d67 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures; + +/// +/// Configures the persistence feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Persistence", + Description = "Provides PostgreSql persistence for workflow definitions and runtime data", + DependsOn = ["PostgreSqlWorkflowDefinitionPersistence", "PostgreSqlWorkflowInstancePersistence", "PostgreSqlWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class PostgreSqlWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..21c5079c99 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Runtime Persistence", + Description = "Provides PostgreSql persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class PostgreSqlWorkflowRuntimePersistenceShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs new file mode 100644 index 0000000000..9f9ff77f3f --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Tenant Persistence", + Description = "Provides PostgreSql persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class PostgreSqlTenantPersistenceShellFeature + : EFCoreTenantManagementShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs new file mode 100644 index 0000000000..f47c2b1cbe --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Alterations Persistence", + Description = "Provides SqlServer persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class SqlServerAlterationsPersistenceShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs new file mode 100644 index 0000000000..bc1ee0e687 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Identity Persistence", + Description = "Provides SqlServer persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class SqlServerIdentityPersistenceShellFeature + : EFCoreIdentityPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs new file mode 100644 index 0000000000..ca3a528b91 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Label Persistence", + Description = "Provides SqlServer persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class SqlServerLabelPersistenceShellFeature + : EFCoreLabelPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..7635fe3ba6 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Management; + +/// +/// Configures the management feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Workflow Definition Persistence", + Description = "Provides SqlServer persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class SqlServerWorkflowDefinitionPersistenceShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..176c1bb04a --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Management; + +/// +/// Configures the management feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Workflow Instance Persistence", + Description = "Provides SqlServer persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class SqlServerWorkflowInstancePersistenceShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..65136a99cc --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Workflow Runtime Persistence", + Description = "Provides SqlServer persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class SqlServerWorkflowRuntimePersistenceShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs new file mode 100644 index 0000000000..16e6663266 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Tenant Persistence", + Description = "Provides SqlServer persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class SqlServerTenantPersistenceShellFeature + : EFCoreTenantManagementShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs new file mode 100644 index 0000000000..253e967ec3 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Alterations Persistence", + Description = "Provides Sqlite persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class SqliteAlterationsPersistenceShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs new file mode 100644 index 0000000000..13ac4baab9 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Identity Persistence", + Description = "Provides Sqlite persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class SqliteIdentityPersistenceShellFeature + : EFCoreIdentityPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs new file mode 100644 index 0000000000..c1150748c2 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Label Persistence", + Description = "Provides Sqlite persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class SqliteLabelPersistenceShellFeature + : EFCoreLabelPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..7cca81a699 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; + +/// +/// Configures the management feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Definition Persistence", + Description = "Provides Sqlite persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class SqliteWorkflowDefinitionPersistenceShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..6de74788b9 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; + +/// +/// Configures the management feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Instance Persistence", + Description = "Provides Sqlite persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class SqliteWorkflowInstancePersistenceShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..1632f5d966 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Runtime Persistence", + Description = "Provides Sqlite persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class SqliteWorkflowRuntimePersistenceShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowPersistenceShellFeature.cs new file mode 100644 index 0000000000..8e8e55f8fc --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowPersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures; + +/// +/// Configures the persistence feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Persistence", + Description = "Provides Sqlite persistence for workflow definitions and runtime data", + DependsOn = ["SqliteWorkflowDefinitionPersistence", "SqliteWorkflowInstancePersistence", "SqliteWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class SqliteWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs new file mode 100644 index 0000000000..eea341eec8 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Tenant Persistence", + Description = "Provides Sqlite persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class SqliteTenantPersistenceShellFeature + : EFCoreTenantManagementShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs new file mode 100644 index 0000000000..0a3aea1ab6 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs @@ -0,0 +1,23 @@ +using Elsa.Alterations.Core.Contracts; +using Elsa.Alterations.Core.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Alterations; + +/// +/// Base class for alterations persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreAlterationsPersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + + AddEntityStore(services); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs new file mode 100644 index 0000000000..a09f2658b0 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs @@ -0,0 +1,24 @@ +using Elsa.Identity.Contracts; +using Elsa.Identity.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Identity; + +/// +/// Base class for identity persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreIdentityPersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + AddEntityStore(services); + AddEntityStore(services); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs new file mode 100644 index 0000000000..20c9fa9370 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs @@ -0,0 +1,22 @@ +using Elsa.Labels.Contracts; +using Elsa.Labels.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Labels; + +/// +/// Base class for label persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreLabelPersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + AddEntityStore(services); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 0000000000..7155c603ec --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using Elsa.Workflows.Management; +using Elsa.Workflows.Management.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Management; + +/// +/// Base class for workflow definition persistence features. +/// This is not a standalone shell feature - use provider-specific features like SqliteWorkflowDefinitionPersistenceShellFeature. +/// +[UsedImplicitly] +public abstract class EFCoreWorkflowDefinitionPersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 0000000000..200c581fc4 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using Elsa.Workflows.Management; +using Elsa.Workflows.Management.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Management; + +/// +/// Base class for workflow instance persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreWorkflowInstancePersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 0000000000..1017ae80df --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,33 @@ +using Elsa.KeyValues.Contracts; +using Elsa.KeyValues.Entities; +using Elsa.Workflows.Runtime; +using Elsa.Workflows.Runtime.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Runtime; + +/// +/// Base class for workflow runtime persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreWorkflowRuntimePersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + AddEntityStore(services); + AddStore(services); + AddStore(services); + AddEntityStore(services); + AddEntityStore(services); + AddStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs new file mode 100644 index 0000000000..f646993940 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs @@ -0,0 +1,20 @@ +using Elsa.Common.Multitenancy; +using Elsa.Tenants; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Tenants; + +/// +/// Base class for tenant management persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreTenantManagementShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs b/src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs new file mode 100644 index 0000000000..7087c4d3bc --- /dev/null +++ b/src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs @@ -0,0 +1,47 @@ +using CShells.FastEndpoints.Features; +using CShells.Features; +using Elsa.Expressions.Options; +using Elsa.Extensions; +using Elsa.Resilience.Entities; +using Elsa.Resilience.Modifiers; +using Elsa.Resilience.Options; +using Elsa.Resilience.Recorders; +using Elsa.Resilience.Serialization; +using Elsa.Resilience.StrategySources; +using Elsa.Workflows; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Resilience.ShellFeatures; + +[ShellFeature] +public class ResilienceShellFeature : IFastEndpointsShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.AddTypeAlias>("RetryAttemptRecordList"); + }); + + services.AddOptions(); + + services + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton(VoidRetryAttemptRecorder.Instance) + .AddSingleton(VoidRetryAttemptReader.Instance) + .AddScoped() + .AddScoped() + .AddScoped() + .AddHandlersFrom(); + + // Register transient exception detection infrastructure + services + .AddSingleton() + .AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs b/src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs new file mode 100644 index 0000000000..0447e7569d --- /dev/null +++ b/src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs @@ -0,0 +1,27 @@ +using CShells.Features; +using Elsa.SasTokens.Contracts; +using JetBrains.Annotations; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.SasTokens.ShellFeatures; + +/// +/// Adds the SAS tokens feature to the workflow runtime. +/// +[ShellFeature( + DisplayName = "SAS Tokens", + Description = "Provides shared access signature token generation and validation")] +[UsedImplicitly] +public class SasTokensFeature : IShellFeature +{ + private Func TokenService { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + public void ConfigureServices(IServiceCollection services) + { + var builder = services.AddDataProtection(); + builder.SetApplicationName("Elsa Workflows"); + + services.AddScoped(TokenService); + } +} diff --git a/src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs b/src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs new file mode 100644 index 0000000000..7473a0b116 --- /dev/null +++ b/src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs @@ -0,0 +1,55 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Common.RecurringTasks; +using Elsa.Extensions; +using Elsa.Scheduling.Bookmarks; +using Elsa.Scheduling.Handlers; +using Elsa.Scheduling.HostedServices; +using Elsa.Scheduling.Services; +using Elsa.Scheduling.TriggerPayloadValidators; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Scheduling.ShellFeatures; + +/// +/// Provides scheduling features to the system. +/// +[ShellFeature( + DisplayName = "Scheduling", + Description = "Provides scheduling capabilities for workflows including cron and delay-based triggers", + DependsOn = ["SystemClock"])] +[UsedImplicitly] +public class SchedulingFeature : IShellFeature +{ + /// + /// Gets or sets the trigger scheduler factory. + /// + public Func WorkflowScheduler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// Gets or sets the CRON parser factory. + /// + public Func CronParser { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton() + .AddSingleton() + .AddSingleton(CronParser) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(WorkflowScheduler) + .AddBackgroundTask() + .AddHandlersFrom() + + //Trigger payload validators. + .AddTriggerPayloadValidator(); + } +} + diff --git a/src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs b/src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs new file mode 100644 index 0000000000..139ddf193f --- /dev/null +++ b/src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs @@ -0,0 +1,40 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Tenants.AspNetCore.Options; +using Elsa.Tenants.AspNetCore.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.AspNetCore.ShellFeatures; + +/// +/// Provides multi-tenant HTTP routing features. +/// +[ShellFeature( + DisplayName = "Multi-tenant HTTP Routing", + Description = "Provides multi-tenant HTTP routing capabilities for workflows")] +[UsedImplicitly] +public class MultitenantHttpRoutingFeature : IShellFeature +{ + /// + /// Gets or sets the tenant header name. + /// + public string TenantHeaderName { get; set; } = "X-Tenant-Id"; + + public void ConfigureServices(IServiceCollection services) + { + // Multitenancy HTTP options. + services.Configure(options => + { + options.TenantHeaderName = TenantHeaderName; + }); + + // Tenant resolvers. + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(); + } +} + diff --git a/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs new file mode 100644 index 0000000000..5ad6276085 --- /dev/null +++ b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs @@ -0,0 +1,21 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.ShellFeatures; + +/// +/// Enables tenant management endpoints. +/// +[ShellFeature( + DisplayName = "Tenant Management Endpoints", + Description = "Provides REST API endpoints for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class TenantManagementEndpointsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + // FastEndpoints are registered via assembly scanning + } +} diff --git a/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs new file mode 100644 index 0000000000..e6c1d1e167 --- /dev/null +++ b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs @@ -0,0 +1,30 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Extensions; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.ShellFeatures; + +/// +/// Enables tenant management capabilities. +/// +[ShellFeature( + DisplayName = "Tenant Management", + Description = "Provides tenant store and management capabilities")] +[UsedImplicitly] +public class TenantManagementFeature : IShellFeature +{ + /// + /// A factory that instantiates an . + /// + public Func TenantStoreFactory { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryStore() + .AddScoped(TenantStoreFactory); + } +} + diff --git a/src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs b/src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs new file mode 100644 index 0000000000..435d0ddf6c --- /dev/null +++ b/src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs @@ -0,0 +1,27 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Tenants.Mediator.Tasks; +using Elsa.Tenants.Options; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.ShellFeatures; + +/// +/// Configures multi-tenancy features. +/// +[ShellFeature( + DisplayName = "Tenants", + Description = "Provides multi-tenancy capabilities for workflows")] +[UsedImplicitly] +public class TenantsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddHostedService() + .AddScoped() + .AddScoped(); + } +} + diff --git a/src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs b/src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs new file mode 100644 index 0000000000..773df8e825 --- /dev/null +++ b/src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.WorkflowProviders.BlobStorage.Contracts; +using Elsa.WorkflowProviders.BlobStorage.ElsaScript.Handlers; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.WorkflowProviders.BlobStorage.ElsaScript.ShellFeatures; + +/// +/// A feature that enables ElsaScript support for the BlobStorage workflow provider. +/// +[ShellFeature( + DisplayName = "ElsaScript Blob Storage", + Description = "Provides ElsaScript format support for the BlobStorage workflow provider", + DependsOn = ["BlobStorage", "ElsaScript"])] +[UsedImplicitly] +public class ElsaScriptBlobStorageFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + // Register the ElsaScript format handler + services.AddScoped(); + } +} + diff --git a/src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs b/src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs new file mode 100644 index 0000000000..805f89aba5 --- /dev/null +++ b/src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs @@ -0,0 +1,50 @@ +using System.Reflection; +using CShells.Features; +using Elsa.WorkflowProviders.BlobStorage.Contracts; +using Elsa.WorkflowProviders.BlobStorage.Handlers; +using Elsa.WorkflowProviders.BlobStorage.Providers; +using Elsa.Workflows.Runtime; +using FluentStorage; +using FluentStorage.Blobs; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.WorkflowProviders.BlobStorage.ShellFeatures; + +/// +/// A feature that enables the FluentStorage workflow definition provider. +/// +[ShellFeature( + DisplayName = "Blob Storage Workflow Provider", + Description = "Provides workflow definitions from blob storage", + DependsOn = ["WorkflowManagement"])] +[UsedImplicitly] +public class BlobStorageFeature : IShellFeature +{ + /// + /// The blob storage to use. + /// + public Func BlobStorage { get; set; } = _ => StorageFactory.Blobs.DirectoryFiles(GetDefaultWorkflowsDirectory()); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(sp => new BlobStorageProvider(BlobStorage(sp))); + + // Register the JSON format handler (built-in support) + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + } + + /// + /// Gets the default workflows directory. + /// + public static string GetDefaultWorkflowsDirectory() + { + var entryAssemblyDir = Path.GetDirectoryName(Assembly.GetEntryAssembly()!.Location)!; + var directory = Path.Combine(entryAssemblyDir, "Workflows"); + return directory; + } +} + diff --git a/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj b/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj index 9311d394cf..f36f1fdcce 100644 --- a/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj +++ b/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj @@ -6,6 +6,10 @@ elsa module workflows api + + + + diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs index f0f1c0cf12..fd82297b98 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs @@ -9,16 +9,8 @@ namespace Elsa.Workflows.Api.Endpoints.Features.Get; /// Returns the specified installed feature. /// [PublicAPI] -internal class Get : ElsaEndpointWithoutRequest +internal class Get(IInstalledFeatureProvider installedFeatureProvider) : ElsaEndpointWithoutRequest { - private readonly IInstalledFeatureRegistry _installedFeatureRegistry; - - /// - public Get(IInstalledFeatureRegistry installedFeatureRegistry) - { - _installedFeatureRegistry = installedFeatureRegistry; - } - /// public override void Configure() { @@ -30,14 +22,14 @@ public override void Configure() public override async Task HandleAsync( CancellationToken cancellationToken) { var fullName = Route("fullName")!; - var descriptor = _installedFeatureRegistry.Find(fullName); + var descriptor = installedFeatureProvider.Find(fullName); if (descriptor == null) { await Send.NotFoundAsync(cancellationToken); return; } - + await Send.OkAsync(descriptor, cancellationToken); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs index dfa38c0583..b62efe712e 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs @@ -10,16 +10,8 @@ namespace Elsa.Workflows.Api.Endpoints.Features.List; /// Returns a list of installed features. /// [PublicAPI] -internal class List : ElsaEndpointWithoutRequest> +internal class List(IInstalledFeatureProvider installedFeatureProvider) : ElsaEndpointWithoutRequest> { - private readonly IInstalledFeatureRegistry _installedFeatureRegistry; - - /// - public List(IInstalledFeatureRegistry installedFeatureRegistry) - { - _installedFeatureRegistry = installedFeatureRegistry; - } - /// public override void Configure() { @@ -30,7 +22,7 @@ public override void Configure() /// public override Task> ExecuteAsync(CancellationToken cancellationToken) { - var descriptors = _installedFeatureRegistry.List().ToList(); + var descriptors = installedFeatureProvider.List().ToList(); var response = new ListResponse(descriptors); return Task.FromResult(response); diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs index a7afdcfe47..caaef86c99 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs @@ -32,7 +32,7 @@ public override async Task HandleAsync(CancellationToken cancellationToken) if (HttpContext.Request.ContentType?.Contains("application/json") ?? false) { using var reader = new StreamReader(HttpContext.Request.Body); - var body = await reader.ReadToEndAsync(); + var body = await reader.ReadToEndAsync(cancellationToken); if (!string.IsNullOrWhiteSpace(body)) { diff --git a/src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs b/src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs new file mode 100644 index 0000000000..9ec781fab2 --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs @@ -0,0 +1,24 @@ +using CShells.Features; +using Elsa.Workflows.Api.RealTime.Handlers; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Api.ShellFeatures; + +/// +/// Sets up a SignalR hub for receiving workflow events on the client. +/// +[ShellFeature( + DisplayName = "Real-Time Workflow Updates", + Description = "Provides real-time workflow updates via SignalR")] +[UsedImplicitly] +public class RealTimeWorkflowUpdatesFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSignalR(); + services.AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs b/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs new file mode 100644 index 0000000000..de155669f4 --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs @@ -0,0 +1,46 @@ +using CShells.FastEndpoints.Features; +using CShells.Features; +using Elsa.Extensions; +using Elsa.Workflows.Api.Constants; +using Elsa.Workflows.Api.Requirements; +using Elsa.Workflows.Api.Serialization; +using Elsa.Workflows.Api.Services; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Api.ShellFeatures; + +/// +/// Adds workflows API features with FastEndpoints support. +/// +/// +/// This feature implements to indicate that this assembly +/// contains FastEndpoints that should be automatically discovered and registered. +/// +[ShellFeature( + DisplayName = "Workflows API", + Description = "Provides REST API endpoints for workflow management", + DependsOn = +[ + "ElsaFastEndpoints", + "WorkflowInstances", + "WorkflowManagement", + "WorkflowRuntime", + "SasTokens" +])] +[UsedImplicitly] +public class WorkflowsApiFeature : IFastEndpointsShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSerializationOptionsConfigurator(); + services.AddScoped(); + services.AddScoped(); + services.Configure(options => + { + options.AddPolicy(AuthorizationPolicies.NotReadOnlyPolicy, policy => policy.AddRequirements(new NotReadOnlyRequirement())); + }); + services.AddScoped(); + } +} diff --git a/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj b/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj index e400062f4d..74eb3c854b 100644 --- a/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj +++ b/src/modules/Elsa.Workflows.Core/Elsa.Workflows.Core.csproj @@ -12,6 +12,7 @@ + diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs new file mode 100644 index 0000000000..c529df84d4 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs @@ -0,0 +1,16 @@ +using CShells.Features; +using Elsa.Extensions; +using Elsa.Workflows.CommitStates; +using Elsa.Workflows.CommitStates.Tasks; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.ShellFeatures; + +public class CommitStrategiesFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddStartupTask(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs new file mode 100644 index 0000000000..b7d9fdce30 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs @@ -0,0 +1,35 @@ +using CShells.Features; +using Elsa.Extensions; +using Elsa.Workflows.Activities.Flowchart.Options; +using Elsa.Workflows.Activities.Flowchart.Serialization; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.ShellFeatures; + +/// +/// Adds support for the Flowchart activity. +/// +[ShellFeature( + DisplayName = "Flowchart", + Description = "Adds support for the Flowchart activity")] +[UsedImplicitly] +public class FlowchartFeature : IShellFeature +{ + /// + /// A delegate to configure . + /// + public Action? FlowchartOptionsConfigurator { get; set; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddSerializationOptionsConfigurator(); + + // Register FlowchartOptions + services.AddOptions(); + + if (FlowchartOptionsConfigurator != null) + services.Configure(FlowchartOptionsConfigurator); + } +} + diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs new file mode 100644 index 0000000000..4c906c1617 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs @@ -0,0 +1,185 @@ +using CShells.Features; +using Elsa.Common; +using Elsa.Common.Serialization; +using Elsa.Common.ShellFeatures; +using Elsa.Expressions.ShellFeatures; +using Elsa.Extensions; +using Elsa.Workflows.ActivationValidators; +using Elsa.Workflows.Activities.Flowchart.Options; +using Elsa.Workflows.Activities.Flowchart.Serialization; +using Elsa.Workflows.Builders; +using Elsa.Workflows.CommitStates; +using Elsa.Workflows.IncidentStrategies; +using Elsa.Workflows.LogPersistence; +using Elsa.Workflows.LogPersistence.Strategies; +using Elsa.Workflows.Middleware.Activities; +using Elsa.Workflows.Middleware.Workflows; +using Elsa.Workflows.Pipelines.ActivityExecution; +using Elsa.Workflows.Pipelines.WorkflowExecution; +using Elsa.Workflows.PortResolvers; +using Elsa.Workflows.Serialization.Configurators; +using Elsa.Workflows.Serialization.Helpers; +using Elsa.Workflows.Serialization.Serializers; +using Elsa.Workflows.Services; +using Elsa.Workflows.UIHints.CheckList; +using Elsa.Workflows.UIHints.Dictionary; +using Elsa.Workflows.UIHints.Dropdown; +using Elsa.Workflows.UIHints.JsonEditor; +using Elsa.Workflows.UIHints.RadioList; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.ShellFeatures; + +[ShellFeature(DependsOn = +[ + "SystemClock", + "Expressions", + "Mediator", + "DefaultFormatters", + "Multitenancy", + "CommitStrategies" +])] +public class WorkflowsFeature : IShellFeature +{ + /// + /// A delegate to configure the . + /// + public Action WorkflowExecutionPipeline { get; set; } = builder => builder + .UseExceptionHandling() + .UseDefaultActivityScheduler(); + + /// + /// A delegate to configure the . + /// + public Action ActivityExecutionPipeline { get; set; } = builder => builder.UseDefaultActivityInvoker(); + + /// + /// A factory that instantiates a concrete . + /// + public Func StandardInStreamProvider { get; set; } = _ => new StandardInStreamProvider(Console.In); + + /// + /// A factory that instantiates a concrete . + /// + public Func StandardOutStreamProvider { get; set; } = _ => new StandardOutStreamProvider(Console.Out); + + /// + /// A factory that instantiates a concrete . + /// + public Func> WorkflowLoggerStateGenerator { get; set; } = sp => new WorkflowLoggerStateGenerator(); + + /// + /// A factory that instantiates a concrete . + /// + public Func> ActivityLoggerStateGenerator { get; set; } = sp => new ActivityLoggerStateGenerator(); + + public void ConfigureServices(IServiceCollection services) + { + services + // Core. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(sp => ActivatorUtilities.CreateInstance(sp)) + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddTransient() + .AddScoped(typeof(Func), sp => () => sp.GetRequiredService()) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Incident Strategies. + .AddTransient() + .AddTransient() + + // Pipelines. + .AddScoped(sp => new ActivityExecutionPipeline(sp, ActivityExecutionPipeline)) + .AddScoped(sp => new WorkflowExecutionPipeline(sp, WorkflowExecutionPipeline)) + + // Built-in activity services. + .AddScoped() + .AddScoped() + .AddSerializationOptionsConfigurator() + .AddSerializationOptionsConfigurator() + + // Domain event handlers. + .AddHandlersFrom() + + // Stream providers. + .AddScoped(StandardInStreamProvider) + .AddScoped(StandardOutStreamProvider) + + // Storage drivers. + .AddScoped() + .AddStorageDriver() + .AddStorageDriver() + .AddStorageDriver() + + // Serialization. + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + + // Instantiation strategies. + .AddScoped() + + // UI. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + + // Logger state generators. + .AddSingleton(WorkflowLoggerStateGenerator) + .AddSingleton(ActivityLoggerStateGenerator) + + // Log Persistence Strategies. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Logging + .AddLogging(); + + // Flowchart + services.AddSerializationOptionsConfigurator(); + + // Register FlowchartOptions + services.AddOptions(); + + // Overridable services + services.AddScoped(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs new file mode 100644 index 0000000000..6e42d111b8 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs @@ -0,0 +1,28 @@ +using CShells.Features; +using Elsa.Workflows.Management.Handlers.Notifications; +using Elsa.Workflows.Management.Services; +using Elsa.Workflows.Management.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Configures workflow definition caching. +/// +[ShellFeature( + DisplayName = "Caching Workflow Definitions", + Description = "Provides caching for workflow definitions")] +[UsedImplicitly] +public class CachingWorkflowDefinitionsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.Decorate(); + services.Decorate(); + services.AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs new file mode 100644 index 0000000000..a79a6ee1f1 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs @@ -0,0 +1,21 @@ +using CShells.Features; +using Elsa.Workflows.Management.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Configures workflow definition storage. +/// +[ShellFeature( + DisplayName = "Workflow Definitions", + Description = "Manages workflow definitions and their storage")] +[UsedImplicitly] +public class WorkflowDefinitionsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + } +} diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs new file mode 100644 index 0000000000..c9d072c369 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs @@ -0,0 +1,21 @@ +using CShells.Features; +using Elsa.Workflows.Management.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Enables storage of workflow instances. +/// +[ShellFeature( + DisplayName = "Workflow Instances", + Description = "Manages workflow execution instances and their persistent state")] +[UsedImplicitly] +public class WorkflowInstancesFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + } +} diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs new file mode 100644 index 0000000000..65b632402f --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs @@ -0,0 +1,163 @@ +using System.Dynamic; +using System.Text.Json; +using System.Text.Json.Nodes; +using CShells.Features; +using Elsa.Caching.Features; +using Elsa.Common.Features; +using Elsa.Expressions.Contracts; +using Elsa.Extensions; +using Elsa.Features.Attributes; +using Elsa.Workflows.Features; +using Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity; +using Elsa.Workflows.Management.Contracts; +using Elsa.Workflows.Management.Entities; +using Elsa.Workflows.Management.Features; +using Elsa.Workflows.Management.Handlers.Notifications; +using Elsa.Workflows.Management.Mappers; +using Elsa.Workflows.Management.Materializers; +using Elsa.Workflows.Management.Models; +using Elsa.Workflows.Management.Options; +using Elsa.Workflows.Management.Providers; +using Elsa.Workflows.Management.Services; +using Elsa.Workflows.Management.Stores; +using Elsa.Workflows.Serialization.Serializers; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Installs and configures the workflow management feature. +/// +[ShellFeature( + DisplayName = "Workflow Management", + Description = "Provides comprehensive workflow definition and instance management capabilities", + DependsOn = + [ + "StringCompression", + "Mediator", + "MemoryCache", + "SystemClock", + "Workflows", + "WorkflowDefinitions", + "WorkflowInstances" + ])] +[UsedImplicitly] +public class WorkflowManagementFeature : IShellFeature +{ + private const string PrimitivesCategory = "Primitives"; + private const string LookupsCategory = "Lookups"; + private const string DynamicCategory = "Dynamic"; + private const string DataCategory = "Data"; + private const string SystemCategory = "System"; + + /// + /// A set of variable types to make available to the system. + /// + public HashSet VariableDescriptors { get; } = + [ + new(typeof(object), PrimitivesCategory, "The root class for all object in the CLR System."), + new(typeof(string), PrimitivesCategory, "Represents a static string of characters."), + new(typeof(bool), PrimitivesCategory, "Represents a true or false value."), + new(typeof(int), PrimitivesCategory, "A 32 bit integer."), + new(typeof(long), PrimitivesCategory, "A 64 bit integer."), + new(typeof(float), PrimitivesCategory, "A 32 bit floating point number."), + new(typeof(double), PrimitivesCategory, "A 64 bit floating point number."), + new(typeof(decimal), PrimitivesCategory, "A decimal number."), + new(typeof(Guid), PrimitivesCategory, "Represents a Globally Unique Identifier."), + new(typeof(DateTime), PrimitivesCategory, "A value type that represents a date and time."), + new(typeof(DateTimeOffset), PrimitivesCategory, "A value type that consists of a DateTime and a time zone offset."), + new(typeof(TimeSpan), PrimitivesCategory, "Represents a duration of time."), + new(typeof(IDictionary), LookupsCategory, "A dictionary with string key and values."), + new(typeof(IDictionary), LookupsCategory, "A dictionary with string key and object values."), + new(typeof(ExpandoObject), DynamicCategory, "A dictionary that can be typed as dynamic to access members using dot notation."), + new(typeof(JsonElement), DynamicCategory, "A JSON element for reading a JSON structure."), + new(typeof(JsonNode), DynamicCategory, "A JSON node for reading and writing a JSON structure."), + new(typeof(JsonObject), DynamicCategory, "A JSON object for reading and writing a JSON structure."), + new(typeof(byte[]), DataCategory, "A byte array."), + new(typeof(Stream), DataCategory, "A stream.") + ]; + + + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryStore() + .AddMemoryStore() + .AddActivityProvider() + .AddActivityProvider() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddSerializationOptionsConfigurator() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddSingleton() + .Decorate() + .Decorate() + .AddNotificationHandler(); + + services + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + ; + + AddActivitiesFrom(); + AddActivitiesFrom(); + + + services.Configure(options => + { + foreach (var activityType in ActivityTypes.Distinct()) + options.ActivityTypes.Add(activityType); + + foreach (var descriptor in VariableDescriptors.DistinctBy(x => x.Type)) + options.VariableDescriptors.Add(descriptor); + }); + } + + /// + /// A set of activity types to make available to the system. + /// + private HashSet ActivityTypes { get; } = []; + + /// + /// Adds the specified activity types to the system. + /// + private WorkflowManagementFeature AddActivities(IEnumerable activityTypes) + { + ActivityTypes.AddRange(activityTypes); + return this; + } + + private WorkflowManagementFeature AddActivitiesFrom() + { + var activityTypes = typeof(TMarker).Assembly.GetExportedTypes() + .Where(x => typeof(IActivity).IsAssignableFrom(x) && x is { IsAbstract: false, IsInterface: false, IsGenericType: false }) + .ToList(); + return AddActivities(activityTypes); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs b/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs index 67a7be6e80..c7facf34d7 100644 --- a/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs +++ b/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs @@ -129,7 +129,7 @@ public async Task CountDistinctAsync(CancellationToken cancellationToken = } /// - public async Task GetIsNameUnique(string name, string? definitionId = default, CancellationToken cancellationToken = default) + public async Task GetIsNameUnique(string name, string? definitionId = null, CancellationToken cancellationToken = default) { var cacheKey = hasher.Hash(nameof(GetIsNameUnique), name, definitionId); return await GetOrCreateAsync(cacheKey, () => decoratedStore.GetIsNameUnique(name, definitionId, cancellationToken)); diff --git a/src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs new file mode 100644 index 0000000000..5f2a401893 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs @@ -0,0 +1,26 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Runtime.Distributed.ShellFeatures; + +/// +/// Installs and configures distributed workflow runtime features. +/// +[ShellFeature( + DisplayName = "Distributed Runtime", + Description = "Provides distributed workflow runtime capabilities", + DependsOn = ["WorkflowRuntime", "Resilience"])] +[UsedImplicitly] +public class DistributedRuntimeFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()) + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()); + } +} + diff --git a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs new file mode 100644 index 0000000000..90949395e3 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs @@ -0,0 +1,31 @@ +using CShells.Features; +using Elsa.Workflows.Runtime.Handlers; +using Elsa.Workflows.Runtime.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Runtime.ShellFeatures; + +/// +/// Installs and configures workflow runtime caching features. +/// +[ShellFeature( + DisplayName = "Caching Workflow Runtime", + Description = "Provides caching for workflow runtime operations", + DependsOn = ["MemoryCache"])] +[UsedImplicitly] +public class CachingWorkflowRuntimeFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + // Decorators. + .Decorate() + + // Handlers. + .AddNotificationHandler() + .AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs new file mode 100644 index 0000000000..0fdc8ae6ea --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs @@ -0,0 +1,274 @@ +using CShells.Features; +using Elsa.Common.RecurringTasks; +using Elsa.Extensions; +using Elsa.Mediator.Contracts; +using Elsa.Workflows.CommitStates; +using Elsa.Workflows.Management; +using Elsa.Workflows.Management.Contracts; +using Elsa.Workflows.Management.Services; +using Elsa.Workflows.Runtime.ActivationValidators; +using Elsa.Workflows.Runtime.Entities; +using Elsa.Workflows.Runtime.Handlers; +using Elsa.Workflows.Runtime.Options; +using Elsa.Workflows.Runtime.Providers; +using Elsa.Workflows.Runtime.Stores; +using Elsa.Workflows.Runtime.Tasks; +using Elsa.Workflows.Runtime.UIHints; +using Medallion.Threading; +using Medallion.Threading.FileSystem; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Runtime.ShellFeatures; + +/// +/// Installs and configures workflow runtime features. +/// +[ShellFeature( + DisplayName = "Workflow Runtime", + Description = "Provides workflow execution runtime and scheduling capabilities", + DependsOn = ["Workflows"])] +public class WorkflowRuntimeFeature : IShellFeature +{ + private IDictionary WorkflowDispatcherChannels { get; set; } = new Dictionary(); + + /// + /// A list of workflow builders configured during application startup. + /// + public IDictionary>> Workflows { get; set; } = new Dictionary>>(); + + /// + /// A factory that instantiates a concrete . + /// + public Func WorkflowRuntime { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowDispatcher { get; set; } = sp => + { + var decoratedService = ActivatorUtilities.CreateInstance(sp); + return ActivatorUtilities.CreateInstance(sp, decoratedService); + }; + + /// + /// A factory that instantiates an . + /// + public Func StimulusDispatcher { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowCancellationDispatcher { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates an . + /// + public Func BookmarkStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func BookmarkQueueStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func TriggerStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowExecutionLogStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func ActivityExecutionLogStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func DistributedLockProvider { get; set; } = _ => new FileDistributedSynchronizationProvider(new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "App_Data", "locks"))); + + /// + /// A factory that instantiates an . + /// + public Func RunTaskDispatcher { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func BackgroundActivityScheduler { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates a log record sink for an . + /// + public Func> ActivityExecutionLogSink { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates a log record sink for an . + /// + public Func> WorkflowExecutionLogSink { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func DispatchWorkflowCommandHandler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowResumer { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func BookmarkQueueWorker { get; set; } = sp => sp.GetRequiredService(); + + + public void ConfigureServices(IServiceCollection services) + { + // Options. + services.Configure(options => { options.Workflows = Workflows; }); + services.Configure(options => + { + options.Channels.AddRange(WorkflowDispatcherChannels.Values); + }); + services.Configure(options => + { + options.Schedule.ConfigureTask(TimeSpan.FromSeconds(10)); + }); + + services + // Core. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(WorkflowRuntime) + .AddScoped(WorkflowDispatcher) + .AddScoped(StimulusDispatcher) + .AddScoped(WorkflowCancellationDispatcher) + .AddScoped(RunTaskDispatcher) + .AddScoped(ActivityExecutionLogSink) + .AddScoped(WorkflowExecutionLogSink) + .AddSingleton(BackgroundActivityScheduler) + .AddSingleton() + .AddSingleton() + .AddScoped(BookmarkQueueWorker) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(WorkflowResumer) + .AddScoped() + .AddScoped(BookmarkQueueWorker) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped, WorkflowExecutionLogRecordExtractor>() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Deprecated services. + .AddScoped() + + // Stores. + .AddScoped(BookmarkStore) + .AddScoped(BookmarkQueueStore) + .AddScoped(TriggerStore) + .AddScoped(WorkflowExecutionLogStore) + .AddScoped(ActivityExecutionLogStore) + + // Lazy services. + .AddScoped>>(sp => sp.GetServices) + .AddScoped>>(sp => sp.GetServices) + + // Noop stores. + .AddScoped() + .AddScoped() + + // Memory stores. + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore() + + // Startup tasks, background tasks, and recurring tasks. + .AddStartupTask() + .AddRecurringTask(TimeSpan.FromMinutes(1)) + .AddRecurringTask(TimeSpan.FromSeconds(10)) + .AddRecurringTask(TimeSpan.FromMinutes(5)) // Same default as the workflow liveness threshold. + + // Distributed locking. + .AddSingleton(DistributedLockProvider) + + // Workflow definition providers. + .AddWorkflowDefinitionProvider() + + // UI property handlers. + .AddScoped() + + // Domain handlers. + .AddCommandHandler() + .AddCommandHandler() + .AddCommandHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + + // Workflow activation strategies. + .AddScoped() + .AddScoped() + .AddScoped() + ; + + services + // Decorators. + .Decorate() + + // Handlers. + .AddNotificationHandler() + .AddNotificationHandler(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa/Elsa.csproj b/src/modules/Elsa/Elsa.csproj index 08ccd9fb78..7dab0dff31 100644 --- a/src/modules/Elsa/Elsa.csproj +++ b/src/modules/Elsa/Elsa.csproj @@ -9,16 +9,16 @@ - - - + + + - - - - + + + + diff --git a/src/modules/Elsa/ShellFeatures/ElsaFeature.cs b/src/modules/Elsa/ShellFeatures/ElsaFeature.cs new file mode 100644 index 0000000000..815b06fe6e --- /dev/null +++ b/src/modules/Elsa/ShellFeatures/ElsaFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.ShellFeatures; + +[ShellFeature( + DisplayName = "Elsa Core", + Description = "Core Elsa workflow system functionality", + DependsOn = [ + "WorkflowManagement", + "WorkflowRuntime" +])] +public class ElsaFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + // Register the shell-based feature provider to bridge shell features to the Elsa feature API + services.AddSingleton(); + } +} \ No newline at end of file