From c60e63c3c674c575251fd4e15a2edbecb8e2c942 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Thu, 12 Feb 2026 20:30:02 +0100 Subject: [PATCH] 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> --- Directory.Packages.props | 21 +- Elsa.sln | 7 + NuGet.Config | 5 + .../Elsa.ModularServer.Web.csproj | 13 + .../Elsa.ModularServer.Web/FodyWeavers.xml | 3 + src/apps/Elsa.ModularServer.Web/Program.cs | 25 ++ .../Properties/launchSettings.json | 23 ++ .../appsettings.Development.json | 8 + .../appsettings.Example.json | 76 +++++ .../Elsa.ModularServer.Web/appsettings.json | 55 ++++ src/apps/Elsa.Server.Web/Program.cs | 1 - .../Elsa.Api.Common/Elsa.Api.Common.csproj | 2 + .../ElsaFastEndpointsConfigurator.cs | 50 ++++ .../ShellFeatures/ElsaFastEndpointsFeature.cs | 22 ++ .../Contracts/IInstalledFeatureProvider.cs | 28 ++ src/common/Elsa.Features/Elsa.Features.csproj | 1 + .../Elsa.Features/Implementations/Module.cs | 1 + .../Services/InstalledFeatureProvider.cs | 30 ++ .../Services/ShellInstalledFeatureProvider.cs | 70 +++++ .../ShellFeatures/AlterationsFeature.cs | 56 ++++ .../ShellFeatures/MemoryCacheFeature.cs | 23 ++ .../DependencyInjectionExtensions.cs | 75 ++--- .../ShellFeatures/DefaultFormattersFeature.cs | 17 ++ .../ShellFeatures/MediatorFeature.cs | 18 ++ .../ShellFeatures/MultitenancyFeature.cs | 42 +++ .../ShellFeatures/StringCompressionFeature.cs | 21 ++ .../ShellFeatures/SystemClockFeature.cs | 17 ++ .../ShellHandlers/ActivateShellTenants.cs | 19 ++ .../ShellFeatures/ElsaScriptFeature.cs | 28 ++ .../ShellFeatures/CSharpFeature.cs | 46 +++ .../JavaScriptLibraryFeatures.cs | 76 +++++ .../ShellFeatures/JavaScriptFeature.cs | 70 +++++ .../ShellFeatures/LiquidFeature.cs | 50 ++++ .../ShellFeatures/PythonFeature.cs | 50 ++++ .../ShellFeatures/ExpressionsFeature.cs | 20 ++ .../ShellFeatures/ClusteringFeature.cs | 42 +++ .../ShellFeatures/HttpCacheFeature.cs | 28 ++ .../Elsa.Http/ShellFeatures/HttpFeature.cs | 211 ++++++++++++++ .../ShellFeatures/HttpJavaScriptFeature.cs | 23 ++ .../Elsa.Identity/Elsa.Identity.csproj | 1 + .../DefaultAuthenticationFeature.cs | 73 +++++ .../ShellFeatures/IdentityFeature.cs | 119 ++++++++ .../ShellFeatures/KeyValueFeature.cs | 27 ++ .../ShellFeatures/LabelsFeature.cs | 42 +++ .../PersistenceShellFeatureBase.cs | 123 +++++++++ ...MySqlAlterationsPersistenceShellFeature.cs | 26 ++ .../MySqlIdentityPersistenceShellFeature.cs | 26 ++ .../MySqlLabelPersistenceShellFeature.cs | 26 ++ ...rkflowDefinitionPersistenceShellFeature.cs | 26 ++ ...WorkflowInstancePersistenceShellFeature.cs | 26 ++ .../MySqlWorkflowPersistenceShellFeature.cs | 20 ++ ...lWorkflowRuntimePersistenceShellFeature.cs | 26 ++ .../MySqlTenantPersistenceShellFeature.cs | 26 ++ ...racleAlterationsPersistenceShellFeature.cs | 26 ++ .../OracleIdentityPersistenceShellFeature.cs | 26 ++ .../OracleLabelPersistenceShellFeature.cs | 26 ++ ...rkflowDefinitionPersistenceShellFeature.cs | 26 ++ ...WorkflowInstancePersistenceShellFeature.cs | 26 ++ .../OracleWorkflowPersistenceShellFeature.cs | 20 ++ ...eWorkflowRuntimePersistenceShellFeature.cs | 26 ++ .../OracleTenantPersistenceShellFeature.cs | 26 ++ ...reSqlAlterationsPersistenceShellFeature.cs | 26 ++ ...stgreSqlIdentityPersistenceShellFeature.cs | 26 ++ .../PostgreSqlLabelPersistenceShellFeature.cs | 26 ++ ...rkflowDefinitionPersistenceShellFeature.cs | 26 ++ ...WorkflowInstancePersistenceShellFeature.cs | 26 ++ ...stgreSqlWorkflowPersistenceShellFeature.cs | 20 ++ ...lWorkflowRuntimePersistenceShellFeature.cs | 26 ++ ...PostgreSqlTenantPersistenceShellFeature.cs | 26 ++ ...erverAlterationsPersistenceShellFeature.cs | 26 ++ ...qlServerIdentityPersistenceShellFeature.cs | 26 ++ .../SqlServerLabelPersistenceShellFeature.cs | 26 ++ ...rkflowDefinitionPersistenceShellFeature.cs | 26 ++ ...WorkflowInstancePersistenceShellFeature.cs | 26 ++ ...rWorkflowRuntimePersistenceShellFeature.cs | 26 ++ .../SqlServerTenantPersistenceShellFeature.cs | 26 ++ ...qliteAlterationsPersistenceShellFeature.cs | 34 +++ .../SqliteIdentityPersistenceShellFeature.cs | 34 +++ .../SqliteLabelPersistenceShellFeature.cs | 34 +++ ...rkflowDefinitionPersistenceShellFeature.cs | 34 +++ ...WorkflowInstancePersistenceShellFeature.cs | 34 +++ ...eWorkflowRuntimePersistenceShellFeature.cs | 34 +++ .../SqliteWorkflowPersistenceShellFeature.cs | 20 ++ .../SqliteTenantPersistenceShellFeature.cs | 34 +++ .../Modules/Alterations/ShellFeature.cs | 23 ++ .../Modules/Identity/ShellFeature.cs | 24 ++ .../Modules/Labels/ShellFeature.cs | 22 ++ ...rkflowDefinitionPersistenceShellFeature.cs | 20 ++ ...WorkflowInstancePersistenceShellFeature.cs | 20 ++ .../WorkflowRuntimePersistenceShellFeature.cs | 33 +++ .../Modules/Tenants/ShellFeature.cs | 20 ++ .../ShellFeatures/ResilienceShellFeature.cs | 47 ++++ .../ShellFeatures/SasTokensFeature.cs | 27 ++ .../ShellFeatures/SchedulingFeature.cs | 55 ++++ .../MultitenantHttpRoutingFeature.cs | 40 +++ .../TenantManagementEndpointsFeature.cs | 21 ++ .../ShellFeatures/TenantManagementFeature.cs | 30 ++ .../ShellFeatures/TenantsFeature.cs | 27 ++ .../ElsaScriptBlobStorageFeature.cs | 25 ++ .../ShellFeatures/BlobStorageFeature.cs | 53 ++++ .../Elsa.Workflows.Api.csproj | 4 + .../Endpoints/Features/Get/Endpoint.cs | 14 +- .../Endpoints/Features/List/Endpoint.cs | 12 +- .../Execute/PostEndpoint.cs | 2 +- .../RealTimeWorkflowUpdatesFeature.cs | 24 ++ .../ShellFeatures/WorkflowsApiFeature.cs | 46 +++ .../ShellFeatures/CommitStrategiesFeature.cs | 17 ++ .../ShellFeatures/FlowchartFeature.cs | 35 +++ .../ShellFeatures/WorkflowsFeature.cs | 179 ++++++++++++ .../CachingWorkflowDefinitionsFeature.cs | 28 ++ .../WorkflowDefinitionsFeature.cs | 21 ++ .../ShellFeatures/WorkflowInstancesFeature.cs | 21 ++ .../WorkflowManagementFeature.cs | 159 +++++++++++ .../Stores/CachingWorkflowDefinitionStore.cs | 2 +- .../DistributedRuntimeFeature.cs | 26 ++ .../CachingWorkflowRuntimeFeature.cs | 31 +++ .../ShellFeatures/WorkflowRuntimeFeature.cs | 261 ++++++++++++++++++ src/modules/Elsa/Elsa.csproj | 14 +- src/modules/Elsa/ShellFeatures/ElsaFeature.cs | 20 ++ 119 files changed, 4134 insertions(+), 73 deletions(-) create mode 100644 src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj create mode 100644 src/apps/Elsa.ModularServer.Web/FodyWeavers.xml create mode 100644 src/apps/Elsa.ModularServer.Web/Program.cs create mode 100644 src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json create mode 100644 src/apps/Elsa.ModularServer.Web/appsettings.Development.json create mode 100644 src/apps/Elsa.ModularServer.Web/appsettings.Example.json create mode 100644 src/apps/Elsa.ModularServer.Web/appsettings.json create mode 100644 src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs create mode 100644 src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs create mode 100644 src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs create mode 100644 src/common/Elsa.Features/Services/InstalledFeatureProvider.cs create mode 100644 src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs create mode 100644 src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs create mode 100644 src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs create mode 100644 src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs create mode 100644 src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs create mode 100644 src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs create mode 100644 src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs create mode 100644 src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs create mode 100644 src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs create mode 100644 src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs create mode 100644 src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs create mode 100644 src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs create mode 100644 src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs create mode 100644 src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs create mode 100644 src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs create mode 100644 src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs create mode 100644 src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs create mode 100644 src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs create mode 100644 src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs create mode 100644 src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs create mode 100644 src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs create mode 100644 src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs create mode 100644 src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs create mode 100644 src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs create mode 100644 src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs create mode 100644 src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs create mode 100644 src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs create mode 100644 src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs create mode 100644 src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs create mode 100644 src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs create mode 100644 src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs create mode 100644 src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs create mode 100644 src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs create mode 100644 src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs create mode 100644 src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs create mode 100644 src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs create mode 100644 src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs create mode 100644 src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs create mode 100644 src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs create mode 100644 src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs create mode 100644 src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs create mode 100644 src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs create mode 100644 src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs create mode 100644 src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs create mode 100644 src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs create mode 100644 src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs create mode 100644 src/modules/Elsa/ShellFeatures/ElsaFeature.cs diff --git a/Directory.Packages.props b/Directory.Packages.props index ca4477da2..8709d3947 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -5,6 +5,9 @@ + + + @@ -31,18 +34,21 @@ - - - + + + + + + @@ -94,14 +100,17 @@ + + + + + + - - - diff --git a/Elsa.sln b/Elsa.sln index 6602771a4..c043709ee 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -327,6 +327,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issue_templates", "issue_te EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "dsl", "dsl", "{477C2416-312D-46AE-BCD6-8FA1FAB43624}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.ModularServer.Web", "src\apps\Elsa.ModularServer.Web\Elsa.ModularServer.Web.csproj", "{E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}" +EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Resilience.Core.UnitTests", "test\unit\Elsa.Resilience.Core.UnitTests\Elsa.Resilience.Core.UnitTests.csproj", "{B8006D70-1630-43DB-A043-FA89FAC70F37}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Common.UnitTests", "test\unit\Elsa.Common.UnitTests\Elsa.Common.UnitTests.csproj", "{A3C07D5B-2A30-494E-B9BC-4B1594B31ABC}" @@ -591,6 +593,10 @@ Global {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48}.Debug|Any CPU.Build.0 = Debug|Any CPU {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48}.Release|Any CPU.ActiveCfg = Release|Any CPU {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48}.Release|Any CPU.Build.0 = Release|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC}.Release|Any CPU.Build.0 = Release|Any CPU {B8006D70-1630-43DB-A043-FA89FAC70F37}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B8006D70-1630-43DB-A043-FA89FAC70F37}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8006D70-1630-43DB-A043-FA89FAC70F37}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -708,6 +714,7 @@ Global {2B7FB49D-E4B6-4AD5-981B-3D85B94F6F48} = {B08B4E00-C2AB-48F3-8389-449F42AEF179} {477C2416-312D-46AE-BCD6-8FA1FAB43624} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79} {874F5A44-DB06-47AB-A18C-2D13942E0147} = {477C2416-312D-46AE-BCD6-8FA1FAB43624} + {E9CA9A0B-6180-4CA7-814C-FA60D1F1B6EC} = {D92BEAB2-60D6-4BB4-885A-6BA681C6CCF1} {B8006D70-1630-43DB-A043-FA89FAC70F37} = {18453B51-25EB-4317-A4B3-B10518252E92} {A3C07D5B-2A30-494E-B9BC-4B1594B31ABC} = {18453B51-25EB-4317-A4B3-B10518252E92} {8C4F6A2D-1E9F-4B3C-9D8E-7F5A6B4C3D2E} = {1B8D5897-902E-4632-8698-E89CAF3DDF54} diff --git a/NuGet.Config b/NuGet.Config index 54f245bd3..d188f6e5a 100644 --- a/NuGet.Config +++ b/NuGet.Config @@ -4,10 +4,15 @@ + + + + + \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj new file mode 100644 index 000000000..d2f791b73 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/Elsa.ModularServer.Web.csproj @@ -0,0 +1,13 @@ + + + + + + + + + + + + + diff --git a/src/apps/Elsa.ModularServer.Web/FodyWeavers.xml b/src/apps/Elsa.ModularServer.Web/FodyWeavers.xml new file mode 100644 index 000000000..00e1d9a1c --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/Program.cs b/src/apps/Elsa.ModularServer.Web/Program.cs new file mode 100644 index 000000000..a5251a00f --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/Program.cs @@ -0,0 +1,25 @@ +using CShells.AspNetCore.Configuration; +using CShells.AspNetCore.Extensions; + +var builder = WebApplication.CreateBuilder(args); +var services = builder.Services; + +// Configure CShells for multi-tenancy with ASP.NET Core integration +// This automatically registers shell-aware authentication and authorization providers +builder.AddShells(shells => shells + .WithWebRouting(options => options.EnablePathRouting = true) + .WithAuthenticationAndAuthorization()); +services.AddHealthChecks(); + +// Add minimal authentication and authorization services in root +// These are required for middleware validation - shells provide the actual configurations +services.AddAuthentication(); +services.AddAuthorization(); + +var app = builder.Build(); + +app.MapHealthChecks("/"); +app.MapShells(); // Sets HttpContext.RequestServices to shell's scoped provider +app.UseAuthentication(); // Runs after MapShells to access shell-specific auth schemes +app.UseAuthorization(); // Runs after MapShells to access shell-specific policies +app.Run(); \ No newline at end of file diff --git a/src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json b/src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json new file mode 100644 index 000000000..49af15d91 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/Properties/launchSettings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "http://localhost:5002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "applicationUrl": "https://localhost:7293;http://localhost:5002", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.Development.json b/src/apps/Elsa.ModularServer.Web/appsettings.Development.json new file mode 100644 index 000000000..0c208ae91 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.Example.json b/src/apps/Elsa.ModularServer.Web/appsettings.Example.json new file mode 100644 index 000000000..86ca822c4 --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/appsettings.Example.json @@ -0,0 +1,76 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "CShells": { + "Shells": [ + { + "Name": "Default", + "Properties": { + "WebRouting": { + "Path": "" + } + }, + "Settings": { + "FastEndpoints": { + "GlobalRoutePrefix": "elsa/api" + }, + "SqliteWorkflowDefinitionPersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + }, + "SqliteWorkflowInstancePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared" + }, + "SqliteWorkflowRuntimePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared" + } + }, + "Features": [ + "Elsa", + "WorkflowsApi", + "Identity", + "DefaultAuthentication", + "SqliteWorkflowDefinitionPersistence", + "SqliteWorkflowInstancePersistence", + "SqliteWorkflowRuntimePersistence" + ] + }, + { + "Name": "Tenant1", + "Properties": { + "WebRouting": { + "Path": "tenant1" + } + }, + "Settings": { + "FastEndpoints": { + "GlobalRoutePrefix": "api" + }, + "SqlServerWorkflowDefinitionPersistence": { + "ConnectionString": "${ConnectionStrings:Tenant1}" + }, + "SqlServerWorkflowInstancePersistence": { + "ConnectionString": "${ConnectionStrings:Tenant1}" + } + }, + "Features": [ + "Elsa", + "WorkflowsApi", + "SqlServerWorkflowDefinitionPersistence", + "SqlServerWorkflowInstancePersistence" + ] + } + ] + }, + "ConnectionStrings": { + "Tenant1": "Server=localhost;Database=ElsaTenant1;Integrated Security=true;TrustServerCertificate=true" + } +} diff --git a/src/apps/Elsa.ModularServer.Web/appsettings.json b/src/apps/Elsa.ModularServer.Web/appsettings.json new file mode 100644 index 000000000..e3ca4a7be --- /dev/null +++ b/src/apps/Elsa.ModularServer.Web/appsettings.json @@ -0,0 +1,55 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*", + "CShells": { + "Shells": [ + { + "Name": "Default", + "Properties": { + "WebRouting": { + "Path": "foo" + } + }, + "Settings": { + "FastEndpoints": { + "GlobalRoutePrefix": "elsa/api" + }, + "SqliteWorkflowDefinitionPersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + }, + "SqliteWorkflowInstancePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + }, + "SqliteWorkflowRuntimePersistence": { + "ConnectionString": "Data Source=elsa_workflows.db;Cache=Shared", + "DbContextOptions": { + "EnableSensitiveDataLogging": false, + "EnableDetailedErrors": false + } + } + }, + "Features": [ + "Elsa", + "WorkflowsApi", + "Identity", + "DefaultAuthentication", + "Resilience", + "SqliteWorkflowPersistence" + ] + } + ] + } +} diff --git a/src/apps/Elsa.Server.Web/Program.cs b/src/apps/Elsa.Server.Web/Program.cs index a1a8de9c7..c020fcbfb 100644 --- a/src/apps/Elsa.Server.Web/Program.cs +++ b/src/apps/Elsa.Server.Web/Program.cs @@ -42,7 +42,6 @@ var configuration = builder.Configuration; var identitySection = configuration.GetSection("Identity"); var identityTokenSection = identitySection.GetSection("Tokens"); -// Add Elsa services. services .AddElsa(elsa => { diff --git a/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj b/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj index 8288f16c2..ff63d1517 100644 --- a/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj +++ b/src/common/Elsa.Api.Common/Elsa.Api.Common.csproj @@ -9,6 +9,8 @@ + + diff --git a/src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs b/src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs new file mode 100644 index 000000000..947399a20 --- /dev/null +++ b/src/common/Elsa.Api.Common/FastEndpointConfigurators/ElsaFastEndpointsConfigurator.cs @@ -0,0 +1,50 @@ +using System.Globalization; +using System.Text.Json; +using System.Text.Json.Serialization; +using CShells.FastEndpoints.Contracts; +using Elsa.Workflows; +using FastEndpoints; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.FastEndpointConfigurators; + +/// +/// Configures FastEndpoints with Elsa-specific serialization options. +/// Uses the same serialization settings as . +/// +[UsedImplicitly] +public class ElsaFastEndpointsConfigurator : IFastEndpointsConfigurator +{ + /// + public void Configure(Config config) + { + config.Serializer.RequestDeserializer = DeserializeRequestAsync; + config.Serializer.ResponseSerializer = SerializeResponseAsync; + + config.Binding.ValueParserFor(s => + new(DateTimeOffset.TryParse(s.ToString(), CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind, out var result), result)); + } + + private static ValueTask DeserializeRequestAsync(HttpRequest httpRequest, Type modelType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken) + { + var serializer = httpRequest.HttpContext.RequestServices.GetRequiredService(); + var options = serializer.GetOptions(); + + return serializerContext == null + ? JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, options, cancellationToken) + : JsonSerializer.DeserializeAsync(httpRequest.Body, modelType, serializerContext, cancellationToken); + } + + private static Task SerializeResponseAsync(HttpResponse httpResponse, object? dto, string contentType, JsonSerializerContext? serializerContext, CancellationToken cancellationToken) + { + var serializer = httpResponse.HttpContext.RequestServices.GetRequiredService(); + var options = serializer.GetOptions(); + + httpResponse.ContentType = contentType; + return serializerContext == null + ? JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto?.GetType() ?? typeof(object), options, cancellationToken) + : JsonSerializer.SerializeAsync(httpResponse.Body, dto, dto?.GetType() ?? typeof(object), serializerContext, cancellationToken); + } +} diff --git a/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs b/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs new file mode 100644 index 000000000..3782b9ad2 --- /dev/null +++ b/src/common/Elsa.Api.Common/ShellFeatures/ElsaFastEndpointsFeature.cs @@ -0,0 +1,22 @@ +using CShells.FastEndpoints.Contracts; +using CShells.Features; +using Elsa.FastEndpointConfigurators; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.ShellFeatures; + +/// +/// Registers the Elsa-specific FastEndpoints configurator. +/// This feature should be enabled alongside the CShells FastEndpointsFeature to apply Elsa's serialization settings. +/// +[ShellFeature(DependsOn = ["FastEndpoints"])] +[UsedImplicitly] +public class ElsaFastEndpointsFeature : IShellFeature +{ + /// + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + } +} diff --git a/src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs b/src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs new file mode 100644 index 000000000..926b377dc --- /dev/null +++ b/src/common/Elsa.Features/Contracts/IInstalledFeatureProvider.cs @@ -0,0 +1,28 @@ +using Elsa.Features.Models; + +namespace Elsa.Features.Contracts; + +/// +/// Provides read-only access to installed features. +/// +/// +/// This interface is used to query the list of features that are installed and available in the application. +/// Unlike , this interface does not provide write operations, +/// making it suitable for implementations where features are discovered automatically (e.g., from shell features) +/// rather than manually registered. +/// +public interface IInstalledFeatureProvider +{ + /// + /// Gets all installed features. + /// + /// All installed features. + IEnumerable List(); + + /// + /// Finds a feature descriptor by its full name. + /// + /// The full name of the feature. + /// The feature descriptor or null if not found. + FeatureDescriptor? Find(string fullName); +} diff --git a/src/common/Elsa.Features/Elsa.Features.csproj b/src/common/Elsa.Features/Elsa.Features.csproj index 10a3eaeb8..1f2078410 100644 --- a/src/common/Elsa.Features/Elsa.Features.csproj +++ b/src/common/Elsa.Features/Elsa.Features.csproj @@ -8,6 +8,7 @@ + diff --git a/src/common/Elsa.Features/Implementations/Module.cs b/src/common/Elsa.Features/Implementations/Module.cs index 4821e91d7..ba9cc1d2e 100644 --- a/src/common/Elsa.Features/Implementations/Module.cs +++ b/src/common/Elsa.Features/Implementations/Module.cs @@ -127,6 +127,7 @@ public class Module : IModule } Services.AddSingleton(registry); + Services.AddSingleton(sp => new InstalledFeatureProvider(sp.GetRequiredService())); } private IEnumerable ExcludeFeaturesWithMissingDependencies(IEnumerable features) diff --git a/src/common/Elsa.Features/Services/InstalledFeatureProvider.cs b/src/common/Elsa.Features/Services/InstalledFeatureProvider.cs new file mode 100644 index 000000000..bbba02882 --- /dev/null +++ b/src/common/Elsa.Features/Services/InstalledFeatureProvider.cs @@ -0,0 +1,30 @@ +using Elsa.Features.Contracts; +using Elsa.Features.Models; + +namespace Elsa.Features.Services; + +/// +/// Legacy implementation of that delegates to . +/// +/// +/// This implementation is used when features are manually registered using the legacy feature system. +/// For shell-based feature discovery, use ShellInstalledFeatureProvider instead. +/// +public class InstalledFeatureProvider : IInstalledFeatureProvider +{ + private readonly IInstalledFeatureRegistry _registry; + + /// + /// Initializes a new instance of the class. + /// + public InstalledFeatureProvider(IInstalledFeatureRegistry registry) + { + _registry = registry; + } + + /// + public IEnumerable List() => _registry.List(); + + /// + public FeatureDescriptor? Find(string fullName) => _registry.Find(fullName); +} diff --git a/src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs b/src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs new file mode 100644 index 000000000..506ce6fff --- /dev/null +++ b/src/common/Elsa.Features/Services/ShellInstalledFeatureProvider.cs @@ -0,0 +1,70 @@ +using CShells.Features; +using Elsa.Features.Contracts; +using Elsa.Features.Models; + +namespace Elsa.Features.Services; + +/// +/// Shell-based implementation of that reads from CShells feature descriptors. +/// +/// +/// This implementation automatically discovers features from the shell's feature descriptors, +/// mapping them to Elsa's model. Features are read from the +/// shell's DI container where they were registered during shell initialization. +/// +public class ShellInstalledFeatureProvider : IInstalledFeatureProvider +{ + private readonly IEnumerable _shellFeatures; + + /// + /// Initializes a new instance of the class. + /// + public ShellInstalledFeatureProvider(IEnumerable shellFeatures) + { + _shellFeatures = shellFeatures; + } + + /// + public IEnumerable List() + { + return _shellFeatures + .Where(sf => sf.StartupType != null) + .Select(MapToElsaFeatureDescriptor); + } + + /// + public FeatureDescriptor? Find(string fullName) + { + var shellFeature = _shellFeatures + .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}"; + } +} diff --git a/src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs b/src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs new file mode 100644 index 000000000..cb8674b42 --- /dev/null +++ b/src/modules/Elsa.Alterations/ShellFeatures/AlterationsFeature.cs @@ -0,0 +1,56 @@ +using CShells.Features; +using Elsa.Alterations.Core.Contracts; +using Elsa.Alterations.Core.Entities; +using Elsa.Alterations.Core.Extensions; +using Elsa.Alterations.Core.Stores; +using Elsa.Alterations.Extensions; +using Elsa.Alterations.Services; +using Elsa.Extensions; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Alterations.ShellFeatures; + +/// +/// Adds the Elsa alterations services. +/// +[ShellFeature( + DisplayName = "Alterations", + Description = "Provides workflow alteration capabilities for modifying running workflow instances")] +[UsedImplicitly] +public class AlterationsFeature : IShellFeature +{ + /// + /// Gets or sets the factory for the alteration plan store. + /// + public Func AlterationPlanStoreFactory { get; set; } = sp => sp.GetRequiredService(); + + /// + /// Gets or sets the factory for the alteration job store. + /// + public Func AlterationJobStoreFactory { get; set; } = sp => sp.GetRequiredService(); + + /// + /// Gets or sets the factory for the alteration job dispatcher. + /// + public Func AlterationJobDispatcherFactory { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + services.AddAlterations(); + services.AddAlterationsCore(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + services.AddMemoryStore(); + services.AddMemoryStore(); + + services.AddScoped(AlterationPlanStoreFactory); + services.AddScoped(AlterationJobStoreFactory); + services.AddScoped(AlterationJobDispatcherFactory); + } +} + diff --git a/src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs b/src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs new file mode 100644 index 000000000..6564c76dc --- /dev/null +++ b/src/modules/Elsa.Caching/ShellFeatures/MemoryCacheFeature.cs @@ -0,0 +1,23 @@ +using CShells.Features; +using Elsa.Caching.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Caching.ShellFeatures; + +/// +/// Configures the MemoryCache. +/// +[ShellFeature] +[UsedImplicitly] +public class MemoryCacheFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryCache() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + } +} diff --git a/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs b/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs index b4c8a42b2..473aa01e4 100644 --- a/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs +++ b/src/modules/Elsa.Common/Extensions/DependencyInjectionExtensions.cs @@ -12,43 +12,46 @@ namespace Elsa.Extensions; /// public static class DependencyInjectionExtensions { - /// - /// Adds a memory store for the specified entity. - /// - public static IServiceCollection AddMemoryStore(this IServiceCollection services) where TStore : class + extension(IServiceCollection services) { - services.TryAddSingleton>(); - services.TryAddScoped(); - return services; - } + /// + /// Adds a memory store for the specified entity. + /// + public IServiceCollection AddMemoryStore() where TStore : class + { + services.TryAddSingleton>(); + services.TryAddScoped(); + return services; + } - /// - /// Adds a serialization options configurator for the specified type. - /// - public static IServiceCollection AddSerializationOptionsConfigurator(this IServiceCollection services) where T : class, ISerializationOptionsConfigurator - { - services.AddSingleton(); - return services; - } - - public static IServiceCollection AddStartupTask(this IServiceCollection services) where T : class, IStartupTask - { - return services.AddScoped(); - } - - public static IServiceCollection AddBackgroundTask(this IServiceCollection services) where T : class, IBackgroundTask - { - return services.AddScoped(); - } - - public static IServiceCollection AddRecurringTask(this IServiceCollection services) where T : class, IRecurringTask - { - return services.AddScoped(); - } - - public static IServiceCollection AddRecurringTask(this IServiceCollection services, TimeSpan interval) where T : class, IRecurringTask - { - services.Configure(options => options.Schedule.ConfigureTask(interval)); - return services.AddRecurringTask(); + /// + /// Adds a serialization options configurator for the specified type. + /// + public IServiceCollection AddSerializationOptionsConfigurator() where T : class, ISerializationOptionsConfigurator + { + services.AddSingleton(); + return services; + } + + public IServiceCollection AddStartupTask() where T : class, IStartupTask + { + return services.AddScoped(); + } + + public IServiceCollection AddBackgroundTask() where T : class, IBackgroundTask + { + return services.AddScoped(); + } + + public IServiceCollection AddRecurringTask() where T : class, IRecurringTask + { + return services.AddScoped(); + } + + public IServiceCollection AddRecurringTask(TimeSpan interval) where T : class, IRecurringTask + { + services.Configure(options => options.Schedule.ConfigureTask(interval)); + return services.AddRecurringTask(); + } } } \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs b/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs new file mode 100644 index 000000000..be1213da8 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/DefaultFormattersFeature.cs @@ -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(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs b/src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs new file mode 100644 index 000000000..43e2ed7db --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/MediatorFeature.cs @@ -0,0 +1,18 @@ +using CShells.Features; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +/// +/// Adds and configures the Mediator feature. +/// +[ShellFeature("Mediator")] +public class MediatorFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddMediator() + .AddMediatorHostedServices(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs new file mode 100644 index 000000000..dc2c02343 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/MultitenancyFeature.cs @@ -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 _tenantsProviderFactory = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + + // TenantTaskManager handles all task lifecycle in the correct order + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) + + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(_tenantsProviderFactory) + + .AddSingleton() + .AddSingleton() + ; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs b/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs new file mode 100644 index 000000000..9a0b4ed59 --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/StringCompressionFeature.cs @@ -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() + .AddSingleton() + .AddSingleton() + .AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs b/src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs new file mode 100644 index 000000000..0ef670e2b --- /dev/null +++ b/src/modules/Elsa.Common/ShellFeatures/SystemClockFeature.cs @@ -0,0 +1,17 @@ +using CShells.Features; +using Elsa.Common.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.ShellFeatures; + +/// +/// Configures the system clock. +/// +[ShellFeature("SystemClock")] +public class SystemClockFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs b/src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs new file mode 100644 index 000000000..39f0fd0f7 --- /dev/null +++ b/src/modules/Elsa.Common/ShellHandlers/ActivateShellTenants.cs @@ -0,0 +1,19 @@ +using CShells.Hosting; +using Elsa.Common.Multitenancy; +using JetBrains.Annotations; + +namespace Elsa.Common.ShellHandlers; + +[UsedImplicitly] +public class ActivateShellTenants(ITenantService tenantService) : IShellActivatedHandler, IShellDeactivatingHandler +{ + public Task OnActivatedAsync(CancellationToken cancellationToken = default) + { + return tenantService.ActivateTenantsAsync(cancellationToken); + } + + public Task OnDeactivatingAsync(CancellationToken cancellationToken = default) + { + return tenantService.DeactivateTenantsAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs b/src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs new file mode 100644 index 000000000..7d3959f67 --- /dev/null +++ b/src/modules/Elsa.Dsl.ElsaScript/ShellFeatures/ElsaScriptFeature.cs @@ -0,0 +1,28 @@ +using CShells.Features; +using Elsa.Dsl.ElsaScript.Compiler; +using Elsa.Dsl.ElsaScript.Contracts; +using Elsa.Dsl.ElsaScript.Materializers; +using Elsa.Dsl.ElsaScript.Parser; +using Elsa.Workflows.Management; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Dsl.ElsaScript.ShellFeatures; + +/// +/// Feature for ElsaScript DSL support. +/// +[ShellFeature( + DisplayName = "ElsaScript DSL", + Description = "Provides ElsaScript DSL support for defining workflows")] +[UsedImplicitly] +public class ElsaScriptFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddScoped(); + services.AddScoped(); + } +} + diff --git a/src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs b/src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs new file mode 100644 index 000000000..04a8e34fb --- /dev/null +++ b/src/modules/Elsa.Expressions.CSharp/ShellFeatures/CSharpFeature.cs @@ -0,0 +1,46 @@ +using CShells.Features; +using Elsa.Expressions.CSharp.Activities; +using Elsa.Expressions.CSharp.Contracts; +using Elsa.Expressions.CSharp.Options; +using Elsa.Expressions.CSharp.Providers; +using Elsa.Expressions.CSharp.Services; +using Elsa.Extensions; +using Elsa.Workflows; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.CSharp.ShellFeatures; + +/// +/// Installs C# integration. +/// +[ShellFeature( + DisplayName = "C# Expressions", + Description = "Provides C# expression evaluation capabilities for workflows", + DependsOn = ["Mediator", "Expressions", "MemoryCache"])] +[UsedImplicitly] +public class CSharpFeature : IShellFeature +{ + /// + /// Configures the . + /// + public Action CSharpOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(CSharpOptions); + + // C# services. + services + .AddExpressionDescriptorProvider() + .AddScoped(); + + // Handlers. + services.AddNotificationHandlersFrom(); + + // UI property handlers. + services.AddScoped(); + } +} + + diff --git a/src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs b/src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs new file mode 100644 index 000000000..c62bdc6d5 --- /dev/null +++ b/src/modules/Elsa.Expressions.JavaScript.Libraries/ShellFeatures/JavaScriptLibraryFeatures.cs @@ -0,0 +1,76 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Expressions.JavaScript.Options; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.JavaScript.Libraries.ShellFeatures; + +/// +/// Base class for JavaScript library shell features. +/// +public abstract class ScriptModuleShellFeatureBase : IShellFeature +{ + private readonly string _moduleName; + + protected ScriptModuleShellFeatureBase(string moduleName) + { + _moduleName = moduleName; + } + + public void ConfigureServices(IServiceCollection services) + { + var moduleName = _moduleName; + services.Configure(options => + { + options.ConfigureEngine((engine, context) => + { + var resourceName = $"Elsa.Expressions.JavaScript.Libraries.ClientLib.dist.{moduleName}.js"; + using var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName)!; + using var reader = new StreamReader(stream); + var script = reader.ReadToEnd(); + engine.Execute(script); + }); + }); + } +} + +/// +/// Adds Lodash library support to JavaScript expressions. +/// +[ShellFeature( + DisplayName = "Lodash JavaScript Library", + Description = "Provides Lodash utility library for JavaScript expressions", + DependsOn = ["JavaScript"])] +[UsedImplicitly] +public class LodashFeature : ScriptModuleShellFeatureBase +{ + public LodashFeature() : base("lodash") { } +} + +/// +/// Adds Lodash FP library support to JavaScript expressions. +/// +[ShellFeature( + DisplayName = "Lodash FP JavaScript Library", + Description = "Provides Lodash FP (functional programming) utility library for JavaScript expressions", + DependsOn = ["JavaScript"])] +[UsedImplicitly] +public class LodashFpFeature : ScriptModuleShellFeatureBase +{ + public LodashFpFeature() : base("lodashFp") { } +} + +/// +/// Adds Moment.js library support to JavaScript expressions. +/// +[ShellFeature( + DisplayName = "Moment JavaScript Library", + Description = "Provides Moment.js date/time library for JavaScript expressions", + DependsOn = ["JavaScript"])] +[UsedImplicitly] +public class MomentFeature : ScriptModuleShellFeatureBase +{ + public MomentFeature() : base("moment") { } +} + diff --git a/src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs b/src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs new file mode 100644 index 000000000..e4cc1fad4 --- /dev/null +++ b/src/modules/Elsa.Expressions.JavaScript/ShellFeatures/JavaScriptFeature.cs @@ -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; + +/// +/// Installs JavaScript integration. +/// +[ShellFeature( + DisplayName = "JavaScript Expressions", + Description = "Provides JavaScript expression evaluation capabilities for workflows", + DependsOn = ["Mediator", "Expressions", "MemoryCache"])] +[UsedImplicitly] +public class JavaScriptFeature : IShellFeature +{ + /// + /// Configures the Jint options. + /// + public Action JintOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(JintOptions); + + // JavaScript services. + services + .AddScoped() + .AddExpressionDescriptorProvider(); + + // Type definition services. + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddFunctionDefinitionProvider() + .AddFunctionDefinitionProvider() + .AddFunctionDefinitionProvider() + .AddTypeDefinitionProvider() + .AddTypeDefinitionProvider() + .AddTypeDefinitionProvider() + .AddVariableDefinitionProvider(); + + // Handlers. + services.AddNotificationHandlersFrom(); + + // Type Script definitions. + services.AddFunctionDefinitionProvider(); + + // UI property handlers. + services.AddScoped(); + + // Hosted services. + services.AddHostedService(); + } +} + + diff --git a/src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs b/src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs new file mode 100644 index 000000000..6702aa2d1 --- /dev/null +++ b/src/modules/Elsa.Expressions.Liquid/ShellFeatures/LiquidFeature.cs @@ -0,0 +1,50 @@ +using CShells.Features; +using Elsa.Expressions.Liquid.Contracts; +using Elsa.Expressions.Liquid.Filters; +using Elsa.Expressions.Liquid.Handlers; +using Elsa.Expressions.Liquid.Options; +using Elsa.Expressions.Liquid.Providers; +using Elsa.Expressions.Liquid.Services; +using Elsa.Extensions; +using Fluid.Filters; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.Liquid.ShellFeatures; + +/// +/// Configures Liquid functionality. +/// +[ShellFeature( + DisplayName = "Liquid Expressions", + Description = "Provides Liquid template expression evaluation capabilities for workflows", + DependsOn = ["MemoryCache", "Mediator", "Expressions"])] +[UsedImplicitly] +public class LiquidFeature : IShellFeature +{ + /// + /// Configures the Fluid options. + /// + public Action FluidOptions { get; set; } = options => + { + options.ConfigureFilters = context => context.Options.Filters + .WithArrayFilters() + .WithStringFilters() + .WithNumberFilters() + .WithMiscFilters(); + }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(FluidOptions); + + services + .AddHandlersFrom() + .AddScoped() + .AddScoped() + .AddExpressionDescriptorProvider() + .AddLiquidFilter("base64") + .AddLiquidFilter("keys"); + } +} + diff --git a/src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs b/src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs new file mode 100644 index 000000000..7b5b38628 --- /dev/null +++ b/src/modules/Elsa.Expressions.Python/ShellFeatures/PythonFeature.cs @@ -0,0 +1,50 @@ +using CShells.Features; +using Elsa.Expressions.Python.Activities; +using Elsa.Expressions.Python.Contracts; +using Elsa.Expressions.Python.HostedServices; +using Elsa.Expressions.Python.Options; +using Elsa.Expressions.Python.Providers; +using Elsa.Expressions.Python.Services; +using Elsa.Extensions; +using Elsa.Workflows; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.Python.ShellFeatures; + +/// +/// Installs Python integration. +/// +[ShellFeature( + DisplayName = "Python Expressions", + Description = "Provides Python expression evaluation capabilities for workflows", + DependsOn = ["Mediator", "Expressions"])] +[UsedImplicitly] +public class PythonFeature : IShellFeature +{ + /// + /// Configures the . + /// + public Action PythonOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(PythonOptions); + + // Python services. + services + .AddScoped() + .AddExpressionDescriptorProvider(); + + // Handlers. + services.AddNotificationHandlersFrom(); + + // UI property handlers. + services.AddScoped(); + + // Hosted services. + services.AddHostedService(); + } +} + + diff --git a/src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs b/src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs new file mode 100644 index 000000000..eccf3af3c --- /dev/null +++ b/src/modules/Elsa.Expressions/ShellFeatures/ExpressionsFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using Elsa.Expressions.Contracts; +using Elsa.Expressions.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Expressions.ShellFeatures; + +/// +/// Installs and configures the expressions feature. +/// +[ShellFeature("Expressions")] +public class ExpressionsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddScoped() + .AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs b/src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs new file mode 100644 index 000000000..fc6eae7f4 --- /dev/null +++ b/src/modules/Elsa.Hosting.Management/ShellFeatures/ClusteringFeature.cs @@ -0,0 +1,42 @@ +using CShells.Features; +using Elsa.Hosting.Management.Contracts; +using Elsa.Hosting.Management.HostedServices; +using Elsa.Hosting.Management.Options; +using Elsa.Hosting.Management.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Hosting.Management.ShellFeatures; + +/// +/// Installs and configures the clustering feature. +/// +[ShellFeature( + DisplayName = "Clustering", + Description = "Provides clustering and heartbeat capabilities for distributed Elsa deployments")] +[UsedImplicitly] +public class ClusteringFeature : IShellFeature +{ + /// + /// A factory that instantiates an . + /// + public Func InstanceNameProvider { get; set; } = sp => + { + return ActivatorUtilities.CreateInstance(sp); + }; + + /// + /// Represents the options for heartbeat feature. + /// + public Action HeartbeatOptions { get; set; } = _ => { }; + + public void ConfigureServices(IServiceCollection services) + { + services.Configure(HeartbeatOptions) + .AddSingleton(InstanceNameProvider) + .AddSingleton() + .AddHostedService() + .AddHostedService(); + } +} + diff --git a/src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs b/src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs new file mode 100644 index 000000000..beb7d69d2 --- /dev/null +++ b/src/modules/Elsa.Http/ShellFeatures/HttpCacheFeature.cs @@ -0,0 +1,28 @@ +using CShells.Features; +using Elsa.Http.Handlers; +using Elsa.Http.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Http.ShellFeatures; + +/// +/// Installs services related to HTTP caching. +/// +[ShellFeature( + DisplayName = "HTTP Cache", + Description = "Provides HTTP workflow caching for improved performance", + DependsOn = ["Http", "CachingWorkflowDefinitions"])] +[UsedImplicitly] +public class HttpCacheFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .Decorate() + .AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs b/src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs new file mode 100644 index 000000000..519590aad --- /dev/null +++ b/src/modules/Elsa.Http/ShellFeatures/HttpFeature.cs @@ -0,0 +1,211 @@ +using CShells.Features; +using Elsa.Expressions.Options; +using Elsa.Extensions; +using Elsa.Http.Bookmarks; +using Elsa.Http.ContentWriters; +using Elsa.Http.DownloadableContentHandlers; +using Elsa.Http.FileCaches; +using Elsa.Http.Handlers; +using Elsa.Http.Options; +using Elsa.Http.Parsers; +using Elsa.Http.PortResolvers; +using Elsa.Http.Selectors; +using Elsa.Http.Services; +using Elsa.Http.Tasks; +using Elsa.Http.TriggerPayloadValidators; +using Elsa.Http.UIHints; +using Elsa.Workflows; +using FluentStorage; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.StaticFiles; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; + +namespace Elsa.Http.ShellFeatures; + +/// +/// Installs services related to HTTP services and activities. +/// +[ShellFeature( + DisplayName = "HTTP", + Description = "Provides HTTP-related activities and services for workflow execution", + DependsOn = ["HttpJavaScript", "Resilience"])] +[UsedImplicitly] +public class HttpFeature : IShellFeature +{ + /// + /// A delegate to configure . + /// + public Action? ConfigureHttpOptions { get; set; } + + /// + /// A delegate to configure . + /// + public Action? ConfigureHttpFileCacheOptions { get; set; } + + /// + /// A delegate that is invoked when authorizing an inbound HTTP request. + /// + public Func HttpEndpointAuthorizationHandler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that is invoked when an HTTP workflow faults. + /// + public Func HttpEndpointWorkflowFaultHandler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate to configure the . + /// + public Func ContentTypeProvider { get; set; } = _ => new FileExtensionContentTypeProvider(); + + /// + /// A delegate to configure the . + /// + public Func FileCache { get; set; } = sp => + { + var options = sp.GetRequiredService>().Value; + var blobStorage = StorageFactory.Blobs.DirectoryFiles(options.LocalCacheDirectory); + return new BlobFileCacheStorageProvider(blobStorage); + }; + + /// + /// A delegate to configure the used when by the and activities. + /// + public Action HttpClient { get; set; } = (_, _) => { }; + + /// + /// A delegate to configure the for . + /// + public Action HttpClientBuilder { get; set; } = _ => { }; + + /// + /// A list of types to register with the service collection. + /// + public ICollection HttpCorrelationIdSelectorTypes { get; } = new List + { + typeof(HeaderHttpCorrelationIdSelector), + typeof(QueryStringHttpCorrelationIdSelector) + }; + + /// + /// A list of types to register with the service collection. + /// + public ICollection HttpWorkflowInstanceIdSelectorTypes { get; } = new List + { + typeof(HeaderHttpWorkflowInstanceIdSelector), + typeof(QueryStringHttpWorkflowInstanceIdSelector) + }; + + public void ConfigureServices(IServiceCollection services) + { + var configureOptions = ConfigureHttpOptions ?? (options => + { + options.BasePath = "/workflows"; + options.BaseUrl = new Uri("http://localhost"); + }); + + var configureFileCacheOptions = ConfigureHttpFileCacheOptions ?? (options => { options.TimeToLive = TimeSpan.FromDays(7); }); + + services.Configure(configureOptions); + services.Configure(configureFileCacheOptions); + + var httpClientBuilder = services.AddHttpClient(HttpClient); + HttpClientBuilder(httpClientBuilder); + + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(ContentTypeProvider) + .AddHttpContextAccessor() + + // Handlers. + .AddNotificationHandler() + + // Content parsers. + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + + // HTTP content factories. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Activity property options providers. + .AddScoped() + .AddScoped() + + // Default providers. + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()) + + // Port resolvers. + .AddScoped() + + // HTTP endpoint handlers. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(HttpEndpointWorkflowFaultHandler) + .AddScoped(HttpEndpointAuthorizationHandler) + .AddScoped(sp => sp.GetRequiredService()) + + // Startup tasks. + .AddStartupTask() + + // Downloadable content handlers. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + //Trigger payload validators. + .AddTriggerPayloadValidator() + + // File caches. + .AddScoped(FileCache) + .AddScoped() + + // AuthenticationBasedHttpEndpointAuthorizationHandler requires Authorization services. + .AddAuthorization(); + + // HTTP clients. + services.AddHttpClient(); + + // Add selectors. + foreach (var httpCorrelationIdSelectorType in HttpCorrelationIdSelectorTypes) + services.AddScoped(typeof(IHttpCorrelationIdSelector), httpCorrelationIdSelectorType); + + foreach (var httpWorkflowInstanceIdSelectorType in HttpWorkflowInstanceIdSelectorTypes) + services.AddScoped(typeof(IHttpWorkflowInstanceIdSelector), httpWorkflowInstanceIdSelectorType); + + services.Configure(options => + { + options.AddTypeAlias("HttpRequest"); + options.AddTypeAlias("HttpResponse"); + options.AddTypeAlias("HttpResponseMessage"); + options.AddTypeAlias("HttpHeaders"); + options.AddTypeAlias("RouteData"); + options.AddTypeAlias("FormFile"); + options.AddTypeAlias("FormFile[]"); + options.AddTypeAlias("HttpFile"); + options.AddTypeAlias("HttpFile[]"); + options.AddTypeAlias("Downloadable"); + options.AddTypeAlias("Downloadable[]"); + }); + } +} + diff --git a/src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs b/src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs new file mode 100644 index 000000000..05d73b0eb --- /dev/null +++ b/src/modules/Elsa.Http/ShellFeatures/HttpJavaScriptFeature.cs @@ -0,0 +1,23 @@ +using CShells.Features; +using Elsa.Http.Scripting.JavaScript; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Http.ShellFeatures; + +/// +/// Provides JavaScript integration for HTTP features. +/// +[ShellFeature( + DisplayName = "HTTP JavaScript", + Description = "Provides JavaScript integration for HTTP activities")] +[UsedImplicitly] +public class HttpJavaScriptFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Identity/Elsa.Identity.csproj b/src/modules/Elsa.Identity/Elsa.Identity.csproj index 7f1bbc5e3..76ecf8edc 100644 --- a/src/modules/Elsa.Identity/Elsa.Identity.csproj +++ b/src/modules/Elsa.Identity/Elsa.Identity.csproj @@ -9,6 +9,7 @@ + diff --git a/src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs b/src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs new file mode 100644 index 000000000..c31783057 --- /dev/null +++ b/src/modules/Elsa.Identity/ShellFeatures/DefaultAuthenticationFeature.cs @@ -0,0 +1,73 @@ +using AspNetCore.Authentication.ApiKey; +using CShells.Features; +using Elsa.Extensions; +using Elsa.Identity.Providers; +using Elsa.Requirements; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Identity.ShellFeatures; + +/// +/// Provides an authorization feature that configures the system with JWT bearer and API key authentication. +/// +[ShellFeature( + DisplayName = "Default Authentication", + Description = "Provides JWT bearer and API key authentication", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class DefaultAuthenticationFeature : IShellFeature +{ + private const string MultiScheme = "Jwt-or-ApiKey"; + + /// + /// Gets or sets the API key provider type. + /// + public Type ApiKeyProviderType { get; set; } = typeof(DefaultApiKeyProvider); + + /// + /// Gets or sets whether to require localhost for the security root policy. + /// + public bool RequireLocalHost { get; set; } = true; + + public void ConfigureServices(IServiceCollection services) + { + services.ConfigureOptions(); + services.ConfigureOptions(); + + var authBuilder = services + .AddAuthentication(MultiScheme) + .AddPolicyScheme(MultiScheme, MultiScheme, options => + { + options.ForwardDefaultSelector = context => + { + return context.Request.Headers.Authorization.Any(x => x!.Contains(ApiKeyDefaults.AuthenticationScheme)) + ? ApiKeyDefaults.AuthenticationScheme + : JwtBearerDefaults.AuthenticationScheme; + }; + }) + .AddJwtBearer(); + + // Configure API key authorization based on provider type + if (ApiKeyProviderType == typeof(AdminApiKeyProvider)) + authBuilder.AddApiKeyInAuthorizationHeader(); + else + authBuilder.AddApiKeyInAuthorizationHeader(); + + services.AddScoped(); + services.AddScoped(); + services.AddScoped(ApiKeyProviderType); + services.AddScoped(sp => (IApiKeyProvider)sp.GetRequiredService(ApiKeyProviderType)); + + services.AddAuthorization(options => + { + if (RequireLocalHost) + options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.AddRequirements(new LocalHostPermissionRequirement())); + else + options.AddPolicy(IdentityPolicyNames.SecurityRoot, policy => policy.RequireAuthenticatedUser()); + }); + } +} diff --git a/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs b/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs new file mode 100644 index 000000000..8ac1bf4df --- /dev/null +++ b/src/modules/Elsa.Identity/ShellFeatures/IdentityFeature.cs @@ -0,0 +1,119 @@ +using AspNetCore.Authentication.ApiKey; +using CShells.FastEndpoints.Features; +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Extensions; +using Elsa.Identity.Contracts; +using Elsa.Identity.Entities; +using Elsa.Identity.Multitenancy; +using Elsa.Identity.Options; +using Elsa.Identity.Providers; +using Elsa.Identity.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Identity.ShellFeatures; + +/// +/// Provides identity feature to authenticate & authorize API requests. +/// +[ShellFeature( + DisplayName = "Identity", + Description = "Provides identity management, authentication and authorization capabilities", + DependsOn = ["SystemClock"])] +[UsedImplicitly] +public class IdentityFeature : IFastEndpointsShellFeature +{ + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func UserStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func ApplicationStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func RoleStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func UserProvider { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func ApplicationProvider { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that creates an instance of an implementation of . + /// + public Func RoleProvider { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + // Configure options - Note: SigningKey must be configured by the application for security + services.Configure(_ => { }); + services.Configure(ApiKeyDefaults.AuthenticationScheme, options => + { + options.Realm = "Elsa Workflows"; + options.KeyName = "ApiKey"; + }); + services.Configure(_ => { }); + services.Configure(_ => { }); + services.Configure(_ => { }); + + // Memory stores. + services + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore(); + + // User providers. + services + .AddScoped() + .AddScoped() + .AddScoped(); + + // Application providers. + services + .AddScoped() + .AddScoped(); + + // Role providers. + services + .AddScoped() + .AddScoped() + .AddScoped(); + + // Tenant resolution strategies. + services + .AddScoped() + .AddScoped(); + + // Services. + services + .AddScoped(UserStore) + .AddScoped(ApplicationStore) + .AddScoped(RoleStore) + .AddScoped(UserProvider) + .AddScoped(ApplicationProvider) + .AddScoped(RoleProvider) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()) + .AddScoped(sp => sp.GetRequiredService()) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddHttpContextAccessor() + ; + } +} diff --git a/src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs b/src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs new file mode 100644 index 000000000..7cbb1ce59 --- /dev/null +++ b/src/modules/Elsa.KeyValues/ShellFeatures/KeyValueFeature.cs @@ -0,0 +1,27 @@ +using CShells.Features; +using Elsa.KeyValues.Contracts; +using Elsa.KeyValues.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.KeyValues.ShellFeatures; + +/// +/// Installs and configures key-value store features. +/// +[ShellFeature( + DisplayName = "Key-Value Store", + Description = "Provides key-value storage capabilities for workflows")] +[UsedImplicitly] +public class KeyValueFeature : IShellFeature +{ + /// + /// A factory that instantiates an . + /// + public Func KeyValueStore { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(KeyValueStore); + } +} diff --git a/src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs b/src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs new file mode 100644 index 000000000..2c67715ea --- /dev/null +++ b/src/modules/Elsa.Labels/ShellFeatures/LabelsFeature.cs @@ -0,0 +1,42 @@ +using CShells.Features; +using Elsa.Extensions; +using Elsa.Labels.Contracts; +using Elsa.Labels.Entities; +using Elsa.Labels.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Labels.ShellFeatures; + +/// +/// Enables functionality to tag workflows with labels. +/// +[ShellFeature( + DisplayName = "Labels", + Description = "Enables functionality to tag workflows with labels", + DependsOn = ["Mediator"])] +[UsedImplicitly] +public class LabelsFeature : IShellFeature +{ + /// + /// A delegate that provides an instance of an implementation of . + /// + public Func LabelStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A delegate that provides an instance of an implementation of . + /// + public Func WorkflowDefinitionLabelStore { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryStore() + .AddMemoryStore() + .AddScoped(LabelStore) + .AddScoped(WorkflowDefinitionLabelStore); + + services.AddNotificationHandlersFrom(GetType()); + } +} + diff --git a/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs b/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs new file mode 100644 index 000000000..4f8c46a91 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Common/PersistenceShellFeatureBase.cs @@ -0,0 +1,123 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Common.Entities; +using Elsa.Extensions; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Diagnostics; +using Microsoft.Extensions.DependencyInjection; + +// ReSharper disable once CheckNamespace +namespace Elsa.Persistence.EFCore; + +public abstract class PersistenceShellFeatureBase : IShellFeature + where TDbContext : ElsaDbContextBase +{ + /// + /// Gets or sets a value indicating whether to use context pooling. + /// + public bool UseContextPooling { get; set; } + + /// + /// Gets or sets a value indicating whether to run migrations. + /// + public bool RunMigrations { get; set; } = true; + + /// + /// Gets or sets the lifetime of the . Defaults to . + /// + public ServiceLifetime DbContextFactoryLifetime { get; set; } = ServiceLifetime.Scoped; + + /// + /// Gets or sets the connection string to use for the database. + /// + public string ConnectionString { get; set; } = null!; + + /// + /// Gets or sets additional options to configure the database context. + /// + public ElsaDbContextOptions? DbContextOptions { get; set; } + + /// + /// Gets or sets the callback used to configure the . + /// + protected virtual Action DbContextOptionsBuilder { get; set; } = (_, _) => { }; + + public void ConfigureServices(IServiceCollection services) + { + Action setup = (sp, opts) => + { + opts.ConfigureWarnings(w => w.Ignore(RelationalEventId.PendingModelChangesWarning)); + + // Configure the database provider + var migrationsAssembly = GetMigrationsAssembly(); + ConfigureProvider(opts, migrationsAssembly, ConnectionString, DbContextOptions); + + // Allow derived classes to further configure + DbContextOptionsBuilder(sp, opts); + }; + + if (UseContextPooling) + services.AddPooledDbContextFactory(setup); + else + services.AddDbContextFactory(setup, DbContextFactoryLifetime); + + services.Decorate, TenantAwareDbContextFactory>(); + + services.Configure(options => + { + options.RunMigrations[typeof(TDbContext)] = RunMigrations; + }); + + services.AddStartupTask>(); + OnConfiguring(services); + } + + /// + /// Gets the assembly containing migrations for this provider. + /// By default, returns the assembly of the concrete feature type. + /// + protected virtual Assembly GetMigrationsAssembly() => GetType().Assembly; + + /// + /// Configures the database provider for the specified . + /// + /// The options builder to configure. + /// The assembly containing migrations. + /// The connection string to use. + /// Additional options to configure the database context. + protected abstract void ConfigureProvider( + DbContextOptionsBuilder builder, + Assembly migrationsAssembly, + string connectionString, + ElsaDbContextOptions? options); + + protected virtual void OnConfiguring(IServiceCollection services) + { + } + + /// + /// Adds a store to the service collection. + /// + /// The type of the entity. + /// The type of the store. + protected void AddStore(IServiceCollection services) where TEntity : class, new() where TStore : class + { + services + .AddScoped>() + .AddScoped() + ; + } + + /// + /// Adds an entity store to the service collection. + /// + /// The type of the entity. + /// The type of the store. + protected void AddEntityStore(IServiceCollection services) where TEntity : Entity, new() where TStore : class + { + services + .AddScoped>() + .AddScoped() + ; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs new file mode 100644 index 000000000..62a740dfb --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Alterations/MySqlAlterationsPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Alterations Persistence", + Description = "Provides MySql persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class MySqlAlterationsPersistenceShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs new file mode 100644 index 000000000..b9d0184ec --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Identity/MySqlIdentityPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Identity Persistence", + Description = "Provides MySql persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class MySqlIdentityPersistenceShellFeature + : EFCoreIdentityPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs new file mode 100644 index 000000000..167bff57e --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Labels/MySqlLabelPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Label Persistence", + Description = "Provides MySql persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class MySqlLabelPersistenceShellFeature + : EFCoreLabelPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 000000000..25a075c23 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Management; + +/// +/// Configures the management feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Definition Persistence", + Description = "Provides MySql persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class MySqlWorkflowDefinitionPersistenceShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 000000000..986a02f09 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Management/MySqlWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Management; + +/// +/// Configures the management feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Instance Persistence", + Description = "Provides MySql persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class MySqlWorkflowInstancePersistenceShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs new file mode 100644 index 000000000..67dff18b8 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/MySqlWorkflowPersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures; + +/// +/// Configures the persistence feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Persistence", + Description = "Provides MySql persistence for workflow definitions and runtime data", + DependsOn = ["MySqlWorkflowDefinitionPersistence", "MySqlWorkflowInstancePersistence", "MySqlWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class MySqlWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 000000000..179a2b964 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Runtime/MySqlWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Workflow Runtime Persistence", + Description = "Provides MySql persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class MySqlWorkflowRuntimePersistenceShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs new file mode 100644 index 000000000..c5b7d1417 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.MySql/ShellFeatures/Tenants/MySqlTenantPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.MySql.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use MySql persistence. +/// +[ShellFeature( + DisplayName = "MySql Tenant Persistence", + Description = "Provides MySql persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class MySqlTenantPersistenceShellFeature + : EFCoreTenantManagementShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaMySql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs new file mode 100644 index 000000000..e5417957c --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Alterations/OracleAlterationsPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Alterations Persistence", + Description = "Provides Oracle persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class OracleAlterationsPersistenceShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs new file mode 100644 index 000000000..7b77d9307 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Identity/OracleIdentityPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Identity Persistence", + Description = "Provides Oracle persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class OracleIdentityPersistenceShellFeature + : EFCoreIdentityPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs new file mode 100644 index 000000000..566efe2ef --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Labels/OracleLabelPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Label Persistence", + Description = "Provides Oracle persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class OracleLabelPersistenceShellFeature + : EFCoreLabelPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 000000000..a218bf987 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Management; + +/// +/// Configures the management feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Definition Persistence", + Description = "Provides Oracle persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class OracleWorkflowDefinitionPersistenceShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 000000000..6c7ab5f96 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Management/OracleWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Management; + +/// +/// Configures the management feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Instance Persistence", + Description = "Provides Oracle persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class OracleWorkflowInstancePersistenceShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs new file mode 100644 index 000000000..4ff31945e --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/OracleWorkflowPersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures; + +/// +/// Configures the persistence feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Persistence", + Description = "Provides Oracle persistence for workflow definitions and runtime data", + DependsOn = ["OracleWorkflowDefinitionPersistence", "OracleWorkflowInstancePersistence", "OracleWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class OracleWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 000000000..4547d1486 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Runtime/OracleWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Workflow Runtime Persistence", + Description = "Provides Oracle persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class OracleWorkflowRuntimePersistenceShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs new file mode 100644 index 000000000..59ced9697 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Oracle/ShellFeatures/Tenants/OracleTenantPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.Oracle.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use Oracle persistence. +/// +[ShellFeature( + DisplayName = "Oracle Tenant Persistence", + Description = "Provides Oracle persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class OracleTenantPersistenceShellFeature + : EFCoreTenantManagementShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaOracle(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs new file mode 100644 index 000000000..ad341f083 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Alterations/PostgreSqlAlterationsPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Alterations Persistence", + Description = "Provides PostgreSql persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class PostgreSqlAlterationsPersistenceShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs new file mode 100644 index 000000000..5544db191 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Identity/PostgreSqlIdentityPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Identity Persistence", + Description = "Provides PostgreSql persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class PostgreSqlIdentityPersistenceShellFeature + : EFCoreIdentityPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs new file mode 100644 index 000000000..53df53847 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Labels/PostgreSqlLabelPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Label Persistence", + Description = "Provides PostgreSql persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class PostgreSqlLabelPersistenceShellFeature + : EFCoreLabelPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 000000000..c79f66d21 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Management; + +/// +/// Configures the management feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Definition Persistence", + Description = "Provides PostgreSql persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class PostgreSqlWorkflowDefinitionPersistenceShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 000000000..4ec10667a --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Management/PostgreSqlWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Management; + +/// +/// Configures the management feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Instance Persistence", + Description = "Provides PostgreSql persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class PostgreSqlWorkflowInstancePersistenceShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs new file mode 100644 index 000000000..590a5c4d6 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/PostgreSqlWorkflowPersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures; + +/// +/// Configures the persistence feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Persistence", + Description = "Provides PostgreSql persistence for workflow definitions and runtime data", + DependsOn = ["PostgreSqlWorkflowDefinitionPersistence", "PostgreSqlWorkflowInstancePersistence", "PostgreSqlWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class PostgreSqlWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 000000000..21c5079c9 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Runtime/PostgreSqlWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Workflow Runtime Persistence", + Description = "Provides PostgreSql persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class PostgreSqlWorkflowRuntimePersistenceShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs new file mode 100644 index 000000000..9f9ff77f3 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.PostgreSql/ShellFeatures/Tenants/PostgreSqlTenantPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.PostgreSql.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use PostgreSql persistence. +/// +[ShellFeature( + DisplayName = "PostgreSql Tenant Persistence", + Description = "Provides PostgreSql persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class PostgreSqlTenantPersistenceShellFeature + : EFCoreTenantManagementShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaPostgreSql(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs new file mode 100644 index 000000000..f47c2b1cb --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Alterations/SqlServerAlterationsPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Alterations Persistence", + Description = "Provides SqlServer persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class SqlServerAlterationsPersistenceShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs new file mode 100644 index 000000000..bc1ee0e68 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Identity/SqlServerIdentityPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Identity Persistence", + Description = "Provides SqlServer persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class SqlServerIdentityPersistenceShellFeature + : EFCoreIdentityPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs new file mode 100644 index 000000000..ca3a528b9 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Labels/SqlServerLabelPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Label Persistence", + Description = "Provides SqlServer persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class SqlServerLabelPersistenceShellFeature + : EFCoreLabelPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 000000000..7635fe3ba --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Management; + +/// +/// Configures the management feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Workflow Definition Persistence", + Description = "Provides SqlServer persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class SqlServerWorkflowDefinitionPersistenceShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 000000000..176c1bb04 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Management/SqlServerWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Management; + +/// +/// Configures the management feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Workflow Instance Persistence", + Description = "Provides SqlServer persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class SqlServerWorkflowInstancePersistenceShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 000000000..65136a99c --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Runtime/SqlServerWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Workflow Runtime Persistence", + Description = "Provides SqlServer persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class SqlServerWorkflowRuntimePersistenceShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs new file mode 100644 index 000000000..16e666326 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.SqlServer/ShellFeatures/Tenants/SqlServerTenantPersistenceShellFeature.cs @@ -0,0 +1,26 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.Persistence.EFCore.SqlServer.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use SqlServer persistence. +/// +[ShellFeature( + DisplayName = "SqlServer Tenant Persistence", + Description = "Provides SqlServer persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class SqlServerTenantPersistenceShellFeature + : EFCoreTenantManagementShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlServer(migrationsAssembly, connectionString, options); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs new file mode 100644 index 000000000..253e967ec --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Alterations/SqliteAlterationsPersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Alterations; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Alterations; + +/// +/// Configures the alterations feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Alterations Persistence", + Description = "Provides Sqlite persistence for workflow alterations", + DependsOn = ["Alterations"])] +[UsedImplicitly] +public class SqliteAlterationsPersistenceShellFeature + : EFCoreAlterationsPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs new file mode 100644 index 000000000..13ac4baab --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Identity/SqliteIdentityPersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Identity; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Identity; + +/// +/// Configures the identity feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Identity Persistence", + Description = "Provides Sqlite persistence for identity management", + DependsOn = ["Identity"])] +[UsedImplicitly] +public class SqliteIdentityPersistenceShellFeature + : EFCoreIdentityPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs new file mode 100644 index 000000000..c1150748c --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Labels/SqliteLabelPersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Labels; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Labels; + +/// +/// Configures the labels feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Label Persistence", + Description = "Provides Sqlite persistence for label management", + DependsOn = ["Labels"])] +[UsedImplicitly] +public class SqliteLabelPersistenceShellFeature + : EFCoreLabelPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 000000000..7cca81a69 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; + +/// +/// Configures the management feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Definition Persistence", + Description = "Provides Sqlite persistence for workflow definitions", + DependsOn = ["WorkflowManagement", "WorkflowDefinitions"])] +[UsedImplicitly] +public class SqliteWorkflowDefinitionPersistenceShellFeature + : EFCoreWorkflowDefinitionPersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 000000000..6de74788b --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Management/SqliteWorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Management; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Management; + +/// +/// Configures the management feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Instance Persistence", + Description = "Provides Sqlite persistence for workflow instances", + DependsOn = ["WorkflowManagement", "WorkflowInstances"])] +[UsedImplicitly] +public class SqliteWorkflowInstancePersistenceShellFeature + : EFCoreWorkflowInstancePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 000000000..1632f5d96 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Runtime/SqliteWorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Runtime; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Runtime; + +/// +/// Configures the runtime feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Runtime Persistence", + Description = "Provides Sqlite persistence for workflow runtime", + DependsOn = ["WorkflowRuntime"])] +[UsedImplicitly] +public class SqliteWorkflowRuntimePersistenceShellFeature + : EFCoreWorkflowRuntimePersistenceShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowPersistenceShellFeature.cs new file mode 100644 index 000000000..8e8e55f8f --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/SqliteWorkflowPersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures; + +/// +/// Configures the persistence feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Workflow Persistence", + Description = "Provides Sqlite persistence for workflow definitions and runtime data", + DependsOn = ["SqliteWorkflowDefinitionPersistence", "SqliteWorkflowInstancePersistence", "SqliteWorkflowRuntimePersistence"])] +[UsedImplicitly] +public class SqliteWorkflowPersistenceShellFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs new file mode 100644 index 000000000..eea341eec --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore.Sqlite/ShellFeatures/Tenants/SqliteTenantPersistenceShellFeature.cs @@ -0,0 +1,34 @@ +using System.Reflection; +using CShells.Features; +using Elsa.Persistence.EFCore.Extensions; +using Elsa.Persistence.EFCore.Modules.Tenants; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Sqlite.ShellFeatures.Tenants; + +/// +/// Configures the tenants feature to use Sqlite persistence. +/// +[ShellFeature( + DisplayName = "Sqlite Tenant Persistence", + Description = "Provides Sqlite persistence for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class SqliteTenantPersistenceShellFeature + : EFCoreTenantManagementShellFeatureBase +{ + /// + protected override void ConfigureProvider(DbContextOptionsBuilder builder, Assembly migrationsAssembly, string connectionString, ElsaDbContextOptions? options) + { + builder.UseElsaSqlite(migrationsAssembly, connectionString, options); + } + + /// + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + base.OnConfiguring(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs new file mode 100644 index 000000000..0a3aea1ab --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Alterations/ShellFeature.cs @@ -0,0 +1,23 @@ +using Elsa.Alterations.Core.Contracts; +using Elsa.Alterations.Core.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Alterations; + +/// +/// Base class for alterations persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreAlterationsPersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + + AddEntityStore(services); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs new file mode 100644 index 000000000..a09f2658b --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Identity/ShellFeature.cs @@ -0,0 +1,24 @@ +using Elsa.Identity.Contracts; +using Elsa.Identity.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Identity; + +/// +/// Base class for identity persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreIdentityPersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + AddEntityStore(services); + AddEntityStore(services); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs new file mode 100644 index 000000000..20c9fa937 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Labels/ShellFeature.cs @@ -0,0 +1,22 @@ +using Elsa.Labels.Contracts; +using Elsa.Labels.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Labels; + +/// +/// Base class for label persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreLabelPersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + AddEntityStore(services); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs new file mode 100644 index 000000000..7155c603e --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowDefinitionPersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using Elsa.Workflows.Management; +using Elsa.Workflows.Management.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Management; + +/// +/// Base class for workflow definition persistence features. +/// This is not a standalone shell feature - use provider-specific features like SqliteWorkflowDefinitionPersistenceShellFeature. +/// +[UsedImplicitly] +public abstract class EFCoreWorkflowDefinitionPersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs new file mode 100644 index 000000000..200c581fc --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Management/WorkflowInstancePersistenceShellFeature.cs @@ -0,0 +1,20 @@ +using Elsa.Workflows.Management; +using Elsa.Workflows.Management.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Management; + +/// +/// Base class for workflow instance persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreWorkflowInstancePersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs new file mode 100644 index 000000000..1017ae80d --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Runtime/WorkflowRuntimePersistenceShellFeature.cs @@ -0,0 +1,33 @@ +using Elsa.KeyValues.Contracts; +using Elsa.KeyValues.Entities; +using Elsa.Workflows.Runtime; +using Elsa.Workflows.Runtime.Entities; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Runtime; + +/// +/// Base class for workflow runtime persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreWorkflowRuntimePersistenceShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + services.AddScoped(); + + AddEntityStore(services); + AddStore(services); + AddStore(services); + AddEntityStore(services); + AddEntityStore(services); + AddStore(services); + } +} diff --git a/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs b/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs new file mode 100644 index 000000000..f64699394 --- /dev/null +++ b/src/modules/Elsa.Persistence.EFCore/Modules/Tenants/ShellFeature.cs @@ -0,0 +1,20 @@ +using Elsa.Common.Multitenancy; +using Elsa.Tenants; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Persistence.EFCore.Modules.Tenants; + +/// +/// Base class for tenant management persistence features. +/// This is not a standalone shell feature - use provider-specific features. +/// +[UsedImplicitly] +public abstract class EFCoreTenantManagementShellFeatureBase : PersistenceShellFeatureBase +{ + protected override void OnConfiguring(IServiceCollection services) + { + services.AddScoped(); + AddEntityStore(services); + } +} diff --git a/src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs b/src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs new file mode 100644 index 000000000..7087c4d3b --- /dev/null +++ b/src/modules/Elsa.Resilience/ShellFeatures/ResilienceShellFeature.cs @@ -0,0 +1,47 @@ +using CShells.FastEndpoints.Features; +using CShells.Features; +using Elsa.Expressions.Options; +using Elsa.Extensions; +using Elsa.Resilience.Entities; +using Elsa.Resilience.Modifiers; +using Elsa.Resilience.Options; +using Elsa.Resilience.Recorders; +using Elsa.Resilience.Serialization; +using Elsa.Resilience.StrategySources; +using Elsa.Workflows; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Resilience.ShellFeatures; + +[ShellFeature] +public class ResilienceShellFeature : IFastEndpointsShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.Configure(options => + { + options.AddTypeAlias>("RetryAttemptRecordList"); + }); + + services.AddOptions(); + + services + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton(VoidRetryAttemptRecorder.Instance) + .AddSingleton(VoidRetryAttemptReader.Instance) + .AddScoped() + .AddScoped() + .AddScoped() + .AddHandlersFrom(); + + // Register transient exception detection infrastructure + services + .AddSingleton() + .AddSingleton(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs b/src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs new file mode 100644 index 000000000..0447e7569 --- /dev/null +++ b/src/modules/Elsa.SasTokens/ShellFeatures/SasTokensFeature.cs @@ -0,0 +1,27 @@ +using CShells.Features; +using Elsa.SasTokens.Contracts; +using JetBrains.Annotations; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.SasTokens.ShellFeatures; + +/// +/// Adds the SAS tokens feature to the workflow runtime. +/// +[ShellFeature( + DisplayName = "SAS Tokens", + Description = "Provides shared access signature token generation and validation")] +[UsedImplicitly] +public class SasTokensFeature : IShellFeature +{ + private Func TokenService { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + public void ConfigureServices(IServiceCollection services) + { + var builder = services.AddDataProtection(); + builder.SetApplicationName("Elsa Workflows"); + + services.AddScoped(TokenService); + } +} diff --git a/src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs b/src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs new file mode 100644 index 000000000..7473a0b11 --- /dev/null +++ b/src/modules/Elsa.Scheduling/ShellFeatures/SchedulingFeature.cs @@ -0,0 +1,55 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Common.RecurringTasks; +using Elsa.Extensions; +using Elsa.Scheduling.Bookmarks; +using Elsa.Scheduling.Handlers; +using Elsa.Scheduling.HostedServices; +using Elsa.Scheduling.Services; +using Elsa.Scheduling.TriggerPayloadValidators; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Scheduling.ShellFeatures; + +/// +/// Provides scheduling features to the system. +/// +[ShellFeature( + DisplayName = "Scheduling", + Description = "Provides scheduling capabilities for workflows including cron and delay-based triggers", + DependsOn = ["SystemClock"])] +[UsedImplicitly] +public class SchedulingFeature : IShellFeature +{ + /// + /// Gets or sets the trigger scheduler factory. + /// + public Func WorkflowScheduler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// Gets or sets the CRON parser factory. + /// + public Func CronParser { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton() + .AddSingleton() + .AddSingleton(CronParser) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(WorkflowScheduler) + .AddBackgroundTask() + .AddHandlersFrom() + + //Trigger payload validators. + .AddTriggerPayloadValidator(); + } +} + diff --git a/src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs b/src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs new file mode 100644 index 000000000..139ddf193 --- /dev/null +++ b/src/modules/Elsa.Tenants.AspNetCore/ShellFeatures/MultitenantHttpRoutingFeature.cs @@ -0,0 +1,40 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Tenants.AspNetCore.Options; +using Elsa.Tenants.AspNetCore.Services; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.AspNetCore.ShellFeatures; + +/// +/// Provides multi-tenant HTTP routing features. +/// +[ShellFeature( + DisplayName = "Multi-tenant HTTP Routing", + Description = "Provides multi-tenant HTTP routing capabilities for workflows")] +[UsedImplicitly] +public class MultitenantHttpRoutingFeature : IShellFeature +{ + /// + /// Gets or sets the tenant header name. + /// + public string TenantHeaderName { get; set; } = "X-Tenant-Id"; + + public void ConfigureServices(IServiceCollection services) + { + // Multitenancy HTTP options. + services.Configure(options => + { + options.TenantHeaderName = TenantHeaderName; + }); + + // Tenant resolvers. + services + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(); + } +} + diff --git a/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs new file mode 100644 index 000000000..5ad627608 --- /dev/null +++ b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementEndpointsFeature.cs @@ -0,0 +1,21 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.ShellFeatures; + +/// +/// Enables tenant management endpoints. +/// +[ShellFeature( + DisplayName = "Tenant Management Endpoints", + Description = "Provides REST API endpoints for tenant management", + DependsOn = ["TenantManagement"])] +[UsedImplicitly] +public class TenantManagementEndpointsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + // FastEndpoints are registered via assembly scanning + } +} diff --git a/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs new file mode 100644 index 000000000..e6c1d1e16 --- /dev/null +++ b/src/modules/Elsa.Tenants/ShellFeatures/TenantManagementFeature.cs @@ -0,0 +1,30 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Extensions; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.ShellFeatures; + +/// +/// Enables tenant management capabilities. +/// +[ShellFeature( + DisplayName = "Tenant Management", + Description = "Provides tenant store and management capabilities")] +[UsedImplicitly] +public class TenantManagementFeature : IShellFeature +{ + /// + /// A factory that instantiates an . + /// + public Func TenantStoreFactory { get; set; } = sp => sp.GetRequiredService(); + + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryStore() + .AddScoped(TenantStoreFactory); + } +} + diff --git a/src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs b/src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs new file mode 100644 index 000000000..435d0ddf6 --- /dev/null +++ b/src/modules/Elsa.Tenants/ShellFeatures/TenantsFeature.cs @@ -0,0 +1,27 @@ +using CShells.Features; +using Elsa.Common.Multitenancy; +using Elsa.Tenants.Mediator.Tasks; +using Elsa.Tenants.Options; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.ShellFeatures; + +/// +/// Configures multi-tenancy features. +/// +[ShellFeature( + DisplayName = "Tenants", + Description = "Provides multi-tenancy capabilities for workflows")] +[UsedImplicitly] +public class TenantsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddHostedService() + .AddScoped() + .AddScoped(); + } +} + diff --git a/src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs b/src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs new file mode 100644 index 000000000..773df8e82 --- /dev/null +++ b/src/modules/Elsa.WorkflowProviders.BlobStorage.ElsaScript/ShellFeatures/ElsaScriptBlobStorageFeature.cs @@ -0,0 +1,25 @@ +using CShells.Features; +using Elsa.WorkflowProviders.BlobStorage.Contracts; +using Elsa.WorkflowProviders.BlobStorage.ElsaScript.Handlers; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.WorkflowProviders.BlobStorage.ElsaScript.ShellFeatures; + +/// +/// A feature that enables ElsaScript support for the BlobStorage workflow provider. +/// +[ShellFeature( + DisplayName = "ElsaScript Blob Storage", + Description = "Provides ElsaScript format support for the BlobStorage workflow provider", + DependsOn = ["BlobStorage", "ElsaScript"])] +[UsedImplicitly] +public class ElsaScriptBlobStorageFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + // Register the ElsaScript format handler + services.AddScoped(); + } +} + diff --git a/src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs b/src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs new file mode 100644 index 000000000..0edba6556 --- /dev/null +++ b/src/modules/Elsa.WorkflowProviders.BlobStorage/ShellFeatures/BlobStorageFeature.cs @@ -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; + +/// +/// A feature that enables the FluentStorage workflow definition provider. +/// +[ShellFeature( + DisplayName = "Blob Storage Workflow Provider", + Description = "Provides workflow definitions from blob storage", + DependsOn = ["WorkflowManagement"])] +[UsedImplicitly] +public class BlobStorageFeature : IShellFeature +{ + /// + /// The blob storage to use. + /// + public Func BlobStorage { get; set; } = _ => StorageFactory.Blobs.DirectoryFiles(GetDefaultWorkflowsDirectory()); + + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(sp => new BlobStorageProvider(BlobStorage(sp))); + + // Register the JSON format handler (built-in support) + services.AddScoped(); + + services.AddScoped(); + services.AddScoped(sp => sp.GetRequiredService()); + } + + /// + /// Gets the default workflows directory. + /// + public static string GetDefaultWorkflowsDirectory() + { + var entryAssembly = Assembly.GetEntryAssembly(); + var entryAssemblyDir = entryAssembly != null + ? Path.GetDirectoryName(entryAssembly.Location) + : AppContext.BaseDirectory; + var directory = Path.Combine(entryAssemblyDir!, "Workflows"); + return directory; + } +} + diff --git a/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj b/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj index 9311d394c..f36f1fdcc 100644 --- a/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj +++ b/src/modules/Elsa.Workflows.Api/Elsa.Workflows.Api.csproj @@ -6,6 +6,10 @@ elsa module workflows api + + + + diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs index f0f1c0cf1..fd82297b9 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/Features/Get/Endpoint.cs @@ -9,16 +9,8 @@ namespace Elsa.Workflows.Api.Endpoints.Features.Get; /// Returns the specified installed feature. /// [PublicAPI] -internal class Get : ElsaEndpointWithoutRequest +internal class Get(IInstalledFeatureProvider installedFeatureProvider) : ElsaEndpointWithoutRequest { - private readonly IInstalledFeatureRegistry _installedFeatureRegistry; - - /// - public Get(IInstalledFeatureRegistry installedFeatureRegistry) - { - _installedFeatureRegistry = installedFeatureRegistry; - } - /// public override void Configure() { @@ -30,14 +22,14 @@ internal class Get : ElsaEndpointWithoutRequest public override async Task HandleAsync( CancellationToken cancellationToken) { var fullName = Route("fullName")!; - var descriptor = _installedFeatureRegistry.Find(fullName); + var descriptor = installedFeatureProvider.Find(fullName); if (descriptor == null) { await Send.NotFoundAsync(cancellationToken); return; } - + await Send.OkAsync(descriptor, cancellationToken); } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs index dfa38c058..b62efe712 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/Features/List/Endpoint.cs @@ -10,16 +10,8 @@ namespace Elsa.Workflows.Api.Endpoints.Features.List; /// Returns a list of installed features. /// [PublicAPI] -internal class List : ElsaEndpointWithoutRequest> +internal class List(IInstalledFeatureProvider installedFeatureProvider) : ElsaEndpointWithoutRequest> { - private readonly IInstalledFeatureRegistry _installedFeatureRegistry; - - /// - public List(IInstalledFeatureRegistry installedFeatureRegistry) - { - _installedFeatureRegistry = installedFeatureRegistry; - } - /// public override void Configure() { @@ -30,7 +22,7 @@ internal class List : ElsaEndpointWithoutRequest /// public override Task> ExecuteAsync(CancellationToken cancellationToken) { - var descriptors = _installedFeatureRegistry.List().ToList(); + var descriptors = installedFeatureProvider.List().ToList(); var response = new ListResponse(descriptors); return Task.FromResult(response); diff --git a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs index a7afdcfe4..caaef86c9 100644 --- a/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs +++ b/src/modules/Elsa.Workflows.Api/Endpoints/WorkflowDefinitions/Execute/PostEndpoint.cs @@ -32,7 +32,7 @@ internal class PostEndpoint( if (HttpContext.Request.ContentType?.Contains("application/json") ?? false) { using var reader = new StreamReader(HttpContext.Request.Body); - var body = await reader.ReadToEndAsync(); + var body = await reader.ReadToEndAsync(cancellationToken); if (!string.IsNullOrWhiteSpace(body)) { diff --git a/src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs b/src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs new file mode 100644 index 000000000..9ec781fab --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/ShellFeatures/RealTimeWorkflowUpdatesFeature.cs @@ -0,0 +1,24 @@ +using CShells.Features; +using Elsa.Workflows.Api.RealTime.Handlers; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Api.ShellFeatures; + +/// +/// Sets up a SignalR hub for receiving workflow events on the client. +/// +[ShellFeature( + DisplayName = "Real-Time Workflow Updates", + Description = "Provides real-time workflow updates via SignalR")] +[UsedImplicitly] +public class RealTimeWorkflowUpdatesFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSignalR(); + services.AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs b/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs new file mode 100644 index 000000000..de155669f --- /dev/null +++ b/src/modules/Elsa.Workflows.Api/ShellFeatures/WorkflowsApiFeature.cs @@ -0,0 +1,46 @@ +using CShells.FastEndpoints.Features; +using CShells.Features; +using Elsa.Extensions; +using Elsa.Workflows.Api.Constants; +using Elsa.Workflows.Api.Requirements; +using Elsa.Workflows.Api.Serialization; +using Elsa.Workflows.Api.Services; +using JetBrains.Annotations; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Api.ShellFeatures; + +/// +/// Adds workflows API features with FastEndpoints support. +/// +/// +/// This feature implements to indicate that this assembly +/// contains FastEndpoints that should be automatically discovered and registered. +/// +[ShellFeature( + DisplayName = "Workflows API", + Description = "Provides REST API endpoints for workflow management", + DependsOn = +[ + "ElsaFastEndpoints", + "WorkflowInstances", + "WorkflowManagement", + "WorkflowRuntime", + "SasTokens" +])] +[UsedImplicitly] +public class WorkflowsApiFeature : IFastEndpointsShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSerializationOptionsConfigurator(); + services.AddScoped(); + services.AddScoped(); + services.Configure(options => + { + options.AddPolicy(AuthorizationPolicies.NotReadOnlyPolicy, policy => policy.AddRequirements(new NotReadOnlyRequirement())); + }); + services.AddScoped(); + } +} diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs new file mode 100644 index 000000000..5c8803789 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/CommitStrategiesFeature.cs @@ -0,0 +1,17 @@ +using CShells.Features; +using Elsa.Extensions; +using Elsa.Workflows.CommitStates; +using Elsa.Workflows.CommitStates.Tasks; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.ShellFeatures; + +[ShellFeature("CommitStrategies")] +public class CommitStrategiesFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.AddStartupTask(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs new file mode 100644 index 000000000..b7d9fdce3 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/FlowchartFeature.cs @@ -0,0 +1,35 @@ +using CShells.Features; +using Elsa.Extensions; +using Elsa.Workflows.Activities.Flowchart.Options; +using Elsa.Workflows.Activities.Flowchart.Serialization; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.ShellFeatures; + +/// +/// Adds support for the Flowchart activity. +/// +[ShellFeature( + DisplayName = "Flowchart", + Description = "Adds support for the Flowchart activity")] +[UsedImplicitly] +public class FlowchartFeature : IShellFeature +{ + /// + /// A delegate to configure . + /// + public Action? FlowchartOptionsConfigurator { get; set; } + + public void ConfigureServices(IServiceCollection services) + { + services.AddSerializationOptionsConfigurator(); + + // Register FlowchartOptions + services.AddOptions(); + + if (FlowchartOptionsConfigurator != null) + services.Configure(FlowchartOptionsConfigurator); + } +} + diff --git a/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs new file mode 100644 index 000000000..4e88b9ef2 --- /dev/null +++ b/src/modules/Elsa.Workflows.Core/ShellFeatures/WorkflowsFeature.cs @@ -0,0 +1,179 @@ +using CShells.Features; +using Elsa.Common; +using Elsa.Common.Serialization; +using Elsa.Common.ShellFeatures; +using Elsa.Expressions.ShellFeatures; +using Elsa.Extensions; +using Elsa.Workflows.ActivationValidators; +using Elsa.Workflows.Activities.Flowchart.Options; +using Elsa.Workflows.Activities.Flowchart.Serialization; +using Elsa.Workflows.Builders; +using Elsa.Workflows.CommitStates; +using Elsa.Workflows.IncidentStrategies; +using Elsa.Workflows.LogPersistence; +using Elsa.Workflows.LogPersistence.Strategies; +using Elsa.Workflows.Middleware.Activities; +using Elsa.Workflows.Middleware.Workflows; +using Elsa.Workflows.Pipelines.ActivityExecution; +using Elsa.Workflows.Pipelines.WorkflowExecution; +using Elsa.Workflows.PortResolvers; +using Elsa.Workflows.Serialization.Configurators; +using Elsa.Workflows.Serialization.Helpers; +using Elsa.Workflows.Serialization.Serializers; +using Elsa.Workflows.Services; +using Elsa.Workflows.UIHints.CheckList; +using Elsa.Workflows.UIHints.Dictionary; +using Elsa.Workflows.UIHints.Dropdown; +using Elsa.Workflows.UIHints.JsonEditor; +using Elsa.Workflows.UIHints.RadioList; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.ShellFeatures; + +[ShellFeature(DependsOn = +[ + "SystemClock", + "Expressions", + "Mediator", + "DefaultFormatters", + "Multitenancy", + "CommitStrategies" +])] +public class WorkflowsFeature : IShellFeature +{ + /// + /// A delegate to configure the . + /// + public Action WorkflowExecutionPipeline { get; set; } = builder => builder + .UseExceptionHandling() + .UseDefaultActivityScheduler(); + + /// + /// A delegate to configure the . + /// + public Action ActivityExecutionPipeline { get; set; } = builder => builder.UseDefaultActivityInvoker(); + + /// + /// A factory that instantiates a concrete . + /// + public Func StandardInStreamProvider { get; set; } = _ => new StandardInStreamProvider(Console.In); + + /// + /// A factory that instantiates a concrete . + /// + public Func StandardOutStreamProvider { get; set; } = _ => new StandardOutStreamProvider(Console.Out); + + /// + /// A factory that instantiates a concrete . + /// + public Func> WorkflowLoggerStateGenerator { get; set; } = sp => new WorkflowLoggerStateGenerator(); + + /// + /// A factory that instantiates a concrete . + /// + public Func> ActivityLoggerStateGenerator { get; set; } = sp => new ActivityLoggerStateGenerator(); + + public void ConfigureServices(IServiceCollection services) + { + services + // Core. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton(sp => ActivatorUtilities.CreateInstance(sp)) + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddTransient() + .AddScoped(typeof(Func), sp => () => sp.GetRequiredService()) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Incident Strategies. + .AddTransient() + .AddTransient() + + // Pipelines. + .AddScoped(sp => new ActivityExecutionPipeline(sp, ActivityExecutionPipeline)) + .AddScoped(sp => new WorkflowExecutionPipeline(sp, WorkflowExecutionPipeline)) + + // Built-in activity services. + .AddScoped() + .AddScoped() + .AddSerializationOptionsConfigurator() + .AddSerializationOptionsConfigurator() + + // Domain event handlers. + .AddHandlersFrom() + + // Stream providers. + .AddScoped(StandardInStreamProvider) + .AddScoped(StandardOutStreamProvider) + + // Storage drivers. + .AddScoped() + .AddStorageDriver() + .AddStorageDriver() + .AddStorageDriver() + + // Serialization. + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddSingleton() + + // Instantiation strategies. + .AddScoped() + + // UI. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + + // Logger state generators. + .AddSingleton(WorkflowLoggerStateGenerator) + .AddSingleton(ActivityLoggerStateGenerator) + + // Log Persistence Strategies. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Logging + .AddLogging(); + + // Overridable services + services.AddScoped(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs new file mode 100644 index 000000000..6e42d111b --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/CachingWorkflowDefinitionsFeature.cs @@ -0,0 +1,28 @@ +using CShells.Features; +using Elsa.Workflows.Management.Handlers.Notifications; +using Elsa.Workflows.Management.Services; +using Elsa.Workflows.Management.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Configures workflow definition caching. +/// +[ShellFeature( + DisplayName = "Caching Workflow Definitions", + Description = "Provides caching for workflow definitions")] +[UsedImplicitly] +public class CachingWorkflowDefinitionsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddSingleton(); + services.Decorate(); + services.Decorate(); + services.AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs new file mode 100644 index 000000000..a79a6ee1f --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowDefinitionsFeature.cs @@ -0,0 +1,21 @@ +using CShells.Features; +using Elsa.Workflows.Management.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Configures workflow definition storage. +/// +[ShellFeature( + DisplayName = "Workflow Definitions", + Description = "Manages workflow definitions and their storage")] +[UsedImplicitly] +public class WorkflowDefinitionsFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + } +} diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs new file mode 100644 index 000000000..c9d072c36 --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowInstancesFeature.cs @@ -0,0 +1,21 @@ +using CShells.Features; +using Elsa.Workflows.Management.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Enables storage of workflow instances. +/// +[ShellFeature( + DisplayName = "Workflow Instances", + Description = "Manages workflow execution instances and their persistent state")] +[UsedImplicitly] +public class WorkflowInstancesFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services.AddScoped(); + } +} diff --git a/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs new file mode 100644 index 000000000..6f54bfefc --- /dev/null +++ b/src/modules/Elsa.Workflows.Management/ShellFeatures/WorkflowManagementFeature.cs @@ -0,0 +1,159 @@ +using System.Dynamic; +using System.Text.Json; +using System.Text.Json.Nodes; +using CShells.Features; +using Elsa.Caching.Features; +using Elsa.Common.Features; +using Elsa.Expressions.Contracts; +using Elsa.Extensions; +using Elsa.Features.Attributes; +using Elsa.Workflows.Features; +using Elsa.Workflows.Management.Activities.WorkflowDefinitionActivity; +using Elsa.Workflows.Management.Contracts; +using Elsa.Workflows.Management.Entities; +using Elsa.Workflows.Management.Features; +using Elsa.Workflows.Management.Handlers.Notifications; +using Elsa.Workflows.Management.Mappers; +using Elsa.Workflows.Management.Materializers; +using Elsa.Workflows.Management.Models; +using Elsa.Workflows.Management.Options; +using Elsa.Workflows.Management.Providers; +using Elsa.Workflows.Management.Services; +using Elsa.Workflows.Management.Stores; +using Elsa.Workflows.Serialization.Serializers; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Management.ShellFeatures; + +/// +/// Installs and configures the workflow management feature. +/// +[ShellFeature( + DisplayName = "Workflow Management", + Description = "Provides comprehensive workflow definition and instance management capabilities", + DependsOn = + [ + "StringCompression", + "Mediator", + "MemoryCache", + "SystemClock", + "Workflows", + "WorkflowDefinitions", + "WorkflowInstances" + ])] +[UsedImplicitly] +public class WorkflowManagementFeature : IShellFeature +{ + private const string PrimitivesCategory = "Primitives"; + private const string LookupsCategory = "Lookups"; + private const string DynamicCategory = "Dynamic"; + private const string DataCategory = "Data"; + private const string SystemCategory = "System"; + + /// + /// A set of variable types to make available to the system. + /// + public HashSet VariableDescriptors { get; } = + [ + new(typeof(object), PrimitivesCategory, "The root class for all objects in the CLR System."), + new(typeof(string), PrimitivesCategory, "Represents a static string of characters."), + new(typeof(bool), PrimitivesCategory, "Represents a true or false value."), + new(typeof(int), PrimitivesCategory, "A 32 bit integer."), + new(typeof(long), PrimitivesCategory, "A 64 bit integer."), + new(typeof(float), PrimitivesCategory, "A 32 bit floating point number."), + new(typeof(double), PrimitivesCategory, "A 64 bit floating point number."), + new(typeof(decimal), PrimitivesCategory, "A decimal number."), + new(typeof(Guid), PrimitivesCategory, "Represents a Globally Unique Identifier."), + new(typeof(DateTime), PrimitivesCategory, "A value type that represents a date and time."), + new(typeof(DateTimeOffset), PrimitivesCategory, "A value type that consists of a DateTime and a time zone offset."), + new(typeof(TimeSpan), PrimitivesCategory, "Represents a duration of time."), + new(typeof(IDictionary), LookupsCategory, "A dictionary with string key and values."), + new(typeof(IDictionary), LookupsCategory, "A dictionary with string key and object values."), + new(typeof(ExpandoObject), DynamicCategory, "A dictionary that can be typed as dynamic to access members using dot notation."), + new(typeof(JsonElement), DynamicCategory, "A JSON element for reading a JSON structure."), + new(typeof(JsonNode), DynamicCategory, "A JSON node for reading and writing a JSON structure."), + new(typeof(JsonObject), DynamicCategory, "A JSON object for reading and writing a JSON structure."), + new(typeof(byte[]), DataCategory, "A byte array."), + new(typeof(Stream), DataCategory, "A stream.") + ]; + + + public void ConfigureServices(IServiceCollection services) + { + services + .AddMemoryStore() + .AddMemoryStore() + .AddActivityProvider() + .AddActivityProvider() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddSerializationOptionsConfigurator() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddSingleton() + .AddSingleton() + .AddScoped() + .AddScoped(); + + services + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + ; + + AddActivitiesFrom(); + AddActivitiesFrom(); + + + services.Configure(options => + { + foreach (var activityType in ActivityTypes.Distinct()) + options.ActivityTypes.Add(activityType); + + foreach (var descriptor in VariableDescriptors.DistinctBy(x => x.Type)) + options.VariableDescriptors.Add(descriptor); + }); + } + + /// + /// A set of activity types to make available to the system. + /// + private HashSet ActivityTypes { get; } = []; + + /// + /// Adds the specified activity types to the system. + /// + private WorkflowManagementFeature AddActivities(IEnumerable activityTypes) + { + ActivityTypes.AddRange(activityTypes); + return this; + } + + private WorkflowManagementFeature AddActivitiesFrom() + { + var activityTypes = typeof(TMarker).Assembly.GetExportedTypes() + .Where(x => typeof(IActivity).IsAssignableFrom(x) && x is { IsAbstract: false, IsInterface: false, IsGenericType: false }) + .ToList(); + return AddActivities(activityTypes); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs b/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs index 67a7be6e8..c7facf34d 100644 --- a/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs +++ b/src/modules/Elsa.Workflows.Management/Stores/CachingWorkflowDefinitionStore.cs @@ -129,7 +129,7 @@ public class CachingWorkflowDefinitionStore(IWorkflowDefinitionStore decoratedSt } /// - public async Task GetIsNameUnique(string name, string? definitionId = default, CancellationToken cancellationToken = default) + public async Task GetIsNameUnique(string name, string? definitionId = null, CancellationToken cancellationToken = default) { var cacheKey = hasher.Hash(nameof(GetIsNameUnique), name, definitionId); return await GetOrCreateAsync(cacheKey, () => decoratedStore.GetIsNameUnique(name, definitionId, cancellationToken)); diff --git a/src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs new file mode 100644 index 000000000..5f2a40189 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime.Distributed/ShellFeatures/DistributedRuntimeFeature.cs @@ -0,0 +1,26 @@ +using CShells.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Runtime.Distributed.ShellFeatures; + +/// +/// Installs and configures distributed workflow runtime features. +/// +[ShellFeature( + DisplayName = "Distributed Runtime", + Description = "Provides distributed workflow runtime capabilities", + DependsOn = ["WorkflowRuntime", "Resilience"])] +[UsedImplicitly] +public class DistributedRuntimeFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()) + .AddScoped() + .AddScoped(sp => sp.GetRequiredService()); + } +} + diff --git a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs new file mode 100644 index 000000000..840fa4b5b --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/CachingWorkflowRuntimeFeature.cs @@ -0,0 +1,31 @@ +using CShells.Features; +using Elsa.Workflows.Runtime.Handlers; +using Elsa.Workflows.Runtime.Stores; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Runtime.ShellFeatures; + +/// +/// Installs and configures workflow runtime caching features. +/// +[ShellFeature( + DisplayName = "Caching Workflow Runtime", + Description = "Provides caching for workflow runtime operations", + DependsOn = ["MemoryCache", "WorkflowRuntime"])] +[UsedImplicitly] +public class CachingWorkflowRuntimeFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + services + // Decorators. + .Decorate() + + // Handlers. + .AddNotificationHandler() + .AddNotificationHandler(); + } +} + + diff --git a/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs new file mode 100644 index 000000000..efc6a241d --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/ShellFeatures/WorkflowRuntimeFeature.cs @@ -0,0 +1,261 @@ +using CShells.Features; +using Elsa.Common.RecurringTasks; +using Elsa.Extensions; +using Elsa.Mediator.Contracts; +using Elsa.Workflows.CommitStates; +using Elsa.Workflows.Management; +using Elsa.Workflows.Management.Contracts; +using Elsa.Workflows.Management.Services; +using Elsa.Workflows.Runtime.ActivationValidators; +using Elsa.Workflows.Runtime.Entities; +using Elsa.Workflows.Runtime.Handlers; +using Elsa.Workflows.Runtime.Options; +using Elsa.Workflows.Runtime.Providers; +using Elsa.Workflows.Runtime.Stores; +using Elsa.Workflows.Runtime.Tasks; +using Elsa.Workflows.Runtime.UIHints; +using Medallion.Threading; +using Medallion.Threading.FileSystem; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.Runtime.ShellFeatures; + +/// +/// Installs and configures workflow runtime features. +/// +[ShellFeature( + DisplayName = "Workflow Runtime", + Description = "Provides workflow execution runtime and scheduling capabilities", + DependsOn = ["Workflows"])] +public class WorkflowRuntimeFeature : IShellFeature +{ + private IDictionary WorkflowDispatcherChannels { get; set; } = new Dictionary(); + + /// + /// A list of workflow builders configured during application startup. + /// + public IDictionary>> Workflows { get; set; } = new Dictionary>>(); + + /// + /// A factory that instantiates a concrete . + /// + public Func WorkflowRuntime { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowDispatcher { get; set; } = sp => + { + var decoratedService = ActivatorUtilities.CreateInstance(sp); + return ActivatorUtilities.CreateInstance(sp, decoratedService); + }; + + /// + /// A factory that instantiates an . + /// + public Func StimulusDispatcher { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowCancellationDispatcher { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates an . + /// + public Func BookmarkStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func BookmarkQueueStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func TriggerStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowExecutionLogStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func ActivityExecutionLogStore { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func DistributedLockProvider { get; set; } = _ => new FileDistributedSynchronizationProvider(new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, "App_Data", "locks"))); + + /// + /// A factory that instantiates an . + /// + public Func RunTaskDispatcher { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func BackgroundActivityScheduler { get; set; } = sp => ActivatorUtilities.CreateInstance(sp); + + /// + /// A factory that instantiates a log record sink for an . + /// + public Func> ActivityExecutionLogSink { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates a log record sink for an . + /// + public Func> WorkflowExecutionLogSink { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func DispatchWorkflowCommandHandler { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func WorkflowResumer { get; set; } = sp => sp.GetRequiredService(); + + /// + /// A factory that instantiates an . + /// + public Func BookmarkQueueWorker { get; set; } = sp => sp.GetRequiredService(); + + + public void ConfigureServices(IServiceCollection services) + { + // Options. + services.Configure(options => { options.Workflows = Workflows; }); + services.Configure(options => + { + options.Channels.AddRange(WorkflowDispatcherChannels.Values); + }); + + services + // Core. + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(WorkflowRuntime) + .AddScoped(WorkflowDispatcher) + .AddScoped(StimulusDispatcher) + .AddScoped(WorkflowCancellationDispatcher) + .AddScoped(RunTaskDispatcher) + .AddScoped(ActivityExecutionLogSink) + .AddScoped(WorkflowExecutionLogSink) + .AddSingleton(BackgroundActivityScheduler) + .AddSingleton() + .AddSingleton() + .AddScoped(BookmarkQueueWorker) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped(WorkflowResumer) + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped, WorkflowExecutionLogRecordExtractor>() + .AddScoped() + .AddScoped() + .AddScoped() + .AddScoped() + + // Deprecated services. + .AddScoped() + + // Stores. + .AddScoped(BookmarkStore) + .AddScoped(BookmarkQueueStore) + .AddScoped(TriggerStore) + .AddScoped(WorkflowExecutionLogStore) + .AddScoped(ActivityExecutionLogStore) + + // Lazy services. + .AddScoped>>(sp => sp.GetServices) + .AddScoped>>(sp => sp.GetServices) + + // Noop stores. + .AddScoped() + .AddScoped() + + // Memory stores. + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore() + .AddMemoryStore() + + // Startup tasks, background tasks, and recurring tasks. + .AddStartupTask() + .AddRecurringTask(TimeSpan.FromMinutes(1)) + .AddRecurringTask(TimeSpan.FromSeconds(10)) + .AddRecurringTask(TimeSpan.FromMinutes(5)) // Same default as the workflow liveness threshold. + + // Distributed locking. + .AddSingleton(DistributedLockProvider) + + // Workflow definition providers. + .AddWorkflowDefinitionProvider() + + // UI property handlers. + .AddScoped() + + // Domain handlers. + .AddCommandHandler() + .AddCommandHandler() + .AddCommandHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + .AddNotificationHandler() + + // Workflow activation strategies. + .AddScoped() + .AddScoped() + .AddScoped() + ; + } +} \ No newline at end of file diff --git a/src/modules/Elsa/Elsa.csproj b/src/modules/Elsa/Elsa.csproj index 08ccd9fb7..7dab0dff3 100644 --- a/src/modules/Elsa/Elsa.csproj +++ b/src/modules/Elsa/Elsa.csproj @@ -9,16 +9,16 @@ - - - + + + - - - - + + + + diff --git a/src/modules/Elsa/ShellFeatures/ElsaFeature.cs b/src/modules/Elsa/ShellFeatures/ElsaFeature.cs new file mode 100644 index 000000000..815b06fe6 --- /dev/null +++ b/src/modules/Elsa/ShellFeatures/ElsaFeature.cs @@ -0,0 +1,20 @@ +using CShells.Features; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.ShellFeatures; + +[ShellFeature( + DisplayName = "Elsa Core", + Description = "Core Elsa workflow system functionality", + DependsOn = [ + "WorkflowManagement", + "WorkflowRuntime" +])] +public class ElsaFeature : IShellFeature +{ + public void ConfigureServices(IServiceCollection services) + { + // Register the shell-based feature provider to bridge shell features to the Elsa feature API + services.AddSingleton(); + } +} \ No newline at end of file