Integrate shells (#7279)

* Add CShells package references and integrate shell features into the application

* Annotate shell features with `[ShellFeature]` attribute and update `TempElsaFeature` to use `ConfigureElsa`.

* Revert "Annotate shell features with `[ShellFeature]` attribute and update `TempElsaFeature` to use `ConfigureElsa`."

This reverts commit e8a875e8f9d14891c5403df50e8ac7e151db25dd.

* Introduce `Elsa.ModularServer.Web` with a minimal API, restructure shell feature configuration, and remove obsolete `CShells` dependency

* Update `CShells` package references, add Fody weaver, and configure new CShells package sources in `NuGet.Config`.

* Adds CShells integration to Elsa

Integrates CShells to enhance modularity and extensibility.

- Adds CShells related projects to the solution.
- Updates NuGet configuration to include CShells preview feed.
- Creates initial app settings for CShells configuration.
- Adds CShells.AspNetCore project reference.
- Implements CShells extensions in the program file.
- Creates shell feature classes in Elsa.Common.
- Creates shell feature classes in Elsa.Expressions.
- Creates shell feature classes in Elsa.Workflows.Core.
- Creates shell feature classes in Elsa.Workflows.Management.
- Creates shell feature classes in Elsa.Workflows.Runtime.
- Creates shell feature classes in Elsa module.

* Refactor CShells: enhance pipeline configuration and features

Consolidated updates to CShells including a new `ResolverPipelineBuilder` for customizable resolver strategy pipelines. Improved assembly scanning, error handling in web routing, and streamlined shell feature dependencies for better clarity and functionality.

* Add feature registration system and FastEndpoints integration

Introduced a feature registration infrastructure with `IInstalledFeatureProvider` and related implementations. Added shell-based feature configurations such as caching, SAS tokens, workflows management, and a FastEndpoints integration module to support dynamic API registration.

* Refactor shell routing and enhance global route handling

Refactored `ShellEndpointRouteBuilder` to simplify initialization and support a combined shell/global route prefix. Enhanced `ShellEndpointRegistrationHandler` to include global route prefix logic and improved feature discovery using pre-resolved descriptors. Updated `Program.cs` for consistent middleware setup.

* Refactor feature endpoints to use `IInstalledFeatureProvider` for improved dependency management and simplified implementation

* Add display names, descriptions, and dependency enhancements to shell features

Standardized `ShellFeature` attributes across `ElsaFeature`, `WorkflowRuntimeFeature`, and `WorkflowManagementFeature` by adding display names, descriptions, and improving dependency declarations. Updated `ElsaFeature` to register `IInstalledFeatureProvider` for feature bridging.

* Add FastEndpoints references and update package versions

Added project references to CShells.FastEndpoints and related projects in multiple csproj files. Updated FastEndpoints package versions in `Directory.Packages.props` for compatibility with .NET 8/9/10. Removed obsolete folder references from Elsa.Caching.csproj and refined the namespace in CShells.AspNetCore.Abstractions.

* Add Identity and DefaultAuthentication features to appsettings.json configuration

* Add project references for Elsa.Identity and CShells.FastEndpoints.Abstractions

* Add `Identity` and `DefaultAuthentication` shell features with enhanced authentication and authorization support

* Set default signing key in `IdentityTokenOptions` for identity configuration

* Add service exclusion infrastructure for shell-specific contexts

Introduce `IShellServiceExclusionProvider` and `IShellServiceExclusionRegistry` to manage excluded service types per-shell. Implement ASP.NET Core-specific providers for authentication and authorization to enable shell-specific configurations. Refactor `DefaultShellHost` to use the new exclusion registry for service inheritance filtering.

* Refactor CShells authentication and authorization APIs

Renamed and unified methods for shell authentication and authorization and added a new combined method `WithAuthenticationAndAuthorization`. Enhanced `AddShells` to automatically register a default configuration provider if none is specified. Updated usage in Elsa.ModularServer to utilize the new API.

* Add Elsa-specific FastEndpoints configurator and feature

Introduce `ElsaFastEndpointsConfigurator` to customize FastEndpoints serialization and value parsing for Elsa workflows. Register this functionality through the new `ElsaFastEndpointsFeature`, which integrates with the shell's dependency injection system using an `IFastEndpointsConfigurator` interface.

* Update Workflow API feature dependency to `ElsaFastEndpoints`

* Pass `cancellationToken` to `ReadToEndAsync` in `PostEndpoint` for improved request handling.

* Add project references for CShells.AspNetCore and CShells.FastEndpoints.Abstractions

* Update CShells package versions to `0.0.6-preview.30` and add `CShells.FastEndpoints.Abstractions`

* Configures shell routing and features

Enables path routing for shells to allow proper routing within each shell.

Configures the ElsaFastEndpoints feature to depend on the FastEndpoints feature.
This ensures that FastEndpoints is properly configured before Elsa's FastEndpoints configurations are applied.

Registers activity types within the WorkflowManagementFeature.
This ensures activities are available for workflow construction and execution.

* Add shell lifecycle management and notification handlers

Introduced interfaces and handlers for shell activation (`IShellActivatedHandler`) and deactivation (`IShellDeactivatingHandler`) to manage shell lifecycles. Added `ShellStartupHostedService` to coordinate shell activation on startup and deactivation on shutdown. Updated notification system to support new shell lifecycle events and renamed existing notification records for consistency.

* Introduces EF Core persistence layer

Adds base classes and implementations for EF Core persistence, including database provider configuration and shell feature integration.

This change introduces a generic approach to configuring EF Core persistence for various Elsa modules, promoting code reuse and simplifying the process of supporting different database providers.

It includes:

- Base classes for database provider configurators and shell features.
- Implementations for Sqlite, SQL Server, MySql, PostgreSql, and Oracle.
- Shell features for Alterations, Identity, Labels, Management (Workflow Definitions and Instances), Runtime, and Tenants modules.

* Add comprehensive feature configuration validation system

Introduce a feature configuration system with support for binding, auto-configuration, and validation using DataAnnotations, FluentValidation, and composite patterns. Includes new validators, binding logic, and extensions to simplify configuration tasks while ensuring robustness and flexibility.

* Add persistence shell features for MySql, Oracle, PostgreSql, and Sqlite

Introduced new shell features to configure MySql, Oracle, PostgreSql, and Sqlite persistence for workflow definitions and runtime data. Updated `appsettings.json` to replace individual Sqlite features with a unified `SqliteWorkflowPersistence`. Made minor code cleanup in `FastEndpointsFeature`.

* Configure shell features for persistence

Added `IServiceCollection` configuration for MySql, Oracle, PostgreSql, and Sqlite shell features to set up persistence services.

* Remove DatabaseProviderConfigurators and refactor persistence shell features

Deleted DatabaseProviderConfigurator classes and restructured persistence shell features by integrating direct configuration logic for MySql, Oracle, PostgreSql, Sqlite, and SqlServer. Simplified configuration by inheriting from abstract shell feature base classes and removed redundant code.

* Correct IWorkflowDefinitionPublisher registration to use WorkflowDefinitionPublisher implementation

* Add resilience feature and scoped services configuration for Sqlite persistence

- Integrated `Microsoft.Extensions.DependencyInjection` to shell features for Sqlite persistence.
- Updated `appsettings.json` and project references to include a new 'Resilience' feature.
- Changed `ICommitStateHandler` service registration in `WorkflowRuntimeFeature` to use an implementation.

* Add `ResilienceShellFeature` for configuring resilience strategies

- Implemented new `ResilienceShellFeature` class to manage services related to resilience features.
- Added scoped and singleton service registrations for resilience strategies, exception detection, and activity invocation.
- Configured expression options for resilience handling in workflows.

* Add new shell features: Alterations, Blob Storage, Caching, Clustering, CSharp, Distributed Runtime, ElsaScript, Flowchart, HTTP, JavaScript, Key-Value, and Labels

* Switch project references to package references for CShells libraries and update to version 0.0.7.

* Potential fix for pull request finding 'Call to 'System.IO.Path.Combine' may silently drop its earlier arguments'

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>

* Fix shell feature discovery and remove duplicate service registrations (#7285)

* Initial plan

* Address PR review comments: Add ShellFeature attributes, fix duplicates, and improve security

Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>

* Update dependencies and fix scoped services registration in `WorkflowRuntimeFeature`

* Fix null reference and typo in shell feature provider (#7286)

* Initial plan

* Fix null StartupType guard in Find() and typo in variable descriptor

Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>

---------

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>

---------

Co-authored-by: Copilot Autofix powered by AI <223894421+github-code-quality[bot]@users.noreply.github.com>
Co-authored-by: Copilot <198982749+Copilot@users.noreply.github.com>
Co-authored-by: sfmskywalker <938393+sfmskywalker@users.noreply.github.com>
This commit is contained in:
Sipke Schoorstra 2026-02-12 20:30:02 +01:00 committed by GitHub
parent 7377103817
commit c60e63c3c6
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
119 changed files with 4134 additions and 73 deletions

View file

@ -5,6 +5,9 @@
</PropertyGroup>
<!-- Conditional package versions for .NET 8/9 compatibility -->
<ItemGroup Condition="'$(TargetFramework)' == 'net8.0' or '$(TargetFramework)' == 'net9.0'">
<PackageVersion Include="FastEndpoints" Version="7.0.0"/>
<PackageVersion Include="FastEndpoints.Security" Version="7.0.0"/>
<PackageVersion Include="FastEndpoints.Swagger" Version="7.0.0"/>
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="9.0.11"/>
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="9.0.11"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="9.0.11"/>
@ -31,18 +34,21 @@
<PackageVersion Include="Microsoft.Extensions.Http" Version="9.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="9.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="9.10.0"/>
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Resilience" Version="9.10.0"/>
<PackageVersion Include="Microsoft.Extensions.Logging" Version="9.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="9.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="9.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Options" Version="9.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="9.0.11"/>
<PackageVersion Include="Microsoft.Extensions.Resilience" Version="9.10.0"/>
<PackageVersion Include="Npgsql.EntityFrameworkCore.PostgreSQL" Version="9.0.4"/>
<PackageVersion Include="Oracle.EntityFrameworkCore" Version="9.23.26000"/>
<PackageVersion Include="Scrutor" Version="6.1.0"/>
</ItemGroup>
<!-- .NET 10 specific package versions -->
<ItemGroup Condition="'$(TargetFramework)' == 'net10.0'">
<PackageVersion Include="FastEndpoints" Version="7.2.0"/>
<PackageVersion Include="FastEndpoints.Security" Version="7.2.0"/>
<PackageVersion Include="FastEndpoints.Swagger" Version="7.2.0"/>
<PackageVersion Include="Microsoft.AspNetCore.Authorization" Version="10.0.1"/>
<PackageVersion Include="Microsoft.AspNetCore.Components" Version="10.0.1"/>
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.1"/>
@ -94,14 +100,17 @@
<PackageVersion Include="coverlet.collector" Version="6.0.4" PrivateAssets="All"/>
<PackageVersion Include="coverlet.msbuild" Version="6.0.4" PrivateAssets="All"/>
<PackageVersion Include="Cronos" Version="0.11.1"/>
<PackageVersion Include="CShells" Version="0.0.7"/>
<PackageVersion Include="CShells.Abstractions" Version="0.0.7"/>
<PackageVersion Include="CShells.AspNetCore" Version="0.0.7"/>
<PackageVersion Include="CShells.AspNetCore.Abstractions" Version="0.0.7"/>
<PackageVersion Include="CShells.FastEndpoints" Version="0.0.7"/>
<PackageVersion Include="CShells.FastEndpoints.Abstractions" Version="0.0.7"/>
<PackageVersion Include="Datadog.Trace.Bundle" Version="3.32.0"/>
<PackageVersion Include="DistributedLock" Version="2.7.1"/>
<PackageVersion Include="DistributedLock.Core" Version="1.0.8"/>
<PackageVersion Include="DistributedLock.FileSystem" Version="1.0.3"/>
<PackageVersion Include="Elastic.Clients.Elasticsearch" Version="9.2.2"/>
<PackageVersion Include="FastEndpoints" Version="7.1.1"/>
<PackageVersion Include="FastEndpoints.Security" Version="7.1.1"/>
<PackageVersion Include="FastEndpoints.Swagger" Version="7.1.1"/>
<PackageVersion Include="FluentStorage" Version="6.0.0"/>
<PackageVersion Include="FluentStorage.Azure.Blobs" Version="6.0.0"/>
<PackageVersion Include="Fluid.Core" Version="2.31.0"/>

View file

@ -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}

View file

@ -4,10 +4,15 @@
<packageSources>
<clear />
<add key="NuGet official package source" value="https://api.nuget.org/v3/index.json" />
<add key="CShells Preview" value="https://f.feedz.io/sfmskywalker/cshells/nuget/index.json" />
</packageSources>
<packageSourceMapping>
<packageSource key="NuGet official package source">
<package pattern="*" />
</packageSource>
<packageSource key="CShells Preview">
<package pattern="CShells" />
<package pattern="CShells.*" />
</packageSource>
</packageSourceMapping>
</configuration>

View file

@ -0,0 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<ItemGroup>
<PackageReference Include="CShells.AspNetCore"/>
<PackageReference Include="CShells.FastEndpoints"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\modules\Elsa.Identity\Elsa.Identity.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Persistence.EFCore.Sqlite\Elsa.Persistence.EFCore.Sqlite.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Resilience\Elsa.Resilience.csproj" />
<ProjectReference Include="..\..\modules\Elsa.Workflows.Api\Elsa.Workflows.Api.csproj" />
<ProjectReference Include="..\..\modules\Elsa\Elsa.csproj" />
</ItemGroup>
</Project>

View file

@ -0,0 +1,3 @@
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait />
</Weavers>

View file

@ -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();

View file

@ -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"
}
}
}
}

View file

@ -0,0 +1,8 @@
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
}
}

View file

@ -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"
}
}

View file

@ -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"
]
}
]
}
}

View file

@ -42,7 +42,6 @@ var configuration = builder.Configuration;
var identitySection = configuration.GetSection("Identity");
var identityTokenSection = identitySection.GetSection("Tokens");
// Add Elsa services.
services
.AddElsa(elsa =>
{

View file

@ -9,6 +9,8 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CShells.AspNetCore.Abstractions" />
<PackageReference Include="CShells.FastEndpoints.Abstractions" />
<PackageReference Include="FastEndpoints" />
<PackageReference Include="FastEndpoints.Security" />
<PackageReference Include="FastEndpoints.Swagger" />

View file

@ -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;
/// <summary>
/// Configures FastEndpoints with Elsa-specific serialization options.
/// Uses the same serialization settings as <see cref="Elsa.Extensions.WebApplicationExtensions.UseWorkflowsApi"/>.
/// </summary>
[UsedImplicitly]
public class ElsaFastEndpointsConfigurator : IFastEndpointsConfigurator
{
/// <inheritdoc />
public void Configure(Config config)
{
config.Serializer.RequestDeserializer = DeserializeRequestAsync;
config.Serializer.ResponseSerializer = SerializeResponseAsync;
config.Binding.ValueParserFor<DateTimeOffset>(s =>
new(DateTimeOffset.TryParse(s.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result), result));
}
private static ValueTask<object?> DeserializeRequestAsync(HttpRequest httpRequest, Type modelType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken)
{
var serializer = httpRequest.HttpContext.RequestServices.GetRequiredService<IApiSerializer>();
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<IApiSerializer>();
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);
}
}

View file

@ -0,0 +1,22 @@
using CShells.FastEndpoints.Contracts;
using CShells.Features;
using Elsa.FastEndpointConfigurators;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.ShellFeatures;
/// <summary>
/// Registers the Elsa-specific FastEndpoints configurator.
/// This feature should be enabled alongside the CShells FastEndpointsFeature to apply Elsa's serialization settings.
/// </summary>
[ShellFeature(DependsOn = ["FastEndpoints"])]
[UsedImplicitly]
public class ElsaFastEndpointsFeature : IShellFeature
{
/// <inheritdoc />
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IFastEndpointsConfigurator, ElsaFastEndpointsConfigurator>();
}
}

View file

@ -0,0 +1,28 @@
using Elsa.Features.Models;
namespace Elsa.Features.Contracts;
/// <summary>
/// Provides read-only access to installed features.
/// </summary>
/// <remarks>
/// This interface is used to query the list of features that are installed and available in the application.
/// Unlike <see cref="IInstalledFeatureRegistry"/>, 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.
/// </remarks>
public interface IInstalledFeatureProvider
{
/// <summary>
/// Gets all installed features.
/// </summary>
/// <returns>All installed features.</returns>
IEnumerable<FeatureDescriptor> List();
/// <summary>
/// Finds a feature descriptor by its full name.
/// </summary>
/// <param name="fullName">The full name of the feature.</param>
/// <returns>The feature descriptor or null if not found.</returns>
FeatureDescriptor? Find(string fullName);
}

View file

@ -8,6 +8,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CShells.Abstractions"/>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" />
</ItemGroup>

View file

@ -127,6 +127,7 @@ public class Module : IModule
}
Services.AddSingleton<IInstalledFeatureRegistry>(registry);
Services.AddSingleton<IInstalledFeatureProvider>(sp => new InstalledFeatureProvider(sp.GetRequiredService<IInstalledFeatureRegistry>()));
}
private IEnumerable<IFeature> ExcludeFeaturesWithMissingDependencies(IEnumerable<IFeature> features)

View file

@ -0,0 +1,30 @@
using Elsa.Features.Contracts;
using Elsa.Features.Models;
namespace Elsa.Features.Services;
/// <summary>
/// Legacy implementation of <see cref="IInstalledFeatureProvider"/> that delegates to <see cref="IInstalledFeatureRegistry"/>.
/// </summary>
/// <remarks>
/// This implementation is used when features are manually registered using the legacy feature system.
/// For shell-based feature discovery, use <c>ShellInstalledFeatureProvider</c> instead.
/// </remarks>
public class InstalledFeatureProvider : IInstalledFeatureProvider
{
private readonly IInstalledFeatureRegistry _registry;
/// <summary>
/// Initializes a new instance of the <see cref="InstalledFeatureProvider"/> class.
/// </summary>
public InstalledFeatureProvider(IInstalledFeatureRegistry registry)
{
_registry = registry;
}
/// <inheritdoc />
public IEnumerable<FeatureDescriptor> List() => _registry.List();
/// <inheritdoc />
public FeatureDescriptor? Find(string fullName) => _registry.Find(fullName);
}

View file

@ -0,0 +1,70 @@
using CShells.Features;
using Elsa.Features.Contracts;
using Elsa.Features.Models;
namespace Elsa.Features.Services;
/// <summary>
/// Shell-based implementation of <see cref="IInstalledFeatureProvider"/> that reads from CShells feature descriptors.
/// </summary>
/// <remarks>
/// This implementation automatically discovers features from the shell's feature descriptors,
/// mapping them to Elsa's <see cref="FeatureDescriptor"/> model. Features are read from the
/// shell's DI container where they were registered during shell initialization.
/// </remarks>
public class ShellInstalledFeatureProvider : IInstalledFeatureProvider
{
private readonly IEnumerable<ShellFeatureDescriptor> _shellFeatures;
/// <summary>
/// Initializes a new instance of the <see cref="ShellInstalledFeatureProvider"/> class.
/// </summary>
public ShellInstalledFeatureProvider(IEnumerable<ShellFeatureDescriptor> shellFeatures)
{
_shellFeatures = shellFeatures;
}
/// <inheritdoc />
public IEnumerable<FeatureDescriptor> List()
{
return _shellFeatures
.Where(sf => sf.StartupType != null)
.Select(MapToElsaFeatureDescriptor);
}
/// <inheritdoc />
public FeatureDescriptor? Find(string fullName)
{
var shellFeature = _shellFeatures
.Where(sf => sf.StartupType != null)
.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}";
}
}

View file

@ -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;
/// <summary>
/// Adds the Elsa alterations services.
/// </summary>
[ShellFeature(
DisplayName = "Alterations",
Description = "Provides workflow alteration capabilities for modifying running workflow instances")]
[UsedImplicitly]
public class AlterationsFeature : IShellFeature
{
/// <summary>
/// Gets or sets the factory for the alteration plan store.
/// </summary>
public Func<IServiceProvider, IAlterationPlanStore> AlterationPlanStoreFactory { get; set; } = sp => sp.GetRequiredService<MemoryAlterationPlanStore>();
/// <summary>
/// Gets or sets the factory for the alteration job store.
/// </summary>
public Func<IServiceProvider, IAlterationJobStore> AlterationJobStoreFactory { get; set; } = sp => sp.GetRequiredService<MemoryAlterationJobStore>();
/// <summary>
/// Gets or sets the factory for the alteration job dispatcher.
/// </summary>
public Func<IServiceProvider, IAlterationJobDispatcher> AlterationJobDispatcherFactory { get; set; } = sp => sp.GetRequiredService<BackgroundAlterationJobDispatcher>();
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IAlterationPlanManager, AlterationPlanManager>();
services.AddAlterations();
services.AddAlterationsCore();
services.AddScoped<BackgroundAlterationJobDispatcher>();
services.AddScoped<IAlterationPlanScheduler, DefaultAlterationPlanScheduler>();
services.AddScoped<IAlterationJobRunner, DefaultAlterationJobRunner>();
services.AddScoped<IAlterationRunner, DefaultAlterationRunner>();
services.AddMemoryStore<AlterationPlan, MemoryAlterationPlanStore>();
services.AddMemoryStore<AlterationJob, MemoryAlterationJobStore>();
services.AddScoped(AlterationPlanStoreFactory);
services.AddScoped(AlterationJobStoreFactory);
services.AddScoped(AlterationJobDispatcherFactory);
}
}

View file

@ -0,0 +1,23 @@
using CShells.Features;
using Elsa.Caching.Services;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Caching.ShellFeatures;
/// <summary>
/// Configures the MemoryCache.
/// </summary>
[ShellFeature]
[UsedImplicitly]
public class MemoryCacheFeature : IShellFeature
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMemoryCache()
.AddSingleton<ICacheManager, CacheManager>()
.AddSingleton<IChangeTokenSignalInvoker, ChangeTokenSignalInvoker>()
.AddSingleton<IChangeTokenSignaler, ChangeTokenSignaler>();
}
}

View file

@ -12,43 +12,46 @@ namespace Elsa.Extensions;
/// </summary>
public static class DependencyInjectionExtensions
{
/// <summary>
/// Adds a memory store for the specified entity.
/// </summary>
public static IServiceCollection AddMemoryStore<TEntity, TStore>(this IServiceCollection services) where TStore : class
extension(IServiceCollection services)
{
services.TryAddSingleton<MemoryStore<TEntity>>();
services.TryAddScoped<TStore>();
return services;
}
/// <summary>
/// Adds a memory store for the specified entity.
/// </summary>
public IServiceCollection AddMemoryStore<TEntity, TStore>() where TStore : class
{
services.TryAddSingleton<MemoryStore<TEntity>>();
services.TryAddScoped<TStore>();
return services;
}
/// <summary>
/// Adds a serialization options configurator for the specified type.
/// </summary>
public static IServiceCollection AddSerializationOptionsConfigurator<T>(this IServiceCollection services) where T : class, ISerializationOptionsConfigurator
{
services.AddSingleton<ISerializationOptionsConfigurator, T>();
return services;
}
public static IServiceCollection AddStartupTask<T>(this IServiceCollection services) where T : class, IStartupTask
{
return services.AddScoped<IStartupTask, T>();
}
public static IServiceCollection AddBackgroundTask<T>(this IServiceCollection services) where T : class, IBackgroundTask
{
return services.AddScoped<IBackgroundTask, T>();
}
public static IServiceCollection AddRecurringTask<T>(this IServiceCollection services) where T : class, IRecurringTask
{
return services.AddScoped<IRecurringTask, T>();
}
public static IServiceCollection AddRecurringTask<T>(this IServiceCollection services, TimeSpan interval) where T : class, IRecurringTask
{
services.Configure<RecurringTaskOptions>(options => options.Schedule.ConfigureTask<T>(interval));
return services.AddRecurringTask<T>();
/// <summary>
/// Adds a serialization options configurator for the specified type.
/// </summary>
public IServiceCollection AddSerializationOptionsConfigurator<T>() where T : class, ISerializationOptionsConfigurator
{
services.AddSingleton<ISerializationOptionsConfigurator, T>();
return services;
}
public IServiceCollection AddStartupTask<T>() where T : class, IStartupTask
{
return services.AddScoped<IStartupTask, T>();
}
public IServiceCollection AddBackgroundTask<T>() where T : class, IBackgroundTask
{
return services.AddScoped<IBackgroundTask, T>();
}
public IServiceCollection AddRecurringTask<T>() where T : class, IRecurringTask
{
return services.AddScoped<IRecurringTask, T>();
}
public IServiceCollection AddRecurringTask<T>(TimeSpan interval) where T : class, IRecurringTask
{
services.Configure<RecurringTaskOptions>(options => options.Schedule.ConfigureTask<T>(interval));
return services.AddRecurringTask<T>();
}
}
}

View file

@ -0,0 +1,17 @@
using System.ComponentModel;
using CShells.Features;
using Elsa.Common.Serialization;
using Elsa.Common.Services;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Common.ShellFeatures;
[ShellFeature("DefaultFormatters")]
public class DefaultFormattersFeature : IShellFeature
{
public void ConfigureServices(IServiceCollection services)
{
TypeDescriptor.AddAttributes(typeof(Type), new TypeConverterAttribute(typeof(TypeTypeConverter)));
services.AddSingleton<IFormatter, JsonFormatter>();
}
}

View file

@ -0,0 +1,18 @@
using CShells.Features;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Common.ShellFeatures;
/// <summary>
/// Adds and configures the Mediator feature.
/// </summary>
[ShellFeature("Mediator")]
public class MediatorFeature : IShellFeature
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddMediator()
.AddMediatorHostedServices();
}
}

View file

@ -0,0 +1,42 @@
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;
[ShellFeature("Multitenancy")]
public class MultitenancyFeature : IShellFeature
{
private readonly Func<IServiceProvider, ITenantsProvider> _tenantsProviderFactory = sp => sp.GetRequiredService<DefaultTenantsProvider>();
public void ConfigureServices(IServiceCollection services)
{
services
.AddSingleton<ITenantScopeFactory, DefaultTenantScopeFactory>()
.AddSingleton<ITenantAccessor, DefaultTenantAccessor>()
.AddSingleton<ITenantFinder, DefaultTenantFinder>()
.AddSingleton<ITenantService, DefaultTenantService>()
// TenantTaskManager handles all task lifecycle in the correct order
.AddSingleton<TenantTaskManager>()
.AddSingleton<ITenantActivatedEvent>(sp => sp.GetRequiredService<TenantTaskManager>())
.AddSingleton<ITenantDeactivatedEvent>(sp => sp.GetRequiredService<TenantTaskManager>())
.AddSingleton<RecurringTaskScheduleManager>()
.AddSingleton<TenantEventsManager>()
.AddScoped<DefaultTenantsProvider>()
.AddScoped<DefaultTenantResolver>()
.AddScoped<ITaskExecutor, TaskExecutor>()
.AddScoped<IBackgroundTaskStarter, TaskExecutor>()
.AddScoped(_tenantsProviderFactory)
.AddSingleton<IShellActivatedHandler, ActivateShellTenants>()
.AddSingleton<IShellDeactivatingHandler, ActivateShellTenants>()
;
}
}

View file

@ -0,0 +1,21 @@
using CShells.Features;
using Elsa.Common.Codecs;
using Elsa.Common.Services;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Common.ShellFeatures;
[UsedImplicitly]
[ShellFeature("StringCompression")]
public class StringCompressionFeature : IShellFeature
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddSingleton<ICompressionCodecResolver, CompressionCodecResolver>()
.AddSingleton<ICompressionCodec, None>()
.AddSingleton<ICompressionCodec, GZip>()
.AddSingleton<ICompressionCodec, Zstd>();
}
}

View file

@ -0,0 +1,17 @@
using CShells.Features;
using Elsa.Common.Services;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Common.ShellFeatures;
/// <summary>
/// Configures the system clock.
/// </summary>
[ShellFeature("SystemClock")]
public class SystemClockFeature : IShellFeature
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<ISystemClock, SystemClock>();
}
}

View file

@ -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);
}
}

View file

@ -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;
/// <summary>
/// Feature for ElsaScript DSL support.
/// </summary>
[ShellFeature(
DisplayName = "ElsaScript DSL",
Description = "Provides ElsaScript DSL support for defining workflows")]
[UsedImplicitly]
public class ElsaScriptFeature : IShellFeature
{
public void ConfigureServices(IServiceCollection services)
{
services.AddSingleton<IElsaScriptParser, ElsaScriptParser>();
services.AddScoped<IElsaScriptCompiler, ElsaScriptCompiler>();
services.AddScoped<IWorkflowMaterializer, ElsaScriptWorkflowMaterializer>();
}
}

View file

@ -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;
/// <summary>
/// Installs C# integration.
/// </summary>
[ShellFeature(
DisplayName = "C# Expressions",
Description = "Provides C# expression evaluation capabilities for workflows",
DependsOn = ["Mediator", "Expressions", "MemoryCache"])]
[UsedImplicitly]
public class CSharpFeature : IShellFeature
{
/// <summary>
/// Configures the <see cref="CSharpOptions"/>.
/// </summary>
public Action<CSharpOptions> CSharpOptions { get; set; } = _ => { };
public void ConfigureServices(IServiceCollection services)
{
services.Configure(CSharpOptions);
// C# services.
services
.AddExpressionDescriptorProvider<CSharpExpressionDescriptorProvider>()
.AddScoped<ICSharpEvaluator, CSharpEvaluator>();
// Handlers.
services.AddNotificationHandlersFrom<CSharpFeature>();
// UI property handlers.
services.AddScoped<IPropertyUIHandler, RunCSharpOptionsProvider>();
}
}

View file

@ -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;
/// <summary>
/// Base class for JavaScript library shell features.
/// </summary>
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<JintOptions>(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);
});
});
}
}
/// <summary>
/// Adds Lodash library support to JavaScript expressions.
/// </summary>
[ShellFeature(
DisplayName = "Lodash JavaScript Library",
Description = "Provides Lodash utility library for JavaScript expressions",
DependsOn = ["JavaScript"])]
[UsedImplicitly]
public class LodashFeature : ScriptModuleShellFeatureBase
{
public LodashFeature() : base("lodash") { }
}
/// <summary>
/// Adds Lodash FP library support to JavaScript expressions.
/// </summary>
[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") { }
}
/// <summary>
/// Adds Moment.js library support to JavaScript expressions.
/// </summary>
[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") { }
}

View file

@ -0,0 +1,70 @@
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;
/// <summary>
/// Installs JavaScript integration.
/// </summary>
[ShellFeature(
DisplayName = "JavaScript Expressions",
Description = "Provides JavaScript expression evaluation capabilities for workflows",
DependsOn = ["Mediator", "Expressions", "MemoryCache"])]
[UsedImplicitly]
public class JavaScriptFeature : IShellFeature
{
/// <summary>
/// Configures the Jint options.
/// </summary>
public Action<JintOptions> JintOptions { get; set; } = _ => { };
public void ConfigureServices(IServiceCollection services)
{
services.Configure(JintOptions);
// JavaScript services.
services
.AddScoped<IJavaScriptEvaluator, JintJavaScriptEvaluator>()
.AddExpressionDescriptorProvider<JavaScriptExpressionDescriptorProvider>();
// Type definition services.
services
.AddScoped<ITypeDefinitionService, TypeDefinitionService>()
.AddScoped<ITypeDescriber, TypeDescriber>()
.AddScoped<ITypeDefinitionDocumentRenderer, TypeDefinitionDocumentRenderer>()
.AddSingleton<ITypeAliasRegistry, TypeAliasRegistry>()
.AddFunctionDefinitionProvider<CommonFunctionsDefinitionProvider>()
.AddFunctionDefinitionProvider<ActivityOutputFunctionsDefinitionProvider>()
.AddFunctionDefinitionProvider<RunJavaScriptFunctionsDefinitionProvider>()
.AddTypeDefinitionProvider<CommonTypeDefinitionProvider>()
.AddTypeDefinitionProvider<VariableTypeDefinitionProvider>()
.AddTypeDefinitionProvider<WorkflowVariablesTypeDefinitionProvider>()
.AddVariableDefinitionProvider<WorkflowVariablesVariableProvider>();
// Handlers.
services.AddNotificationHandlersFrom<JavaScriptFeature>();
// Type Script definitions.
services.AddFunctionDefinitionProvider<InputFunctionsDefinitionProvider>();
// UI property handlers.
services.AddScoped<IPropertyUIHandler, RunJavaScriptOptionsProvider>();
// Hosted services.
services.AddHostedService<RegisterVariableTypesWithJavaScriptHostedService>();
}
}

View file

@ -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;
/// <summary>
/// Configures Liquid functionality.
/// </summary>
[ShellFeature(
DisplayName = "Liquid Expressions",
Description = "Provides Liquid template expression evaluation capabilities for workflows",
DependsOn = ["MemoryCache", "Mediator", "Expressions"])]
[UsedImplicitly]
public class LiquidFeature : IShellFeature
{
/// <summary>
/// Configures the Fluid options.
/// </summary>
public Action<FluidOptions> FluidOptions { get; set; } = options =>
{
options.ConfigureFilters = context => context.Options.Filters
.WithArrayFilters()
.WithStringFilters()
.WithNumberFilters()
.WithMiscFilters();
};
public void ConfigureServices(IServiceCollection services)
{
services.Configure(FluidOptions);
services
.AddHandlersFrom<ConfigureLiquidEngine>()
.AddScoped<ILiquidTemplateManager, LiquidTemplateManager>()
.AddScoped<LiquidParser>()
.AddExpressionDescriptorProvider<LiquidExpressionDescriptorProvider>()
.AddLiquidFilter<Base64Filter>("base64")
.AddLiquidFilter<DictionaryKeysFilter>("keys");
}
}

View file

@ -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;
/// <summary>
/// Installs Python integration.
/// </summary>
[ShellFeature(
DisplayName = "Python Expressions",
Description = "Provides Python expression evaluation capabilities for workflows",
DependsOn = ["Mediator", "Expressions"])]
[UsedImplicitly]
public class PythonFeature : IShellFeature
{
/// <summary>
/// Configures the <see cref="Options.PythonOptions"/>.
/// </summary>
public Action<PythonOptions> PythonOptions { get; set; } = _ => { };
public void ConfigureServices(IServiceCollection services)
{
services.Configure(PythonOptions);
// Python services.
services
.AddScoped<IPythonEvaluator, PythonNetPythonEvaluator>()
.AddExpressionDescriptorProvider<PythonExpressionDescriptorProvider>();
// Handlers.
services.AddNotificationHandlersFrom<PythonFeature>();
// UI property handlers.
services.AddScoped<IPropertyUIHandler, RunPythonOptionsProvider>();
// Hosted services.
services.AddHostedService<PythonGlobalInterpreterManager>();
}
}

View file

@ -0,0 +1,20 @@
using CShells.Features;
using Elsa.Expressions.Contracts;
using Elsa.Expressions.Services;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Expressions.ShellFeatures;
/// <summary>
/// Installs and configures the expressions feature.
/// </summary>
[ShellFeature("Expressions")]
public class ExpressionsFeature : IShellFeature
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddScoped<IExpressionEvaluator, ExpressionEvaluator>()
.AddSingleton<IWellKnownTypeRegistry, WellKnownTypeRegistry>();
}
}

View file

@ -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;
/// <summary>
/// Installs and configures the clustering feature.
/// </summary>
[ShellFeature(
DisplayName = "Clustering",
Description = "Provides clustering and heartbeat capabilities for distributed Elsa deployments")]
[UsedImplicitly]
public class ClusteringFeature : IShellFeature
{
/// <summary>
/// A factory that instantiates an <see cref="IApplicationInstanceNameProvider"/>.
/// </summary>
public Func<IServiceProvider, IApplicationInstanceNameProvider> InstanceNameProvider { get; set; } = sp =>
{
return ActivatorUtilities.CreateInstance<RandomApplicationInstanceNameProvider>(sp);
};
/// <summary>
/// Represents the options for heartbeat feature.
/// </summary>
public Action<HeartbeatOptions> HeartbeatOptions { get; set; } = _ => { };
public void ConfigureServices(IServiceCollection services)
{
services.Configure(HeartbeatOptions)
.AddSingleton(InstanceNameProvider)
.AddSingleton<RandomIntIdentityGenerator>()
.AddHostedService<InstanceHeartbeatService>()
.AddHostedService<InstanceHeartbeatMonitorService>();
}
}

View file

@ -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;
/// <summary>
/// Installs services related to HTTP caching.
/// </summary>
[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<IHttpWorkflowsCacheManager, HttpWorkflowsCacheManager>()
.Decorate<IHttpWorkflowLookupService, CachingHttpWorkflowLookupService>()
.AddNotificationHandler<InvalidateHttpWorkflowsCache>();
}
}

View file

@ -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;
/// <summary>
/// Installs services related to HTTP services and activities.
/// </summary>
[ShellFeature(
DisplayName = "HTTP",
Description = "Provides HTTP-related activities and services for workflow execution",
DependsOn = ["HttpJavaScript", "Resilience"])]
[UsedImplicitly]
public class HttpFeature : IShellFeature
{
/// <summary>
/// A delegate to configure <see cref="HttpActivityOptions"/>.
/// </summary>
public Action<HttpActivityOptions>? ConfigureHttpOptions { get; set; }
/// <summary>
/// A delegate to configure <see cref="HttpFileCacheOptions"/>.
/// </summary>
public Action<HttpFileCacheOptions>? ConfigureHttpFileCacheOptions { get; set; }
/// <summary>
/// A delegate that is invoked when authorizing an inbound HTTP request.
/// </summary>
public Func<IServiceProvider, IHttpEndpointAuthorizationHandler> HttpEndpointAuthorizationHandler { get; set; } = sp => sp.GetRequiredService<AuthenticationBasedHttpEndpointAuthorizationHandler>();
/// <summary>
/// A delegate that is invoked when an HTTP workflow faults.
/// </summary>
public Func<IServiceProvider, IHttpEndpointFaultHandler> HttpEndpointWorkflowFaultHandler { get; set; } = sp => sp.GetRequiredService<DefaultHttpEndpointFaultHandler>();
/// <summary>
/// A delegate to configure the <see cref="IContentTypeProvider"/>.
/// </summary>
public Func<IServiceProvider, IContentTypeProvider> ContentTypeProvider { get; set; } = _ => new FileExtensionContentTypeProvider();
/// <summary>
/// A delegate to configure the <see cref="IFileCacheStorageProvider"/>.
/// </summary>
public Func<IServiceProvider, IFileCacheStorageProvider> FileCache { get; set; } = sp =>
{
var options = sp.GetRequiredService<IOptions<HttpFileCacheOptions>>().Value;
var blobStorage = StorageFactory.Blobs.DirectoryFiles(options.LocalCacheDirectory);
return new BlobFileCacheStorageProvider(blobStorage);
};
/// <summary>
/// A delegate to configure the <see cref="HttpClient"/> used when by the <see cref="FlowSendHttpRequest"/> and <see cref="SendHttpRequest"/> activities.
/// </summary>
public Action<IServiceProvider, HttpClient> HttpClient { get; set; } = (_, _) => { };
/// <summary>
/// A delegate to configure the <see cref="HttpClientBuilder"/> for <see cref="HttpClient"/>.
/// </summary>
public Action<IHttpClientBuilder> HttpClientBuilder { get; set; } = _ => { };
/// <summary>
/// A list of <see cref="IHttpCorrelationIdSelector"/> types to register with the service collection.
/// </summary>
public ICollection<Type> HttpCorrelationIdSelectorTypes { get; } = new List<Type>
{
typeof(HeaderHttpCorrelationIdSelector),
typeof(QueryStringHttpCorrelationIdSelector)
};
/// <summary>
/// A list of <see cref="IHttpWorkflowInstanceIdSelector"/> types to register with the service collection.
/// </summary>
public ICollection<Type> HttpWorkflowInstanceIdSelectorTypes { get; } = new List<Type>
{
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<SendHttpRequestBase>(HttpClient);
HttpClientBuilder(httpClientBuilder);
services
.AddScoped<IRouteMatcher, RouteMatcher>()
.AddScoped<IRouteTable, RouteTable>()
.AddScoped<IAbsoluteUrlProvider, DefaultAbsoluteUrlProvider>()
.AddScoped<IRouteTableUpdater, DefaultRouteTableUpdater>()
.AddScoped<IHttpWorkflowLookupService, HttpWorkflowLookupService>()
.AddScoped(ContentTypeProvider)
.AddHttpContextAccessor()
// Handlers.
.AddNotificationHandler<UpdateRouteTable>()
// Content parsers.
.AddSingleton<IHttpContentParser, JsonHttpContentParser>()
.AddSingleton<IHttpContentParser, XmlHttpContentParser>()
.AddSingleton<IHttpContentParser, PlainTextHttpContentParser>()
.AddSingleton<IHttpContentParser, TextHtmlHttpContentParser>()
.AddSingleton<IHttpContentParser, FileHttpContentParser>()
// HTTP content factories.
.AddScoped<IHttpContentFactory, TextContentFactory>()
.AddScoped<IHttpContentFactory, JsonContentFactory>()
.AddScoped<IHttpContentFactory, XmlContentFactory>()
.AddScoped<IHttpContentFactory, FormUrlEncodedHttpContentFactory>()
// Activity property options providers.
.AddScoped<IPropertyUIHandler, HttpContentTypeOptionsProvider>()
.AddScoped<IPropertyUIHandler, HttpEndpointPathUIHandler>()
// Default providers.
.AddScoped<DefaultHttpEndpointBasePathProvider>()
.AddScoped<IHttpEndpointBasePathProvider>(sp => sp.GetRequiredService<DefaultHttpEndpointBasePathProvider>())
// Port resolvers.
.AddScoped<IActivityResolver, SendHttpRequestActivityResolver>()
// HTTP endpoint handlers.
.AddScoped<AuthenticationBasedHttpEndpointAuthorizationHandler>()
.AddScoped<AllowAnonymousHttpEndpointAuthorizationHandler>()
.AddScoped<DefaultHttpEndpointFaultHandler>()
.AddScoped<DefaultHttpEndpointRoutesProvider>()
.AddScoped(HttpEndpointWorkflowFaultHandler)
.AddScoped(HttpEndpointAuthorizationHandler)
.AddScoped<IHttpEndpointRoutesProvider>(sp => sp.GetRequiredService<DefaultHttpEndpointRoutesProvider>())
// Startup tasks.
.AddStartupTask<UpdateRouteTableStartupTask>()
// Downloadable content handlers.
.AddScoped<IDownloadableManager, DefaultDownloadableManager>()
.AddScoped<IDownloadableContentHandler, MultiDownloadableContentHandler>()
.AddScoped<IDownloadableContentHandler, BinaryDownloadableContentHandler>()
.AddScoped<IDownloadableContentHandler, StreamDownloadableContentHandler>()
.AddScoped<IDownloadableContentHandler, FormFileDownloadableContentHandler>()
.AddScoped<IDownloadableContentHandler, DownloadableDownloadableContentHandler>()
.AddScoped<IDownloadableContentHandler, UrlDownloadableContentHandler>()
.AddScoped<IDownloadableContentHandler, StringDownloadableContentHandler>()
.AddScoped<IDownloadableContentHandler, HttpFileDownloadableContentHandler>()
//Trigger payload validators.
.AddTriggerPayloadValidator<HttpEndpointTriggerPayloadValidator, HttpEndpointBookmarkPayload>()
// File caches.
.AddScoped(FileCache)
.AddScoped<ZipManager>()
// AuthenticationBasedHttpEndpointAuthorizationHandler requires Authorization services.
.AddAuthorization();
// HTTP clients.
services.AddHttpClient<IFileDownloader, HttpClientFileDownloader>();
// Add selectors.
foreach (var httpCorrelationIdSelectorType in HttpCorrelationIdSelectorTypes)
services.AddScoped(typeof(IHttpCorrelationIdSelector), httpCorrelationIdSelectorType);
foreach (var httpWorkflowInstanceIdSelectorType in HttpWorkflowInstanceIdSelectorTypes)
services.AddScoped(typeof(IHttpWorkflowInstanceIdSelector), httpWorkflowInstanceIdSelectorType);
services.Configure<ExpressionOptions>(options =>
{
options.AddTypeAlias<HttpRequest>("HttpRequest");
options.AddTypeAlias<HttpResponse>("HttpResponse");
options.AddTypeAlias<HttpResponseMessage>("HttpResponseMessage");
options.AddTypeAlias<HttpHeaders>("HttpHeaders");
options.AddTypeAlias<HttpRouteData>("RouteData");
options.AddTypeAlias<IFormFile>("FormFile");
options.AddTypeAlias<IFormFile[]>("FormFile[]");
options.AddTypeAlias<HttpFile>("HttpFile");
options.AddTypeAlias<HttpFile[]>("HttpFile[]");
options.AddTypeAlias<Downloadable>("Downloadable");
options.AddTypeAlias<Downloadable[]>("Downloadable[]");
});
}
}

View file

@ -0,0 +1,23 @@
using CShells.Features;
using Elsa.Http.Scripting.JavaScript;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Http.ShellFeatures;
/// <summary>
/// Provides JavaScript integration for HTTP features.
/// </summary>
[ShellFeature(
DisplayName = "HTTP JavaScript",
Description = "Provides JavaScript integration for HTTP activities")]
[UsedImplicitly]
public class HttpJavaScriptFeature : IShellFeature
{
public void ConfigureServices(IServiceCollection services)
{
services.AddNotificationHandler<HttpJavaScriptHandler>();
}
}

View file

@ -9,6 +9,7 @@
<ItemGroup>
<PackageReference Include="AspNetCore.Authentication.ApiKey" />
<PackageReference Include="CShells.FastEndpoints.Abstractions" />
</ItemGroup>
<ItemGroup>

View file

@ -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;
/// <summary>
/// Provides an authorization feature that configures the system with JWT bearer and API key authentication.
/// </summary>
[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";
/// <summary>
/// Gets or sets the API key provider type.
/// </summary>
public Type ApiKeyProviderType { get; set; } = typeof(DefaultApiKeyProvider);
/// <summary>
/// Gets or sets whether to require localhost for the security root policy.
/// </summary>
public bool RequireLocalHost { get; set; } = true;
public void ConfigureServices(IServiceCollection services)
{
services.ConfigureOptions<ConfigureJwtBearerOptions>();
services.ConfigureOptions<ValidateIdentityTokenOptions>();
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<AdminApiKeyProvider>();
else
authBuilder.AddApiKeyInAuthorizationHeader<DefaultApiKeyProvider>();
services.AddScoped<IAuthorizationHandler, LocalHostRequirementHandler>();
services.AddScoped<IAuthorizationHandler, LocalHostPermissionRequirementHandler>();
services.AddScoped(ApiKeyProviderType);
services.AddScoped<IApiKeyProvider>(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());
});
}
}

View file

@ -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;
/// <summary>
/// Provides identity feature to authenticate &amp; authorize API requests.
/// </summary>
[ShellFeature(
DisplayName = "Identity",
Description = "Provides identity management, authentication and authorization capabilities",
DependsOn = ["SystemClock"])]
[UsedImplicitly]
public class IdentityFeature : IFastEndpointsShellFeature
{
/// <summary>
/// A delegate that creates an instance of an implementation of <see cref="IUserStore"/>.
/// </summary>
public Func<IServiceProvider, IUserStore> UserStore { get; set; } = sp => sp.GetRequiredService<MemoryUserStore>();
/// <summary>
/// A delegate that creates an instance of an implementation of <see cref="IApplicationStore"/>.
/// </summary>
public Func<IServiceProvider, IApplicationStore> ApplicationStore { get; set; } = sp => sp.GetRequiredService<MemoryApplicationStore>();
/// <summary>
/// A delegate that creates an instance of an implementation of <see cref="IRoleStore"/>.
/// </summary>
public Func<IServiceProvider, IRoleStore> RoleStore { get; set; } = sp => sp.GetRequiredService<MemoryRoleStore>();
/// <summary>
/// A delegate that creates an instance of an implementation of <see cref="IUserProvider"/>.
/// </summary>
public Func<IServiceProvider, IUserProvider> UserProvider { get; set; } = sp => sp.GetRequiredService<AdminUserProvider>();
/// <summary>
/// A delegate that creates an instance of an implementation of <see cref="IApplicationProvider"/>.
/// </summary>
public Func<IServiceProvider, IApplicationProvider> ApplicationProvider { get; set; } = sp => sp.GetRequiredService<StoreBasedApplicationProvider>();
/// <summary>
/// A delegate that creates an instance of an implementation of <see cref="IRoleProvider"/>.
/// </summary>
public Func<IServiceProvider, IRoleProvider> RoleProvider { get; set; } = sp => sp.GetRequiredService<AdminRoleProvider>();
public void ConfigureServices(IServiceCollection services)
{
// Configure options - Note: SigningKey must be configured by the application for security
services.Configure<IdentityTokenOptions>(_ => { });
services.Configure<ApiKeyOptions>(ApiKeyDefaults.AuthenticationScheme, options =>
{
options.Realm = "Elsa Workflows";
options.KeyName = "ApiKey";
});
services.Configure<UsersOptions>(_ => { });
services.Configure<ApplicationsOptions>(_ => { });
services.Configure<RolesOptions>(_ => { });
// Memory stores.
services
.AddMemoryStore<User, MemoryUserStore>()
.AddMemoryStore<Application, MemoryApplicationStore>()
.AddMemoryStore<Role, MemoryRoleStore>();
// User providers.
services
.AddScoped<AdminUserProvider>()
.AddScoped<StoreBasedUserProvider>()
.AddScoped<ConfigurationBasedUserProvider>();
// Application providers.
services
.AddScoped<StoreBasedApplicationProvider>()
.AddScoped<ConfigurationBasedApplicationProvider>();
// Role providers.
services
.AddScoped<AdminRoleProvider>()
.AddScoped<StoreBasedRoleProvider>()
.AddScoped<ConfigurationBasedRoleProvider>();
// Tenant resolution strategies.
services
.AddScoped<ITenantResolver, ClaimsTenantResolver>()
.AddScoped<ITenantResolver, CurrentUserTenantResolver>();
// Services.
services
.AddScoped(UserStore)
.AddScoped(ApplicationStore)
.AddScoped(RoleStore)
.AddScoped(UserProvider)
.AddScoped(ApplicationProvider)
.AddScoped(RoleProvider)
.AddScoped<ISecretHasher, DefaultSecretHasher>()
.AddScoped<IAccessTokenIssuer, DefaultAccessTokenIssuer>()
.AddScoped<IUserCredentialsValidator, DefaultUserCredentialsValidator>()
.AddScoped<IApplicationCredentialsValidator, DefaultApplicationCredentialsValidator>()
.AddScoped<IApiKeyGenerator>(sp => sp.GetRequiredService<DefaultApiKeyGeneratorAndParser>())
.AddScoped<IApiKeyParser>(sp => sp.GetRequiredService<DefaultApiKeyGeneratorAndParser>())
.AddScoped<IClientIdGenerator, DefaultClientIdGenerator>()
.AddScoped<ISecretGenerator, DefaultSecretGenerator>()
.AddScoped<IRandomStringGenerator, DefaultRandomStringGenerator>()
.AddScoped<DefaultApiKeyGeneratorAndParser>()
.AddHttpContextAccessor()
;
}
}

View file

@ -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;
/// <summary>
/// Installs and configures key-value store features.
/// </summary>
[ShellFeature(
DisplayName = "Key-Value Store",
Description = "Provides key-value storage capabilities for workflows")]
[UsedImplicitly]
public class KeyValueFeature : IShellFeature
{
/// <summary>
/// A factory that instantiates an <see cref="IKeyValueStore"/>.
/// </summary>
public Func<IServiceProvider, IKeyValueStore> KeyValueStore { get; set; } = sp => ActivatorUtilities.CreateInstance<MemoryKeyValueStore>(sp);
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped(KeyValueStore);
}
}

View file

@ -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;
/// <summary>
/// Enables functionality to tag workflows with labels.
/// </summary>
[ShellFeature(
DisplayName = "Labels",
Description = "Enables functionality to tag workflows with labels",
DependsOn = ["Mediator"])]
[UsedImplicitly]
public class LabelsFeature : IShellFeature
{
/// <summary>
/// A delegate that provides an instance of an implementation of <see cref="ILabelStore"/>.
/// </summary>
public Func<IServiceProvider, ILabelStore> LabelStore { get; set; } = sp => sp.GetRequiredService<InMemoryLabelStore>();
/// <summary>
/// A delegate that provides an instance of an implementation of <see cref="IWorkflowDefinitionLabelStore"/>.
/// </summary>
public Func<IServiceProvider, IWorkflowDefinitionLabelStore> WorkflowDefinitionLabelStore { get; set; } = sp => sp.GetRequiredService<InMemoryWorkflowDefinitionLabelStore>();
public void ConfigureServices(IServiceCollection services)
{
services
.AddMemoryStore<Label, InMemoryLabelStore>()
.AddMemoryStore<WorkflowDefinitionLabel, InMemoryWorkflowDefinitionLabelStore>()
.AddScoped(LabelStore)
.AddScoped(WorkflowDefinitionLabelStore);
services.AddNotificationHandlersFrom(GetType());
}
}

View file

@ -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<TDbContext> : IShellFeature
where TDbContext : ElsaDbContextBase
{
/// <summary>
/// Gets or sets a value indicating whether to use context pooling.
/// </summary>
public bool UseContextPooling { get; set; }
/// <summary>
/// Gets or sets a value indicating whether to run migrations.
/// </summary>
public bool RunMigrations { get; set; } = true;
/// <summary>
/// Gets or sets the lifetime of the <see cref="IDbContextFactory{TContext}"/>. Defaults to <see cref="ServiceLifetime.Scoped"/>.
/// </summary>
public ServiceLifetime DbContextFactoryLifetime { get; set; } = ServiceLifetime.Scoped;
/// <summary>
/// Gets or sets the connection string to use for the database.
/// </summary>
public string ConnectionString { get; set; } = null!;
/// <summary>
/// Gets or sets additional options to configure the database context.
/// </summary>
public ElsaDbContextOptions? DbContextOptions { get; set; }
/// <summary>
/// Gets or sets the callback used to configure the <see cref="DbContextOptionsBuilder"/>.
/// </summary>
protected virtual Action<IServiceProvider, DbContextOptionsBuilder> DbContextOptionsBuilder { get; set; } = (_, _) => { };
public void ConfigureServices(IServiceCollection services)
{
Action<IServiceProvider, DbContextOptionsBuilder> 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<TDbContext>(setup);
else
services.AddDbContextFactory<TDbContext>(setup, DbContextFactoryLifetime);
services.Decorate<IDbContextFactory<TDbContext>, TenantAwareDbContextFactory<TDbContext>>();
services.Configure<MigrationOptions>(options =>
{
options.RunMigrations[typeof(TDbContext)] = RunMigrations;
});
services.AddStartupTask<RunMigrationsStartupTask<TDbContext>>();
OnConfiguring(services);
}
/// <summary>
/// Gets the assembly containing migrations for this provider.
/// By default, returns the assembly of the concrete feature type.
/// </summary>
protected virtual Assembly GetMigrationsAssembly() => GetType().Assembly;
/// <summary>
/// Configures the database provider for the specified <see cref="DbContextOptionsBuilder"/>.
/// </summary>
/// <param name="builder">The options builder to configure.</param>
/// <param name="migrationsAssembly">The assembly containing migrations.</param>
/// <param name="connectionString">The connection string to use.</param>
/// <param name="options">Additional options to configure the database context.</param>
protected abstract void ConfigureProvider(
DbContextOptionsBuilder builder,
Assembly migrationsAssembly,
string connectionString,
ElsaDbContextOptions? options);
protected virtual void OnConfiguring(IServiceCollection services)
{
}
/// <summary>
/// Adds a store to the service collection.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TStore">The type of the store.</typeparam>
protected void AddStore<TEntity, TStore>(IServiceCollection services) where TEntity : class, new() where TStore : class
{
services
.AddScoped<Store<TDbContext, TEntity>>()
.AddScoped<TStore>()
;
}
/// <summary>
/// Adds an entity store to the service collection.
/// </summary>
/// <typeparam name="TEntity">The type of the entity.</typeparam>
/// <typeparam name="TStore">The type of the store.</typeparam>
protected void AddEntityStore<TEntity, TStore>(IServiceCollection services) where TEntity : Entity, new() where TStore : class
{
services
.AddScoped<EntityStore<TDbContext, TEntity>>()
.AddScoped<TStore>()
;
}
}

View file

@ -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;
/// <summary>
/// Configures the alterations feature to use MySql persistence.
/// </summary>
[ShellFeature(
DisplayName = "MySql Alterations Persistence",
Description = "Provides MySql persistence for workflow alterations",
DependsOn = ["Alterations"])]
[UsedImplicitly]
public class MySqlAlterationsPersistenceShellFeature
: EFCoreAlterationsPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaMySql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the identity feature to use MySql persistence.
/// </summary>
[ShellFeature(
DisplayName = "MySql Identity Persistence",
Description = "Provides MySql persistence for identity management",
DependsOn = ["Identity"])]
[UsedImplicitly]
public class MySqlIdentityPersistenceShellFeature
: EFCoreIdentityPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaMySql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the labels feature to use MySql persistence.
/// </summary>
[ShellFeature(
DisplayName = "MySql Label Persistence",
Description = "Provides MySql persistence for label management",
DependsOn = ["Labels"])]
[UsedImplicitly]
public class MySqlLabelPersistenceShellFeature
: EFCoreLabelPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaMySql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the management feature to use MySql persistence.
/// </summary>
[ShellFeature(
DisplayName = "MySql Workflow Definition Persistence",
Description = "Provides MySql persistence for workflow definitions",
DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])]
[UsedImplicitly]
public class MySqlWorkflowDefinitionPersistenceShellFeature
: EFCoreWorkflowDefinitionPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaMySql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the management feature to use MySql persistence.
/// </summary>
[ShellFeature(
DisplayName = "MySql Workflow Instance Persistence",
Description = "Provides MySql persistence for workflow instances",
DependsOn = ["WorkflowManagement", "WorkflowInstances"])]
[UsedImplicitly]
public class MySqlWorkflowInstancePersistenceShellFeature
: EFCoreWorkflowInstancePersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaMySql(migrationsAssembly, connectionString, options);
}
}

View file

@ -0,0 +1,20 @@
using CShells.Features;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Persistence.EFCore.MySql.ShellFeatures;
/// <summary>
/// Configures the persistence feature to use MySql persistence.
/// </summary>
[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)
{
}
}

View file

@ -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;
/// <summary>
/// Configures the runtime feature to use MySql persistence.
/// </summary>
[ShellFeature(
DisplayName = "MySql Workflow Runtime Persistence",
Description = "Provides MySql persistence for workflow runtime",
DependsOn = ["WorkflowRuntime"])]
[UsedImplicitly]
public class MySqlWorkflowRuntimePersistenceShellFeature
: EFCoreWorkflowRuntimePersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaMySql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the tenants feature to use MySql persistence.
/// </summary>
[ShellFeature(
DisplayName = "MySql Tenant Persistence",
Description = "Provides MySql persistence for tenant management",
DependsOn = ["TenantManagement"])]
[UsedImplicitly]
public class MySqlTenantPersistenceShellFeature
: EFCoreTenantManagementShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaMySql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the alterations feature to use Oracle persistence.
/// </summary>
[ShellFeature(
DisplayName = "Oracle Alterations Persistence",
Description = "Provides Oracle persistence for workflow alterations",
DependsOn = ["Alterations"])]
[UsedImplicitly]
public class OracleAlterationsPersistenceShellFeature
: EFCoreAlterationsPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaOracle(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the identity feature to use Oracle persistence.
/// </summary>
[ShellFeature(
DisplayName = "Oracle Identity Persistence",
Description = "Provides Oracle persistence for identity management",
DependsOn = ["Identity"])]
[UsedImplicitly]
public class OracleIdentityPersistenceShellFeature
: EFCoreIdentityPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaOracle(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the labels feature to use Oracle persistence.
/// </summary>
[ShellFeature(
DisplayName = "Oracle Label Persistence",
Description = "Provides Oracle persistence for label management",
DependsOn = ["Labels"])]
[UsedImplicitly]
public class OracleLabelPersistenceShellFeature
: EFCoreLabelPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaOracle(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the management feature to use Oracle persistence.
/// </summary>
[ShellFeature(
DisplayName = "Oracle Workflow Definition Persistence",
Description = "Provides Oracle persistence for workflow definitions",
DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])]
[UsedImplicitly]
public class OracleWorkflowDefinitionPersistenceShellFeature
: EFCoreWorkflowDefinitionPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaOracle(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the management feature to use Oracle persistence.
/// </summary>
[ShellFeature(
DisplayName = "Oracle Workflow Instance Persistence",
Description = "Provides Oracle persistence for workflow instances",
DependsOn = ["WorkflowManagement", "WorkflowInstances"])]
[UsedImplicitly]
public class OracleWorkflowInstancePersistenceShellFeature
: EFCoreWorkflowInstancePersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaOracle(migrationsAssembly, connectionString, options);
}
}

View file

@ -0,0 +1,20 @@
using CShells.Features;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures;
/// <summary>
/// Configures the persistence feature to use Oracle persistence.
/// </summary>
[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)
{
}
}

View file

@ -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;
/// <summary>
/// Configures the runtime feature to use Oracle persistence.
/// </summary>
[ShellFeature(
DisplayName = "Oracle Workflow Runtime Persistence",
Description = "Provides Oracle persistence for workflow runtime",
DependsOn = ["WorkflowRuntime"])]
[UsedImplicitly]
public class OracleWorkflowRuntimePersistenceShellFeature
: EFCoreWorkflowRuntimePersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaOracle(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the tenants feature to use Oracle persistence.
/// </summary>
[ShellFeature(
DisplayName = "Oracle Tenant Persistence",
Description = "Provides Oracle persistence for tenant management",
DependsOn = ["TenantManagement"])]
[UsedImplicitly]
public class OracleTenantPersistenceShellFeature
: EFCoreTenantManagementShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaOracle(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the alterations feature to use PostgreSql persistence.
/// </summary>
[ShellFeature(
DisplayName = "PostgreSql Alterations Persistence",
Description = "Provides PostgreSql persistence for workflow alterations",
DependsOn = ["Alterations"])]
[UsedImplicitly]
public class PostgreSqlAlterationsPersistenceShellFeature
: EFCoreAlterationsPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the identity feature to use PostgreSql persistence.
/// </summary>
[ShellFeature(
DisplayName = "PostgreSql Identity Persistence",
Description = "Provides PostgreSql persistence for identity management",
DependsOn = ["Identity"])]
[UsedImplicitly]
public class PostgreSqlIdentityPersistenceShellFeature
: EFCoreIdentityPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the labels feature to use PostgreSql persistence.
/// </summary>
[ShellFeature(
DisplayName = "PostgreSql Label Persistence",
Description = "Provides PostgreSql persistence for label management",
DependsOn = ["Labels"])]
[UsedImplicitly]
public class PostgreSqlLabelPersistenceShellFeature
: EFCoreLabelPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the management feature to use PostgreSql persistence.
/// </summary>
[ShellFeature(
DisplayName = "PostgreSql Workflow Definition Persistence",
Description = "Provides PostgreSql persistence for workflow definitions",
DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])]
[UsedImplicitly]
public class PostgreSqlWorkflowDefinitionPersistenceShellFeature
: EFCoreWorkflowDefinitionPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the management feature to use PostgreSql persistence.
/// </summary>
[ShellFeature(
DisplayName = "PostgreSql Workflow Instance Persistence",
Description = "Provides PostgreSql persistence for workflow instances",
DependsOn = ["WorkflowManagement", "WorkflowInstances"])]
[UsedImplicitly]
public class PostgreSqlWorkflowInstancePersistenceShellFeature
: EFCoreWorkflowInstancePersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options);
}
}

View file

@ -0,0 +1,20 @@
using CShells.Features;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures;
/// <summary>
/// Configures the persistence feature to use PostgreSql persistence.
/// </summary>
[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)
{
}
}

View file

@ -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;
/// <summary>
/// Configures the runtime feature to use PostgreSql persistence.
/// </summary>
[ShellFeature(
DisplayName = "PostgreSql Workflow Runtime Persistence",
Description = "Provides PostgreSql persistence for workflow runtime",
DependsOn = ["WorkflowRuntime"])]
[UsedImplicitly]
public class PostgreSqlWorkflowRuntimePersistenceShellFeature
: EFCoreWorkflowRuntimePersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the tenants feature to use PostgreSql persistence.
/// </summary>
[ShellFeature(
DisplayName = "PostgreSql Tenant Persistence",
Description = "Provides PostgreSql persistence for tenant management",
DependsOn = ["TenantManagement"])]
[UsedImplicitly]
public class PostgreSqlTenantPersistenceShellFeature
: EFCoreTenantManagementShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the alterations feature to use SqlServer persistence.
/// </summary>
[ShellFeature(
DisplayName = "SqlServer Alterations Persistence",
Description = "Provides SqlServer persistence for workflow alterations",
DependsOn = ["Alterations"])]
[UsedImplicitly]
public class SqlServerAlterationsPersistenceShellFeature
: EFCoreAlterationsPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlServer(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the identity feature to use SqlServer persistence.
/// </summary>
[ShellFeature(
DisplayName = "SqlServer Identity Persistence",
Description = "Provides SqlServer persistence for identity management",
DependsOn = ["Identity"])]
[UsedImplicitly]
public class SqlServerIdentityPersistenceShellFeature
: EFCoreIdentityPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlServer(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the labels feature to use SqlServer persistence.
/// </summary>
[ShellFeature(
DisplayName = "SqlServer Label Persistence",
Description = "Provides SqlServer persistence for label management",
DependsOn = ["Labels"])]
[UsedImplicitly]
public class SqlServerLabelPersistenceShellFeature
: EFCoreLabelPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlServer(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the management feature to use SqlServer persistence.
/// </summary>
[ShellFeature(
DisplayName = "SqlServer Workflow Definition Persistence",
Description = "Provides SqlServer persistence for workflow definitions",
DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])]
[UsedImplicitly]
public class SqlServerWorkflowDefinitionPersistenceShellFeature
: EFCoreWorkflowDefinitionPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlServer(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the management feature to use SqlServer persistence.
/// </summary>
[ShellFeature(
DisplayName = "SqlServer Workflow Instance Persistence",
Description = "Provides SqlServer persistence for workflow instances",
DependsOn = ["WorkflowManagement", "WorkflowInstances"])]
[UsedImplicitly]
public class SqlServerWorkflowInstancePersistenceShellFeature
: EFCoreWorkflowInstancePersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlServer(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the runtime feature to use SqlServer persistence.
/// </summary>
[ShellFeature(
DisplayName = "SqlServer Workflow Runtime Persistence",
Description = "Provides SqlServer persistence for workflow runtime",
DependsOn = ["WorkflowRuntime"])]
[UsedImplicitly]
public class SqlServerWorkflowRuntimePersistenceShellFeature
: EFCoreWorkflowRuntimePersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlServer(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the tenants feature to use SqlServer persistence.
/// </summary>
[ShellFeature(
DisplayName = "SqlServer Tenant Persistence",
Description = "Provides SqlServer persistence for tenant management",
DependsOn = ["TenantManagement"])]
[UsedImplicitly]
public class SqlServerTenantPersistenceShellFeature
: EFCoreTenantManagementShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlServer(migrationsAssembly, connectionString, options);
}
}

View file

@ -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;
/// <summary>
/// Configures the alterations feature to use Sqlite persistence.
/// </summary>
[ShellFeature(
DisplayName = "Sqlite Alterations Persistence",
Description = "Provides Sqlite persistence for workflow alterations",
DependsOn = ["Alterations"])]
[UsedImplicitly]
public class SqliteAlterationsPersistenceShellFeature
: EFCoreAlterationsPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlite(migrationsAssembly, connectionString, options);
}
/// <inheritdoc />
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IEntityModelCreatingHandler, SetupForSqlite>();
base.OnConfiguring(services);
}
}

View file

@ -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;
/// <summary>
/// Configures the identity feature to use Sqlite persistence.
/// </summary>
[ShellFeature(
DisplayName = "Sqlite Identity Persistence",
Description = "Provides Sqlite persistence for identity management",
DependsOn = ["Identity"])]
[UsedImplicitly]
public class SqliteIdentityPersistenceShellFeature
: EFCoreIdentityPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlite(migrationsAssembly, connectionString, options);
}
/// <inheritdoc />
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IEntityModelCreatingHandler, SetupForSqlite>();
base.OnConfiguring(services);
}
}

View file

@ -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;
/// <summary>
/// Configures the labels feature to use Sqlite persistence.
/// </summary>
[ShellFeature(
DisplayName = "Sqlite Label Persistence",
Description = "Provides Sqlite persistence for label management",
DependsOn = ["Labels"])]
[UsedImplicitly]
public class SqliteLabelPersistenceShellFeature
: EFCoreLabelPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlite(migrationsAssembly, connectionString, options);
}
/// <inheritdoc />
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IEntityModelCreatingHandler, SetupForSqlite>();
base.OnConfiguring(services);
}
}

View file

@ -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;
/// <summary>
/// Configures the management feature to use Sqlite persistence.
/// </summary>
[ShellFeature(
DisplayName = "Sqlite Workflow Definition Persistence",
Description = "Provides Sqlite persistence for workflow definitions",
DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])]
[UsedImplicitly]
public class SqliteWorkflowDefinitionPersistenceShellFeature
: EFCoreWorkflowDefinitionPersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlite(migrationsAssembly, connectionString, options);
}
/// <inheritdoc />
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IEntityModelCreatingHandler, SetupForSqlite>();
base.OnConfiguring(services);
}
}

View file

@ -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;
/// <summary>
/// Configures the management feature to use Sqlite persistence.
/// </summary>
[ShellFeature(
DisplayName = "Sqlite Workflow Instance Persistence",
Description = "Provides Sqlite persistence for workflow instances",
DependsOn = ["WorkflowManagement", "WorkflowInstances"])]
[UsedImplicitly]
public class SqliteWorkflowInstancePersistenceShellFeature
: EFCoreWorkflowInstancePersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlite(migrationsAssembly, connectionString, options);
}
/// <inheritdoc />
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IEntityModelCreatingHandler, SetupForSqlite>();
base.OnConfiguring(services);
}
}

View file

@ -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;
/// <summary>
/// Configures the runtime feature to use Sqlite persistence.
/// </summary>
[ShellFeature(
DisplayName = "Sqlite Workflow Runtime Persistence",
Description = "Provides Sqlite persistence for workflow runtime",
DependsOn = ["WorkflowRuntime"])]
[UsedImplicitly]
public class SqliteWorkflowRuntimePersistenceShellFeature
: EFCoreWorkflowRuntimePersistenceShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlite(migrationsAssembly, connectionString, options);
}
/// <inheritdoc />
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IEntityModelCreatingHandler, SetupForSqlite>();
base.OnConfiguring(services);
}
}

View file

@ -0,0 +1,20 @@
using CShells.Features;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures;
/// <summary>
/// Configures the persistence feature to use Sqlite persistence.
/// </summary>
[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)
{
}
}

View file

@ -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;
/// <summary>
/// Configures the tenants feature to use Sqlite persistence.
/// </summary>
[ShellFeature(
DisplayName = "Sqlite Tenant Persistence",
Description = "Provides Sqlite persistence for tenant management",
DependsOn = ["TenantManagement"])]
[UsedImplicitly]
public class SqliteTenantPersistenceShellFeature
: EFCoreTenantManagementShellFeatureBase
{
/// <inheritdoc />
protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options)
{
builder.UseElsaSqlite(migrationsAssembly, connectionString, options);
}
/// <inheritdoc />
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IEntityModelCreatingHandler, SetupForSqlite>();
base.OnConfiguring(services);
}
}

View file

@ -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;
/// <summary>
/// Base class for alterations persistence features.
/// This is not a standalone shell feature - use provider-specific features.
/// </summary>
[UsedImplicitly]
public abstract class EFCoreAlterationsPersistenceShellFeatureBase : PersistenceShellFeatureBase<AlterationsElsaDbContext>
{
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IAlterationPlanStore, EFCoreAlterationPlanStore>();
services.AddScoped<IAlterationJobStore, EFCoreAlterationJobStore>();
AddEntityStore<AlterationPlan, EFCoreAlterationPlanStore>(services);
AddEntityStore<AlterationJob, EFCoreAlterationJobStore>(services);
}
}

View file

@ -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;
/// <summary>
/// Base class for identity persistence features.
/// This is not a standalone shell feature - use provider-specific features.
/// </summary>
[UsedImplicitly]
public abstract class EFCoreIdentityPersistenceShellFeatureBase : PersistenceShellFeatureBase<IdentityElsaDbContext>
{
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IUserStore, EFCoreUserStore>();
services.AddScoped<IApplicationStore, EFCoreApplicationStore>();
services.AddScoped<IRoleStore, EFCoreRoleStore>();
AddEntityStore<User, EFCoreUserStore>(services);
AddEntityStore<Application, EFCoreApplicationStore>(services);
AddEntityStore<Role, EFCoreRoleStore>(services);
}
}

View file

@ -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;
/// <summary>
/// Base class for label persistence features.
/// This is not a standalone shell feature - use provider-specific features.
/// </summary>
[UsedImplicitly]
public abstract class EFCoreLabelPersistenceShellFeatureBase : PersistenceShellFeatureBase<LabelsElsaDbContext>
{
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<ILabelStore, EFCoreLabelStore>();
services.AddScoped<IWorkflowDefinitionLabelStore, EFCoreWorkflowDefinitionLabelStore>();
AddEntityStore<Label, EFCoreLabelStore>(services);
AddEntityStore<WorkflowDefinitionLabel, EFCoreWorkflowDefinitionLabelStore>(services);
}
}

View file

@ -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;
/// <summary>
/// Base class for workflow definition persistence features.
/// This is not a standalone shell feature - use provider-specific features like SqliteWorkflowDefinitionPersistenceShellFeature.
/// </summary>
[UsedImplicitly]
public abstract class EFCoreWorkflowDefinitionPersistenceShellFeatureBase : PersistenceShellFeatureBase<ManagementElsaDbContext>
{
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IWorkflowDefinitionStore, EFCoreWorkflowDefinitionStore>();
AddEntityStore<WorkflowDefinition, EFCoreWorkflowDefinitionStore>(services);
}
}

View file

@ -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;
/// <summary>
/// Base class for workflow instance persistence features.
/// This is not a standalone shell feature - use provider-specific features.
/// </summary>
[UsedImplicitly]
public abstract class EFCoreWorkflowInstancePersistenceShellFeatureBase : PersistenceShellFeatureBase<ManagementElsaDbContext>
{
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<IWorkflowInstanceStore, EFCoreWorkflowInstanceStore>();
AddEntityStore<WorkflowInstance, EFCoreWorkflowInstanceStore>(services);
}
}

View file

@ -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;
/// <summary>
/// Base class for workflow runtime persistence features.
/// This is not a standalone shell feature - use provider-specific features.
/// </summary>
[UsedImplicitly]
public abstract class EFCoreWorkflowRuntimePersistenceShellFeatureBase : PersistenceShellFeatureBase<RuntimeElsaDbContext>
{
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<ITriggerStore, EFCoreTriggerStore>();
services.AddScoped<IBookmarkStore, EFCoreBookmarkStore>();
services.AddScoped<IBookmarkQueueStore, EFBookmarkQueueStore>();
services.AddScoped<IWorkflowExecutionLogStore, EFCoreWorkflowExecutionLogStore>();
services.AddScoped<IActivityExecutionStore, EFCoreActivityExecutionStore>();
services.AddScoped<IKeyValueStore, EFCoreKeyValueStore>();
AddEntityStore<StoredTrigger, EFCoreTriggerStore>(services);
AddStore<StoredBookmark, EFCoreBookmarkStore>(services);
AddStore<BookmarkQueueItem, EFBookmarkQueueStore>(services);
AddEntityStore<WorkflowExecutionLogRecord, EFCoreWorkflowExecutionLogStore>(services);
AddEntityStore<ActivityExecutionRecord, EFCoreActivityExecutionStore>(services);
AddStore<SerializedKeyValuePair, EFCoreKeyValueStore>(services);
}
}

View file

@ -0,0 +1,20 @@
using Elsa.Common.Multitenancy;
using Elsa.Tenants;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Persistence.EFCore.Modules.Tenants;
/// <summary>
/// Base class for tenant management persistence features.
/// This is not a standalone shell feature - use provider-specific features.
/// </summary>
[UsedImplicitly]
public abstract class EFCoreTenantManagementShellFeatureBase : PersistenceShellFeatureBase<TenantsElsaDbContext>
{
protected override void OnConfiguring(IServiceCollection services)
{
services.AddScoped<ITenantStore, EFCoreTenantStore>();
AddEntityStore<Tenant, EFCoreTenantStore>(services);
}
}

View file

@ -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<ExpressionOptions>(options =>
{
options.AddTypeAlias<List<RetryAttemptRecord>>("RetryAttemptRecordList");
});
services.AddOptions<ResilienceOptions>();
services
.AddSingleton<ResilienceStrategySerializer>()
.AddSingleton<IActivityDescriptorModifier, ResilientActivityDescriptorModifier>()
.AddScoped<IResilienceStrategyCatalog, ResilienceStrategyCatalog>()
.AddScoped<IResilienceStrategyConfigEvaluator, ResilienceStrategyConfigEvaluator>()
.AddScoped<IResilientActivityInvoker, ResilientActivityInvoker>()
.AddScoped<IResilienceStrategySource, ConfigurationResilienceStrategySource>()
.AddSingleton(VoidRetryAttemptRecorder.Instance)
.AddSingleton(VoidRetryAttemptReader.Instance)
.AddScoped<IRetryAttemptRecorder, ActivityExecutionContextRetryAttemptRecorder>()
.AddScoped<IRetryAttemptReader, ActivityExecutionContextRetryAttemptReader>()
.AddScoped<ActivityExecutionContextRetryAttemptReader>()
.AddHandlersFrom<ResilienceShellFeature>();
// Register transient exception detection infrastructure
services
.AddSingleton<ITransientExceptionStrategy, DefaultTransientExceptionStrategy>()
.AddSingleton<ITransientExceptionDetector, TransientExceptionDetector>();
}
}

View file

@ -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;
/// <summary>
/// Adds the SAS tokens feature to the workflow runtime.
/// </summary>
[ShellFeature(
DisplayName = "SAS Tokens",
Description = "Provides shared access signature token generation and validation")]
[UsedImplicitly]
public class SasTokensFeature : IShellFeature
{
private Func<IServiceProvider, ITokenService> TokenService { get; set; } = sp => ActivatorUtilities.CreateInstance<DataProtectorTokenService>(sp);
public void ConfigureServices(IServiceCollection services)
{
var builder = services.AddDataProtection();
builder.SetApplicationName("Elsa Workflows");
services.AddScoped(TokenService);
}
}

View file

@ -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;
/// <summary>
/// Provides scheduling features to the system.
/// </summary>
[ShellFeature(
DisplayName = "Scheduling",
Description = "Provides scheduling capabilities for workflows including cron and delay-based triggers",
DependsOn = ["SystemClock"])]
[UsedImplicitly]
public class SchedulingFeature : IShellFeature
{
/// <summary>
/// Gets or sets the trigger scheduler factory.
/// </summary>
public Func<IServiceProvider, IWorkflowScheduler> WorkflowScheduler { get; set; } = sp => sp.GetRequiredService<DefaultWorkflowScheduler>();
/// <summary>
/// Gets or sets the CRON parser factory.
/// </summary>
public Func<IServiceProvider, ICronParser> CronParser { get; set; } = sp => sp.GetRequiredService<CronosCronParser>();
public void ConfigureServices(IServiceCollection services)
{
services
.AddSingleton<UpdateTenantSchedules>()
.AddSingleton<ITenantActivatedEvent>(sp => sp.GetRequiredService<UpdateTenantSchedules>())
.AddSingleton<ITenantDeletedEvent>(sp => sp.GetRequiredService<UpdateTenantSchedules>())
.AddSingleton<IScheduler, LocalScheduler>()
.AddSingleton<CronosCronParser>()
.AddSingleton(CronParser)
.AddScoped<ITriggerScheduler, DefaultTriggerScheduler>()
.AddScoped<IBookmarkScheduler, DefaultBookmarkScheduler>()
.AddScoped<DefaultWorkflowScheduler>()
.AddScoped(WorkflowScheduler)
.AddBackgroundTask<CreateSchedulesBackgroundTask>()
.AddHandlersFrom<ScheduleWorkflows>()
//Trigger payload validators.
.AddTriggerPayloadValidator<CronTriggerPayloadValidator, CronTriggerPayload>();
}
}

View file

@ -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;
/// <summary>
/// Provides multi-tenant HTTP routing features.
/// </summary>
[ShellFeature(
DisplayName = "Multi-tenant HTTP Routing",
Description = "Provides multi-tenant HTTP routing capabilities for workflows")]
[UsedImplicitly]
public class MultitenantHttpRoutingFeature : IShellFeature
{
/// <summary>
/// Gets or sets the tenant header name.
/// </summary>
public string TenantHeaderName { get; set; } = "X-Tenant-Id";
public void ConfigureServices(IServiceCollection services)
{
// Multitenancy HTTP options.
services.Configure<MultitenancyHttpOptions>(options =>
{
options.TenantHeaderName = TenantHeaderName;
});
// Tenant resolvers.
services
.AddScoped<ITenantResolver, RoutePrefixTenantResolver>()
.AddScoped<ITenantResolver, HeaderTenantResolver>()
.AddScoped<ITenantResolver, HostTenantResolver>()
.AddScoped<TenantPrefixHttpEndpointRoutesProvider>();
}
}

View file

@ -0,0 +1,21 @@
using CShells.Features;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Tenants.ShellFeatures;
/// <summary>
/// Enables tenant management endpoints.
/// </summary>
[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
}
}

View file

@ -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;
/// <summary>
/// Enables tenant management capabilities.
/// </summary>
[ShellFeature(
DisplayName = "Tenant Management",
Description = "Provides tenant store and management capabilities")]
[UsedImplicitly]
public class TenantManagementFeature : IShellFeature
{
/// <summary>
/// A factory that instantiates an <see cref="ITenantStore"/>.
/// </summary>
public Func<IServiceProvider, ITenantStore> TenantStoreFactory { get; set; } = sp => sp.GetRequiredService<MemoryTenantStore>();
public void ConfigureServices(IServiceCollection services)
{
services
.AddMemoryStore<Tenant, MemoryTenantStore>()
.AddScoped(TenantStoreFactory);
}
}

View file

@ -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;
/// <summary>
/// Configures multi-tenancy features.
/// </summary>
[ShellFeature(
DisplayName = "Tenants",
Description = "Provides multi-tenancy capabilities for workflows")]
[UsedImplicitly]
public class TenantsFeature : IShellFeature
{
public void ConfigureServices(IServiceCollection services)
{
services
.AddHostedService<SetupMediatorPipelines>()
.AddScoped<ITenantResolverPipelineInvoker, DefaultTenantResolverPipelineInvoker>()
.AddScoped<ITenantResolver, DefaultTenantResolver>();
}
}

View file

@ -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;
/// <summary>
/// A feature that enables ElsaScript support for the BlobStorage workflow provider.
/// </summary>
[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<IBlobWorkflowFormatHandler, ElsaScriptBlobWorkflowFormatHandler>();
}
}

View file

@ -0,0 +1,53 @@
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;
/// <summary>
/// A feature that enables the FluentStorage workflow definition provider.
/// </summary>
[ShellFeature(
DisplayName = "Blob Storage Workflow Provider",
Description = "Provides workflow definitions from blob storage",
DependsOn = ["WorkflowManagement"])]
[UsedImplicitly]
public class BlobStorageFeature : IShellFeature
{
/// <summary>
/// The blob storage to use.
/// </summary>
public Func<IServiceProvider, IBlobStorage> BlobStorage { get; set; } = _ => StorageFactory.Blobs.DirectoryFiles(GetDefaultWorkflowsDirectory());
public void ConfigureServices(IServiceCollection services)
{
services.AddScoped<IBlobStorageProvider>(sp => new BlobStorageProvider(BlobStorage(sp)));
// Register the JSON format handler (built-in support)
services.AddScoped<IBlobWorkflowFormatHandler, JsonBlobWorkflowFormatHandler>();
services.AddScoped<BlobStorageWorkflowsProvider>();
services.AddScoped<IWorkflowsProvider>(sp => sp.GetRequiredService<BlobStorageWorkflowsProvider>());
}
/// <summary>
/// Gets the default workflows directory.
/// </summary>
public static string GetDefaultWorkflowsDirectory()
{
var entryAssembly = Assembly.GetEntryAssembly();
var entryAssemblyDir = entryAssembly != null
? Path.GetDirectoryName(entryAssembly.Location)
: AppContext.BaseDirectory;
var directory = Path.Combine(entryAssemblyDir!, "Workflows");
return directory;
}
}

Some files were not shown because too many files have changed in this diff Show more