diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index 3a77d06f0..820ac8cda 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -5,7 +5,7 @@ on: branches: - 'main' release: - types: [ prereleased, published ] + types: [ prereleased ] env: base_version: '3.3.0' feedz_feed_source: 'https://f.feedz.io/elsa-workflows/elsa-3/nuget/index.json' diff --git a/Directory.Packages.props b/Directory.Packages.props index 0395ab99e..157742de1 100644 --- a/Directory.Packages.props +++ b/Directory.Packages.props @@ -131,6 +131,7 @@ + @@ -167,6 +168,7 @@ + diff --git a/scripts/migrations/efcore-3.3.sh b/scripts/migrations/efcore-3.3.sh index 9d6198511..a285230b8 100755 --- a/scripts/migrations/efcore-3.3.sh +++ b/scripts/migrations/efcore-3.3.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash # Define the modules to update -mods=("Alterations" "Runtime" "Management" "Identity" "Labels") +mods=("Alterations" "Runtime" "Management" "Identity" "Labels" "Tenants") # Define the list of providers providers=("MySql" "SqlServer" "Sqlite" "PostgreSql" "Oracle") diff --git a/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj b/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj index 0b5e96890..67c92b74c 100644 --- a/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj +++ b/src/apps/Elsa.Server.Web/Elsa.Server.Web.csproj @@ -63,8 +63,4 @@ - - - - diff --git a/src/apps/Elsa.Server.Web/Program.cs b/src/apps/Elsa.Server.Web/Program.cs index 353359bf6..38df82113 100644 --- a/src/apps/Elsa.Server.Web/Program.cs +++ b/src/apps/Elsa.Server.Web/Program.cs @@ -13,6 +13,7 @@ using Elsa.EntityFrameworkCore.Modules.Alterations; using Elsa.EntityFrameworkCore.Modules.Identity; using Elsa.EntityFrameworkCore.Modules.Management; using Elsa.EntityFrameworkCore.Modules.Runtime; +using Elsa.EntityFrameworkCore.Modules.Tenants; using Elsa.Extensions; using Elsa.Features.Services; using Elsa.Identity.Multitenancy; @@ -24,6 +25,7 @@ using Elsa.MongoDb.Modules.Alterations; using Elsa.MongoDb.Modules.Identity; using Elsa.MongoDb.Modules.Management; using Elsa.MongoDb.Modules.Runtime; +using Elsa.MongoDb.Modules.Tenants; using Elsa.OpenTelemetry.Middleware; using Elsa.Secrets.Extensions; using Elsa.Secrets.Management.Tasks; @@ -73,6 +75,7 @@ const WorkflowRuntime workflowRuntime = WorkflowRuntime.Distributed; const DistributedCachingTransport distributedCachingTransport = DistributedCachingTransport.MassTransit; const MassTransitBroker massTransitBroker = MassTransitBroker.Memory; const bool useMultitenancy = false; +const bool useTenantsFromConfiguration = false; const bool useAgents = false; const bool useSecrets = false; const bool disableVariableWrappers = false; @@ -146,7 +149,7 @@ services ef.UseSqlServer(sqlServerConnectionString!); else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql) ef.UsePostgreSql(postgresConnectionString!); - else if(sqlDatabaseProvider == SqlDatabaseProvider.MySql) + else if (sqlDatabaseProvider == SqlDatabaseProvider.MySql) ef.UseMySql(mySqlConnectionString); else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb) ef.UsePostgreSql(cockroachDbConnectionString!); @@ -180,7 +183,7 @@ services ef.UseSqlServer(sqlServerConnectionString!); else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql) ef.UsePostgreSql(postgresConnectionString!); - else if(sqlDatabaseProvider == SqlDatabaseProvider.MySql) + else if (sqlDatabaseProvider == SqlDatabaseProvider.MySql) ef.UseMySql(mySqlConnectionString); else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb) ef.UsePostgreSql(cockroachDbConnectionString!); @@ -251,7 +254,7 @@ services } else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql) ef.UsePostgreSql(postgresConnectionString!); - else if(sqlDatabaseProvider == SqlDatabaseProvider.MySql) + else if (sqlDatabaseProvider == SqlDatabaseProvider.MySql) ef.UseMySql(mySqlConnectionString); else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb) ef.UsePostgreSql(cockroachDbConnectionString!); @@ -376,7 +379,7 @@ services ef.UseSqlServer(sqlServerConnectionString); else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql) ef.UsePostgreSql(postgresConnectionString); - else if(sqlDatabaseProvider == SqlDatabaseProvider.MySql) + else if (sqlDatabaseProvider == SqlDatabaseProvider.MySql) ef.UseMySql(mySqlConnectionString); else if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb) ef.UsePostgreSql(cockroachDbConnectionString!); @@ -452,8 +455,8 @@ services asb.AzureServiceBusOptions = options => configuration.GetSection("AzureServiceBus").Bind(options); }); } - - if(useKafka) + + if (useKafka) { elsa.UseKafka(kafka => { @@ -468,10 +471,10 @@ services .UseAgentPersistence(persistence => persistence.UseEntityFrameworkCore(ef => ef.UseSqlite(sp => sp.GetSqliteConnectionString()))) .UseAgentsApi() ; - + services.Configure(options => builder.Configuration.GetSection("Agents").Bind(options)); } - + if (useSecrets) { elsa @@ -482,11 +485,11 @@ services if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql) management.UseEntityFrameworkCore(ef => ef.UseSqlServer(sqlServerConnectionString) - ); + ); else if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql) - management.UseEntityFrameworkCore(ef => + management.UseEntityFrameworkCore(ef => ef.UsePostgreSql(postgresConnectionString) - ); + ); else management.UseEntityFrameworkCore(ef => { @@ -502,16 +505,44 @@ services { elsa.UseTenants(tenants => { - tenants.ConfigureOptions(options => + tenants.ConfigureMultitenancy(options => { - configuration.GetSection("Multitenancy").Bind(options); options.TenantResolverPipelineBuilder .Append() .Append() .Append() .Append(); }); - tenants.UseConfigurationBasedTenantsProvider(); + + if (useTenantsFromConfiguration) + { + tenants.UseConfigurationBasedTenantsProvider(options => configuration.GetSection("Multitenancy").Bind(options)); + } + else + { + tenants.UseStoreBasedTenantsProvider(); + + tenants.UseTenantManagement(management => + { + if (persistenceProvider == PersistenceProvider.MongoDb) + management.UseMongoDb(); + if (persistenceProvider == PersistenceProvider.Dapper) + throw new NotSupportedException("Dapper is not supported for tenant management."); + if (persistenceProvider == PersistenceProvider.EntityFrameworkCore) + { + management.UseEntityFrameworkCore(ef => + { + if (sqlDatabaseProvider == SqlDatabaseProvider.Sqlite) ef.UseSqlite(sqliteConnectionString); + if (sqlDatabaseProvider == SqlDatabaseProvider.SqlServer) ef.UseSqlServer(sqlServerConnectionString); + if (sqlDatabaseProvider == SqlDatabaseProvider.PostgreSql) ef.UsePostgreSql(postgresConnectionString); + if (sqlDatabaseProvider == SqlDatabaseProvider.MySql) ef.UseMySql(mySqlConnectionString); + if (sqlDatabaseProvider == SqlDatabaseProvider.CockroachDb) ef.UsePostgreSql(cockroachDbConnectionString); + }); + } + }); + + tenants.UseTenantManagementEndpoints(); + } }); elsa.UseTenantHttpRouting(); @@ -562,7 +593,7 @@ app.UseAuthentication(); app.UseAuthorization(); // Multitenancy. -if(useMultitenancy) +if (useMultitenancy) app.UseTenants(); // Elsa API endpoints for designer. diff --git a/src/apps/Elsa.Server.Web/appsettings.json b/src/apps/Elsa.Server.Web/appsettings.json index 13b84c684..889afc59e 100644 --- a/src/apps/Elsa.Server.Web/appsettings.json +++ b/src/apps/Elsa.Server.Web/appsettings.json @@ -12,6 +12,9 @@ "System.Net.Http": "Warning" } }, + "HostBuilder": { + "reloadConfigOnChange": true + }, "AllowedHosts": "*", "ConnectionStrings": { "Sqlite": "Data Source=App_Data/elsa.sqlite.db;Cache=Shared;", @@ -37,6 +40,19 @@ "Sqlite": "Data Source=App_Data/elsa.sqlite.db;Cache=Shared;" } } + }, + { + "Id": "tenant-1", + "Name": "Tenant 12 dd", + "Configuration": { + "Http": { + "Prefix": "/tenant-1", + "Host": "localhost:5001" + }, + "ConnectionStrings": { + "Sqlite": "Data Source=App_Data/elsa.sqlite.db;Cache=Shared;" + } + } } ] }, @@ -81,7 +97,7 @@ "Roles": [ "admin" ], - "TenantId": null + "TenantId": "" }, { "Id": "b0cd0e506e713a9d", diff --git a/src/common/Elsa.Testing.Shared.Component/Services/TestTenantsProvider.cs b/src/common/Elsa.Testing.Shared.Component/Services/TestTenantsProvider.cs index 0059cbc3a..8dffc68fa 100644 --- a/src/common/Elsa.Testing.Shared.Component/Services/TestTenantsProvider.cs +++ b/src/common/Elsa.Testing.Shared.Component/Services/TestTenantsProvider.cs @@ -1,17 +1,18 @@ using Elsa.Common.Multitenancy; -using Elsa.Tenants; +using JetBrains.Annotations; namespace Elsa.Testing.Shared.Services; +[UsedImplicitly] public class TestTenantsProvider(params string[] tenantIds) : ITenantsProvider { - public ValueTask> ListAsync(CancellationToken cancellationToken = default) + public Task> ListAsync(CancellationToken cancellationToken = default) { var tenants = tenantIds.Select(id => new Tenant {Id = id, Name = id}); - return new(tenants); + return Task.FromResult(tenants); } - public async ValueTask FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) + public async Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) { var query = (await ListAsync(cancellationToken)).AsQueryable(); return filter.Apply(query).FirstOrDefault(); diff --git a/src/modules/Elsa.Common/Elsa.Common.csproj b/src/modules/Elsa.Common/Elsa.Common.csproj index 105f9b8e5..7899635c9 100644 --- a/src/modules/Elsa.Common/Elsa.Common.csproj +++ b/src/modules/Elsa.Common/Elsa.Common.csproj @@ -18,6 +18,7 @@ + diff --git a/src/modules/Elsa.Common/Elsa.Common.csproj.DotSettings b/src/modules/Elsa.Common/Elsa.Common.csproj.DotSettings index 4a6456d86..298209647 100644 --- a/src/modules/Elsa.Common/Elsa.Common.csproj.DotSettings +++ b/src/modules/Elsa.Common/Elsa.Common.csproj.DotSettings @@ -7,7 +7,9 @@ True True True + True True True True + True True \ No newline at end of file diff --git a/src/modules/Elsa.Common/Features/BackgroundTasksFeature.cs b/src/modules/Elsa.Common/Features/BackgroundTasksFeature.cs deleted file mode 100644 index 400d6a144..000000000 --- a/src/modules/Elsa.Common/Features/BackgroundTasksFeature.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Elsa.Common.Multitenancy.HostedServices; -using Elsa.Common.RecurringTasks; -using Elsa.Extensions; -using Elsa.Features.Abstractions; -using Elsa.Features.Services; -using Microsoft.Extensions.DependencyInjection; - -namespace Elsa.Common.Features; - -public class BackgroundTasksFeature(IModule module) : FeatureBase(module) -{ - public override void Apply() - { - Services.AddSingleton(); - Services.AddScoped(); - Services.AddStartupTask(); - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Features/MultitenancyFeature.cs b/src/modules/Elsa.Common/Features/MultitenancyFeature.cs index 6c334c6d9..84facff7b 100644 --- a/src/modules/Elsa.Common/Features/MultitenancyFeature.cs +++ b/src/modules/Elsa.Common/Features/MultitenancyFeature.cs @@ -1,5 +1,8 @@ using Elsa.Common.Multitenancy; +using Elsa.Common.Multitenancy.EventHandlers; using Elsa.Common.Multitenancy.HostedServices; +using Elsa.Common.RecurringTasks; +using Elsa.Extensions; using Elsa.Features.Abstractions; using Elsa.Features.Services; using Microsoft.Extensions.DependencyInjection; @@ -26,9 +29,7 @@ public class MultitenancyFeature(IModule module) : FeatureBase(module) public override void ConfigureHostedServices() { Module - .ConfigureHostedService(1) - .ConfigureHostedService(1) - .ConfigureHostedService(1) + .ConfigureHostedService(-1) ; } @@ -38,9 +39,24 @@ public class MultitenancyFeature(IModule module) : FeatureBase(module) .AddSingleton() .AddSingleton() .AddSingleton() - .AddSingleton() + .AddSingleton() + + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) + + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) + + .AddSingleton() + .AddSingleton() + .AddSingleton() + .AddStartupTask() .AddScoped() .AddScoped() + .AddScoped() + .AddScoped() .AddScoped(_tenantsProviderFactory); } } \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Abstractions/MultitenantBackgroundService.cs b/src/modules/Elsa.Common/Multitenancy/Abstractions/MultitenantBackgroundService.cs deleted file mode 100644 index bafd2c14e..000000000 --- a/src/modules/Elsa.Common/Multitenancy/Abstractions/MultitenantBackgroundService.cs +++ /dev/null @@ -1,56 +0,0 @@ -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Elsa.Common.Multitenancy; - -/// -/// A hosted service that executes a task for each tenant. -/// -public abstract class MultitenantBackgroundService(IServiceScopeFactory serviceScopeFactory) : BackgroundService -{ - private bool _disposed; - protected IServiceScope ServiceScope { get; private set; } = default!; - protected ICollection Tenants { get; private set; } = default!; - protected IDictionary TenantScopes { get; private set; } = default!; - - public override async Task StartAsync(CancellationToken cancellationToken) - { - ServiceScope = serviceScopeFactory.CreateScope(); - var tenantScopeFactory = ServiceScope.ServiceProvider.GetRequiredService(); - var tenantsProvider = ServiceScope.ServiceProvider.GetRequiredService(); - Tenants = (await tenantsProvider.ListAsync(cancellationToken)).ToList(); - TenantScopes = Tenants.ToDictionary(x => x, x => tenantScopeFactory.CreateScope(x)); - - foreach (var entry in TenantScopes) - await StartAsync(entry.Value, cancellationToken); - - await base.StartAsync(cancellationToken); - } - - public override async Task StopAsync(CancellationToken cancellationToken) - { - if(_disposed) - return; - - foreach (var entry in TenantScopes) - await StopAsync(entry.Value, cancellationToken); - - foreach (var entry in TenantScopes) - await entry.Value.DisposeAsync(); - - ServiceScope.Dispose(); - await base.StopAsync(cancellationToken); - - _disposed = true; - } - - protected override async Task ExecuteAsync(CancellationToken stoppingToken) - { - foreach (var entry in TenantScopes) - await ExecuteAsync(entry.Value, stoppingToken); - } - - protected virtual Task StartAsync(TenantScope tenantScope, CancellationToken stoppingToken) => Task.CompletedTask; - protected virtual Task ExecuteAsync(TenantScope tenantScope, CancellationToken stoppingToken) => Task.CompletedTask; - protected virtual Task StopAsync(TenantScope tenantScope, CancellationToken stoppingToken) => Task.CompletedTask; -} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Abstractions/MultitenantHostedService.cs b/src/modules/Elsa.Common/Multitenancy/Abstractions/MultitenantHostedService.cs deleted file mode 100644 index 845685ae0..000000000 --- a/src/modules/Elsa.Common/Multitenancy/Abstractions/MultitenantHostedService.cs +++ /dev/null @@ -1,31 +0,0 @@ -using JetBrains.Annotations; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; - -namespace Elsa.Common.Multitenancy; - -/// -/// A hosted service that runs the start and stop methods for each tenant. -/// -[UsedImplicitly] -public abstract class MultitenantHostedService(IServiceScopeFactory serviceScopeFactory) : IHostedService -{ - public virtual async Task StartAsync(CancellationToken cancellationToken) => await RunThroughTenantsAsync(StartAsync, cancellationToken); - public virtual Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; - - protected virtual Task StartAsync(TenantScope tenantScope, CancellationToken cancellationToken) => Task.CompletedTask; - - private async Task RunThroughTenantsAsync(Func action, CancellationToken cancellationToken) - { - using var serviceScope = serviceScopeFactory.CreateScope(); - var tenantScopeFactory = serviceScope.ServiceProvider.GetRequiredService(); - var tenantsProvider = serviceScope.ServiceProvider.GetRequiredService(); - var tenants = (await tenantsProvider.ListAsync(cancellationToken)).ToList(); - - foreach (var tenant in tenants) - { - await using var tenantScope = tenantScopeFactory.CreateScope(tenant); - await action(tenantScope, cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Abstractions/TenantResolverBase.cs b/src/modules/Elsa.Common/Multitenancy/Abstractions/TenantResolverBase.cs index 01adf6422..db0966a5d 100644 --- a/src/modules/Elsa.Common/Multitenancy/Abstractions/TenantResolverBase.cs +++ b/src/modules/Elsa.Common/Multitenancy/Abstractions/TenantResolverBase.cs @@ -40,9 +40,4 @@ public abstract class TenantResolverBase : ITenantResolver /// Automatically resolves the tenant if the tenant ID is not null. /// protected TenantResolverResult AutoResolve(string? tenantId) => tenantId == null ? Unresolved() : Resolved(tenantId); - - public Task ResolveAsync(CancellationToken cancellationToken = default) - { - throw new NotImplementedException(); - } } \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Contracts/IBackgroundTaskStarter.cs b/src/modules/Elsa.Common/Multitenancy/Contracts/IBackgroundTaskStarter.cs new file mode 100644 index 000000000..4aaae4ebd --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/Contracts/IBackgroundTaskStarter.cs @@ -0,0 +1,7 @@ +namespace Elsa.Common.Multitenancy; + +public interface IBackgroundTaskStarter +{ + Task StartAsync(IBackgroundTask task, CancellationToken cancellationToken); + Task StopAsync(IBackgroundTask task, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Contracts/ITaskExecutor.cs b/src/modules/Elsa.Common/Multitenancy/Contracts/ITaskExecutor.cs new file mode 100644 index 000000000..3694f7f3d --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/Contracts/ITaskExecutor.cs @@ -0,0 +1,6 @@ +namespace Elsa.Common.Multitenancy; + +public interface ITaskExecutor +{ + Task ExecuteTaskAsync(ITask task, CancellationToken cancellationToken); +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantAccessor.cs b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantAccessor.cs index 09355ef5c..66dab5b99 100644 --- a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantAccessor.cs +++ b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantAccessor.cs @@ -6,5 +6,7 @@ public interface ITenantAccessor /// Get the current . /// /// Current tenant or null. - Tenant? Tenant { get; set; } + Tenant? Tenant { get; } + + IDisposable PushContext(Tenant? tenant); } \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantActivatedEvent.cs b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantActivatedEvent.cs new file mode 100644 index 000000000..8bba0657e --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantActivatedEvent.cs @@ -0,0 +1,6 @@ +namespace Elsa.Common.Multitenancy; + +public interface ITenantActivatedEvent +{ + Task TenantActivatedAsync(TenantActivatedEventArgs args); +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantContextInitializer.cs b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantContextInitializer.cs deleted file mode 100644 index c989076d4..000000000 --- a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantContextInitializer.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Elsa.Common.Multitenancy; - -public interface ITenantContextInitializer -{ - Task InitializeAsync(string tenantId, CancellationToken cancellationToken = default); -} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantDeactivatedEvent.cs b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantDeactivatedEvent.cs new file mode 100644 index 000000000..d1761057e --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantDeactivatedEvent.cs @@ -0,0 +1,6 @@ +namespace Elsa.Common.Multitenancy; + +public interface ITenantDeactivatedEvent +{ + Task TenantDeactivatedAsync(TenantDeactivatedEventArgs args); +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantService.cs b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantService.cs new file mode 100644 index 000000000..88b1e44fb --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantService.cs @@ -0,0 +1,68 @@ +namespace Elsa.Common.Multitenancy; + +public interface ITenantService +{ + /// + /// Finds a tenant by the given id. + /// + /// The tenant id. + /// The cancellation token. + /// The tenant or null if not found. + Task FindAsync(string id, CancellationToken cancellationToken = default); + + /// + /// Finds a tenant using the given filter. + /// + /// The filter. + /// The cancellation token. + Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default); + + /// + /// Gets a tenant by the given id. + /// + /// The tenant id. + /// The cancellation token. + /// The tenant. + /// Thrown if the tenant is not found. + Task GetAsync(string id, CancellationToken cancellationToken = default); + + /// + /// Gets a tenant by the given id. + /// + /// The filter. + /// The cancellation token. + /// The tenant. + /// Thrown if the tenant is not found. + Task GetAsync(TenantFilter filter, CancellationToken cancellationToken = default); + + /// + /// Lists all the tenants. + /// + /// The cancellation token. + /// The tenants. + Task> ListAsync(CancellationToken cancellationToken = default); + + /// + /// Lists all the tenants using the given filter. + /// + /// The filter. + /// The cancellation token. + /// The tenants. + Task> ListAsync(TenantFilter filter, CancellationToken cancellationToken = default); + + /// + /// Activates all tenants. + /// + Task ActivateTenantsAsync(CancellationToken cancellationToken = default); + + /// + /// Deactivates all tenants. + /// + Task DeactivateTenantsAsync(CancellationToken cancellationToken = default); + + /// + /// Invokes the and caches the result. + /// When new tenants are added, lifecycle events are triggered to ensure background tasks are updated. + /// + Task RefreshAsync(CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantsProvider.cs b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantsProvider.cs index 624945fa0..e5e7a494f 100644 --- a/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantsProvider.cs +++ b/src/modules/Elsa.Common/Multitenancy/Contracts/ITenantsProvider.cs @@ -8,10 +8,10 @@ public interface ITenantsProvider /// /// Lists all the tenants. /// - ValueTask> ListAsync(CancellationToken cancellationToken = default); + Task> ListAsync(CancellationToken cancellationToken = default); /// /// Finds a tenant by the given filter. /// - ValueTask FindAsync(TenantFilter filter, CancellationToken cancellationToken = default); + Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default); } \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Entities/Tenant.cs b/src/modules/Elsa.Common/Multitenancy/Entities/Tenant.cs index 03ebd398a..a832baa2c 100644 --- a/src/modules/Elsa.Common/Multitenancy/Entities/Tenant.cs +++ b/src/modules/Elsa.Common/Multitenancy/Entities/Tenant.cs @@ -22,7 +22,7 @@ public class Tenant : Entity public static readonly Tenant Default = new() { - Id = null!, + Id = string.Empty, Name = "Default" }; } diff --git a/src/modules/Elsa.Common/Multitenancy/EventArgs/TenantActivatedEventArgs.cs b/src/modules/Elsa.Common/Multitenancy/EventArgs/TenantActivatedEventArgs.cs new file mode 100644 index 000000000..a30edf8a2 --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/EventArgs/TenantActivatedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Elsa.Common.Multitenancy; + +public record TenantActivatedEventArgs(Tenant Tenant, TenantScope TenantScope, CancellationToken CancellationToken) : TenantEventArgs(Tenant, TenantScope, CancellationToken); \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/EventArgs/TenantDeactivatedEventArgs.cs b/src/modules/Elsa.Common/Multitenancy/EventArgs/TenantDeactivatedEventArgs.cs new file mode 100644 index 000000000..b6cb61ed5 --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/EventArgs/TenantDeactivatedEventArgs.cs @@ -0,0 +1,3 @@ +namespace Elsa.Common.Multitenancy; + +public record TenantDeactivatedEventArgs(Tenant Tenant, TenantScope TenantScope, CancellationToken CancellationToken) : TenantEventArgs(Tenant, TenantScope, CancellationToken); \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/EventArgs/TenantEventArgs.cs b/src/modules/Elsa.Common/Multitenancy/EventArgs/TenantEventArgs.cs new file mode 100644 index 000000000..f2813c12c --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/EventArgs/TenantEventArgs.cs @@ -0,0 +1,3 @@ +namespace Elsa.Common.Multitenancy; + +public record TenantEventArgs(Tenant Tenant, TenantScope TenantScope, CancellationToken CancellationToken); \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/EventHandlers/RunBackgroundTasks.cs b/src/modules/Elsa.Common/Multitenancy/EventHandlers/RunBackgroundTasks.cs new file mode 100644 index 000000000..33f684d6d --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/EventHandlers/RunBackgroundTasks.cs @@ -0,0 +1,34 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.Multitenancy.EventHandlers; + +public class RunBackgroundTasks : ITenantActivatedEvent, ITenantDeactivatedEvent +{ + private readonly ICollection _runningTasks = new List(); + private CancellationTokenSource _cancellationTokenSource = default!; + + public Task TenantActivatedAsync(TenantActivatedEventArgs args) + { + var cancellationToken = args.CancellationToken; + _cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + + var tenantScope = args.TenantScope; + var backgroundTasks = tenantScope.ServiceProvider.GetServices(); + var backgroundTaskStarter = tenantScope.ServiceProvider.GetRequiredService(); + + foreach (var backgroundTask in backgroundTasks) + { + var task = backgroundTaskStarter.StartAsync(backgroundTask, _cancellationTokenSource.Token); + if (!task.IsCompleted) _runningTasks.Add(task); + } + + return Task.CompletedTask; + } + + public Task TenantDeactivatedAsync(TenantDeactivatedEventArgs args) + { + _cancellationTokenSource.Cancel(); + _runningTasks.Clear(); + return Task.CompletedTask; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/EventHandlers/RunStartupTasks.cs b/src/modules/Elsa.Common/Multitenancy/EventHandlers/RunStartupTasks.cs new file mode 100644 index 000000000..0bcca5c03 --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/EventHandlers/RunStartupTasks.cs @@ -0,0 +1,19 @@ +using System.Reflection; +using Elsa.Common.RecurringTasks; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.Multitenancy.EventHandlers; + +public class RunStartupTasks : ITenantActivatedEvent +{ + public async Task TenantActivatedAsync(TenantActivatedEventArgs args) + { + var cancellationToken = args.CancellationToken; + var tenantScope = args.TenantScope; + var tasks = tenantScope.ServiceProvider.GetServices().OrderBy(x => x.GetType().GetCustomAttribute()?.Order ?? 0f).ToList(); + var taskExecutor = tenantScope.ServiceProvider.GetRequiredService(); + + foreach (var task in tasks) + await taskExecutor.ExecuteTaskAsync(task, cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/EventHandlers/StartRecurringTasks.cs b/src/modules/Elsa.Common/Multitenancy/EventHandlers/StartRecurringTasks.cs new file mode 100644 index 000000000..c89c79c70 --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/EventHandlers/StartRecurringTasks.cs @@ -0,0 +1,49 @@ +using Elsa.Common.RecurringTasks; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; + +namespace Elsa.Common.Multitenancy.EventHandlers; + +public class StartRecurringTasks(RecurringTaskScheduleManager scheduleManager, ILogger logger) : ITenantActivatedEvent, ITenantDeactivatedEvent +{ + private readonly ICollection _scheduledTimers = new List(); + private CancellationTokenSource _cancellationTokenSource = default!; + + public async Task TenantActivatedAsync(TenantActivatedEventArgs args) + { + var cancellationToken = args.CancellationToken; + _cancellationTokenSource = new CancellationTokenSource(); + var tenantScope = args.TenantScope; + var tasks = tenantScope.ServiceProvider.GetServices().ToList(); + var taskExecutor = tenantScope.ServiceProvider.GetRequiredService(); + + foreach (var task in tasks) + { + var schedule = scheduleManager.GetScheduleFor(task.GetType()); + var timer = schedule.CreateTimer(async () => + { + try + { + await taskExecutor.ExecuteTaskAsync(task, _cancellationTokenSource.Token); + } + catch (OperationCanceledException e) + { + logger.LogInformation(e, "Task {TaskType} was cancelled", task.GetType().Name); + } + + }); + _scheduledTimers.Add(timer); + await task.StartAsync(cancellationToken); + } + } + + public async Task TenantDeactivatedAsync(TenantDeactivatedEventArgs args) + { + var tenantScope = args.TenantScope; + _cancellationTokenSource.Cancel(); + foreach (var timer in _scheduledTimers) await timer.DisposeAsync(); + _scheduledTimers.Clear(); + var tasks = tenantScope.ServiceProvider.GetServices(); + foreach (var task in tasks) await task.StopAsync(args.CancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/HostedServices/ActivateTenants.cs b/src/modules/Elsa.Common/Multitenancy/HostedServices/ActivateTenants.cs new file mode 100644 index 000000000..bb5fed0f3 --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/HostedServices/ActivateTenants.cs @@ -0,0 +1,18 @@ +using JetBrains.Annotations; +using Microsoft.Extensions.Hosting; + +namespace Elsa.Common.Multitenancy.HostedServices; + +[UsedImplicitly] +public class ActivateTenants(ITenantService tenantService) : IHostedService +{ + public async Task StartAsync(CancellationToken cancellationToken) + { + await tenantService.ActivateTenantsAsync(cancellationToken); + } + + public async Task StopAsync(CancellationToken cancellationToken) + { + await tenantService.DeactivateTenantsAsync(cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/HostedServices/BackgroundTasksRunner.cs b/src/modules/Elsa.Common/Multitenancy/HostedServices/BackgroundTasksRunner.cs deleted file mode 100644 index 490682f1a..000000000 --- a/src/modules/Elsa.Common/Multitenancy/HostedServices/BackgroundTasksRunner.cs +++ /dev/null @@ -1,27 +0,0 @@ -using JetBrains.Annotations; -using Microsoft.Extensions.DependencyInjection; - -namespace Elsa.Common.Multitenancy.HostedServices; - -[UsedImplicitly] -public class BackgroundTasksRunner(IServiceScopeFactory serviceScopeFactory) : MultitenantBackgroundService(serviceScopeFactory) -{ - protected override async Task StartAsync(TenantScope tenantScope, CancellationToken stoppingToken) - { - var tasks = tenantScope.ServiceProvider.GetServices(); - foreach (var task in tasks) await task.StartAsync(stoppingToken); - } - - protected override async Task ExecuteAsync(TenantScope tenantScope, CancellationToken stoppingToken) - { - var tasks = tenantScope.ServiceProvider.GetServices(); - var taskExecutor = tenantScope.ServiceProvider.GetRequiredService(); - foreach (var task in tasks) await taskExecutor.ExecuteTaskAsync(task, stoppingToken); - } - - protected override async Task StopAsync(TenantScope tenantScope, CancellationToken stoppingToken) - { - var tasks = tenantScope.ServiceProvider.GetServices(); - foreach (var task in tasks) await task.StopAsync(stoppingToken); - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/HostedServices/RecurringTasksRunner.cs b/src/modules/Elsa.Common/Multitenancy/HostedServices/RecurringTasksRunner.cs deleted file mode 100644 index b18c7ef40..000000000 --- a/src/modules/Elsa.Common/Multitenancy/HostedServices/RecurringTasksRunner.cs +++ /dev/null @@ -1,36 +0,0 @@ -using Elsa.Common.RecurringTasks; -using JetBrains.Annotations; -using Microsoft.Extensions.DependencyInjection; - -namespace Elsa.Common.Multitenancy.HostedServices; - -[UsedImplicitly] -public class RecurringTasksRunner(IServiceScopeFactory serviceScopeFactory, RecurringTaskScheduleManager scheduleManager) : MultitenantBackgroundService(serviceScopeFactory) -{ - private readonly ICollection _scheduledTimers = new List(); - - protected override async Task StartAsync(TenantScope tenantScope, CancellationToken stoppingToken) - { - var tasks = tenantScope.ServiceProvider.GetServices().ToList(); - var taskExecutor = tenantScope.ServiceProvider.GetRequiredService(); - - foreach (var task in tasks) - { - var schedule = scheduleManager.GetScheduleFor(task.GetType()); - var timer = schedule.CreateTimer(async () => - { - await taskExecutor.ExecuteTaskAsync(task, stoppingToken); - }); - _scheduledTimers.Add(timer); - await task.StartAsync(stoppingToken); - } - } - - protected override async Task StopAsync(TenantScope tenantScope, CancellationToken stoppingToken) - { - foreach (var timer in _scheduledTimers) await timer.DisposeAsync(); - _scheduledTimers.Clear(); - var tasks = tenantScope.ServiceProvider.GetServices(); - foreach (var task in tasks) await task.StopAsync(stoppingToken); - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/HostedServices/StartupTasksRunner.cs b/src/modules/Elsa.Common/Multitenancy/HostedServices/StartupTasksRunner.cs deleted file mode 100644 index 69c088396..000000000 --- a/src/modules/Elsa.Common/Multitenancy/HostedServices/StartupTasksRunner.cs +++ /dev/null @@ -1,18 +0,0 @@ -using Elsa.Common.DistributedHosting; -using JetBrains.Annotations; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; - -namespace Elsa.Common.Multitenancy.HostedServices; - -[UsedImplicitly] -public class StartupTasksRunner(IServiceScopeFactory serviceScopeFactory, IOptions options) : MultitenantHostedService(serviceScopeFactory) -{ - protected override async Task StartAsync(TenantScope tenantScope, CancellationToken cancellationToken) - { - var tasks = tenantScope.ServiceProvider.GetServices(); - var taskExecutor = tenantScope.ServiceProvider.GetRequiredService(); - foreach (var task in tasks) - await taskExecutor.ExecuteTaskAsync(task, cancellationToken); - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/HostedServices/TaskExecutor.cs b/src/modules/Elsa.Common/Multitenancy/HostedServices/TaskExecutor.cs deleted file mode 100644 index 74ffc08dc..000000000 --- a/src/modules/Elsa.Common/Multitenancy/HostedServices/TaskExecutor.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Reflection; -using Elsa.Common.DistributedHosting; -using Elsa.Common.RecurringTasks; -using Medallion.Threading; -using Microsoft.Extensions.Options; - -namespace Elsa.Common.Multitenancy.HostedServices; - -public class TaskExecutor(IDistributedLockProvider distributedLockProvider, IOptions options) -{ - public async Task ExecuteTaskAsync(ITask task, CancellationToken cancellationToken) - { - var taskType = task.GetType(); - var singleNodeTask = taskType.GetCustomAttribute() != null; - - if (singleNodeTask) - { - var resourceName = taskType.AssemblyQualifiedName!; - await using (await distributedLockProvider.AcquireLockAsync(resourceName, options.Value.LockAcquisitionTimeout, cancellationToken: cancellationToken)) - await task.ExecuteAsync(cancellationToken); - } - else - { - await task.ExecuteAsync(cancellationToken); - } - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantAccessor.cs b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantAccessor.cs index 7325c664c..fd8a699ba 100644 --- a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantAccessor.cs +++ b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantAccessor.cs @@ -11,6 +11,29 @@ public class DefaultTenantAccessor : ITenantAccessor public Tenant? Tenant { get => CurrentTenantField.Value; - set => CurrentTenantField.Value = value; + internal set => CurrentTenantField.Value = value; + } + + public IDisposable PushContext(Tenant? tenant) + { + return new CurrentTenantScope(tenant, Tenant, t => Tenant = t); + } +} + +public class CurrentTenantScope : IDisposable +{ + private readonly Tenant? _previousTenant; + private readonly Action _apply; + + public CurrentTenantScope(Tenant? newTenant, Tenant? previousTenant, Action apply) + { + _previousTenant = previousTenant; + _apply = apply; + _apply(newTenant); + } + + public void Dispose() + { + _apply(_previousTenant); } } \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantContextInitializer.cs b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantContextInitializer.cs deleted file mode 100644 index 0b3b929eb..000000000 --- a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantContextInitializer.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Elsa.Common.Multitenancy; - -public class DefaultTenantContextInitializer(ITenantFinder tenantFinder, ITenantAccessor tenantAccessor) : ITenantContextInitializer -{ - public async Task InitializeAsync(string tenantId, CancellationToken cancellationToken = default) - { - var tenant = await tenantFinder.FindByIdAsync(tenantId, cancellationToken); - tenantAccessor.Tenant = tenant; - } -} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantResolver.cs b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantResolver.cs index 890375d5a..112aa9f2a 100644 --- a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantResolver.cs +++ b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantResolver.cs @@ -7,7 +7,7 @@ public class DefaultTenantResolver : TenantResolverBase { private readonly Tenant _defaultTenant = new() { - Id = null!, + Id = string.Empty, Name = "Default" }; diff --git a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantService.cs b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantService.cs new file mode 100644 index 000000000..9db13dc36 --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantService.cs @@ -0,0 +1,124 @@ +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Common.Multitenancy; + +public class DefaultTenantService(IServiceScopeFactory scopeFactory, ITenantScopeFactory tenantScopeFactory, TenantEventsManager tenantEvents, ITenantAccessor tenantAccessor) : ITenantService, IAsyncDisposable +{ + private readonly AsyncServiceScope _serviceScope = scopeFactory.CreateAsyncScope(); + private IDictionary? _tenantsDictionary; + private IDictionary? _tenantScopesDictionary; + + public async ValueTask DisposeAsync() + { + await _serviceScope.DisposeAsync(); + } + + public async Task FindAsync(string id, CancellationToken cancellationToken = default) + { + var dictionary = await GetTenantsDictionaryAsync(cancellationToken); + return dictionary.TryGetValue(id, out var tenant) ? tenant : null; + } + + public async Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + var dictionary = await GetTenantsDictionaryAsync(cancellationToken); + return filter.Apply(dictionary.Values.AsQueryable()).FirstOrDefault(); + } + + public async Task GetAsync(string id, CancellationToken cancellationToken = default) + { + var dictionary = await GetTenantsDictionaryAsync(cancellationToken); + return dictionary[id]; + } + + public async Task GetAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + var dictionary = await GetTenantsDictionaryAsync(cancellationToken); + return filter.Apply(dictionary.Values.AsQueryable()).First(); + } + + public async Task> ListAsync(CancellationToken cancellationToken = default) + { + var dictionary = await GetTenantsDictionaryAsync(cancellationToken); + return dictionary.Values; + } + + public async Task> ListAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + var dictionary = await GetTenantsDictionaryAsync(cancellationToken); + return filter.Apply(dictionary.Values.AsQueryable()); + } + + public async Task ActivateTenantsAsync(CancellationToken cancellationToken = default) + { + await RefreshAsync(cancellationToken); + } + + public async Task DeactivateTenantsAsync(CancellationToken cancellationToken = default) + { + var tenants = _tenantsDictionary!.Values.ToArray(); + + foreach (var tenant in tenants) + await UnregisterTenantAsync(tenant, cancellationToken); + } + + public async Task RefreshAsync(CancellationToken cancellationToken = default) + { + await using var scope = scopeFactory.CreateAsyncScope(); + var tenantsProvider = scope.ServiceProvider.GetRequiredService(); + var currentTenants = await GetTenantsDictionaryAsync(cancellationToken); + var currentTenantIds = currentTenants.Keys; + var newTenants = (await tenantsProvider.ListAsync(cancellationToken)).ToDictionary(x => x.Id); + var newTenantIds = newTenants.Keys; + var removedTenantIds = currentTenantIds.Except(newTenantIds).ToArray(); + var addedTenantIds = newTenantIds.Except(currentTenantIds).ToArray(); + + foreach (var removedTenantId in removedTenantIds) + { + var removedTenant = currentTenants[removedTenantId]; + await UnregisterTenantAsync(removedTenant, cancellationToken); + } + + foreach (var addedTenantId in addedTenantIds) + { + var addedTenant = newTenants[addedTenantId]; + await RegisterTenantAsync(addedTenant, cancellationToken); + } + } + + private async Task> GetTenantsDictionaryAsync(CancellationToken cancellationToken) + { + if (_tenantsDictionary == null) + { + _tenantsDictionary = new Dictionary(); + _tenantScopesDictionary = new Dictionary(); + var tenantsProvider = _serviceScope.ServiceProvider.GetRequiredService(); + var tenants = await tenantsProvider.ListAsync(cancellationToken); + + foreach (var tenant in tenants) + await RegisterTenantAsync(tenant, cancellationToken); + } + + return _tenantsDictionary; + } + + private async Task RegisterTenantAsync(Tenant tenant, CancellationToken cancellationToken = default) + { + var scope = tenantScopeFactory.CreateScope(tenant); + _tenantsDictionary![tenant.Id] = tenant; + _tenantScopesDictionary![tenant] = scope; + + using (tenantAccessor.PushContext(tenant)) + await tenantEvents.TenantActivatedAsync(new TenantActivatedEventArgs(tenant, scope, cancellationToken)); + } + + private async Task UnregisterTenantAsync(Tenant tenant, CancellationToken cancellationToken = default) + { + var scope = _tenantScopesDictionary![tenant]; + _tenantsDictionary!.Remove(tenant.Id); + _tenantScopesDictionary!.Remove(tenant); + + using (tenantAccessor.PushContext(tenant)) + await tenantEvents.TenantDeactivatedAsync(new TenantDeactivatedEventArgs(tenant, scope, cancellationToken)); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantsProvider.cs b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantsProvider.cs index bc9fd2d61..7618205a5 100644 --- a/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantsProvider.cs +++ b/src/modules/Elsa.Common/Multitenancy/Implementations/DefaultTenantsProvider.cs @@ -10,14 +10,14 @@ public class DefaultTenantsProvider : ITenantsProvider { private readonly Tenant[] _tenants = { Tenant.Default }; - public ValueTask> ListAsync(CancellationToken cancellationToken = default) + public Task> ListAsync(CancellationToken cancellationToken = default) { - return new(_tenants); + return Task.FromResult>(_tenants); } - public ValueTask FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) + public Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) { var queryable = _tenants.AsQueryable(); - return new ValueTask(filter.Apply(queryable).FirstOrDefault()); + return Task.FromResult(filter.Apply(queryable).FirstOrDefault()); } } \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Implementations/TaskExecutor.cs b/src/modules/Elsa.Common/Multitenancy/Implementations/TaskExecutor.cs new file mode 100644 index 000000000..6f0062859 --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/Implementations/TaskExecutor.cs @@ -0,0 +1,42 @@ +using System.Reflection; +using Elsa.Common.DistributedHosting; +using Elsa.Common.RecurringTasks; +using Medallion.Threading; +using Microsoft.Extensions.Options; + +namespace Elsa.Common.Multitenancy; + +public class TaskExecutor(IDistributedLockProvider distributedLockProvider, IOptions options) : ITaskExecutor, IBackgroundTaskStarter +{ + public async Task ExecuteTaskAsync(ITask task, CancellationToken cancellationToken) + { + await ExecuteInternalAsync(task, () => task.ExecuteAsync(cancellationToken), cancellationToken); + } + + public async Task StartAsync(IBackgroundTask task, CancellationToken cancellationToken) + { + await ExecuteInternalAsync(task, () => task.StartAsync(cancellationToken), cancellationToken); + } + + public async Task StopAsync(IBackgroundTask task, CancellationToken cancellationToken) + { + await ExecuteInternalAsync(task, () => task.StopAsync(cancellationToken), cancellationToken); + } + + private async Task ExecuteInternalAsync(ITask task, Func action, CancellationToken cancellationToken) + { + var taskType = task.GetType(); + var singleNodeTask = taskType.GetCustomAttribute() != null; + + if (singleNodeTask) + { + var resourceName = taskType.AssemblyQualifiedName!; + await using (await distributedLockProvider.AcquireLockAsync(resourceName, options.Value.LockAcquisitionTimeout, cancellationToken: cancellationToken)) + await action(); + } + else + { + await action(); + } + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Implementations/TenantEventsManager.cs b/src/modules/Elsa.Common/Multitenancy/Implementations/TenantEventsManager.cs new file mode 100644 index 000000000..1c8e85a85 --- /dev/null +++ b/src/modules/Elsa.Common/Multitenancy/Implementations/TenantEventsManager.cs @@ -0,0 +1,36 @@ +using Microsoft.Extensions.Logging; + +namespace Elsa.Common.Multitenancy; + +public class TenantEventsManager(IEnumerable tenantActivatedEvents, IEnumerable tenantDeactivatedEvents, ILogger logger) +{ + public async Task TenantActivatedAsync(TenantActivatedEventArgs args) + { + foreach (var tenantActivatedEvent in tenantActivatedEvents) + { + try + { + await tenantActivatedEvent.TenantActivatedAsync(args); + } + catch (Exception e) + { + logger.LogError(e, "Error occurred while processing tenant activated event."); + } + } + } + + public async Task TenantDeactivatedAsync(TenantDeactivatedEventArgs args) + { + foreach (var tenantDeactivatedEvent in tenantDeactivatedEvents) + { + try + { + await tenantDeactivatedEvent.TenantDeactivatedAsync(args); + } + catch (Exception e) + { + logger.LogError(e, "Error occurred while processing tenant deactivated event."); + } + } + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Models/TenantFilter.cs b/src/modules/Elsa.Common/Multitenancy/Models/TenantFilter.cs index 919fba5b2..1fc1c1fa1 100644 --- a/src/modules/Elsa.Common/Multitenancy/Models/TenantFilter.cs +++ b/src/modules/Elsa.Common/Multitenancy/Models/TenantFilter.cs @@ -24,4 +24,6 @@ public class TenantFilter return queryable; } + + public static TenantFilter ById(string id) => new() { Id = id }; } \ No newline at end of file diff --git a/src/modules/Elsa.Common/Multitenancy/Models/TenantScope.cs b/src/modules/Elsa.Common/Multitenancy/Models/TenantScope.cs index 9c937f734..33d6fb260 100644 --- a/src/modules/Elsa.Common/Multitenancy/Models/TenantScope.cs +++ b/src/modules/Elsa.Common/Multitenancy/Models/TenantScope.cs @@ -6,19 +6,16 @@ namespace Elsa.Common.Multitenancy; /// Represents a tenant scope, which sets the current tenant for the duration of the scope. /// After the scope is disposed, the original tenant is restored. /// -public class TenantScope : IAsyncDisposable +public class TenantScope(IServiceScope serviceScope, ITenantAccessor tenantAccessor, Tenant? tenant) : IAsyncDisposable { - public TenantScope(IServiceScope serviceScope, ITenantAccessor tenantAccessor, Tenant? tenant) - { - ServiceScope = serviceScope; - tenantAccessor.Tenant = tenant; - } - - public IServiceScope ServiceScope { get; } + private readonly IDisposable _tenantContext = tenantAccessor.PushContext(tenant); + + public IServiceScope ServiceScope { get; } = serviceScope; public IServiceProvider ServiceProvider => ServiceScope.ServiceProvider; public async ValueTask DisposeAsync() { + _tenantContext.Dispose(); if (ServiceScope is IAsyncDisposable asyncDisposable) await asyncDisposable.DisposeAsync(); else diff --git a/src/modules/Elsa.Common/RecurringTasks/OrderAttribute.cs b/src/modules/Elsa.Common/RecurringTasks/OrderAttribute.cs new file mode 100644 index 000000000..b0efd88b7 --- /dev/null +++ b/src/modules/Elsa.Common/RecurringTasks/OrderAttribute.cs @@ -0,0 +1,10 @@ +namespace Elsa.Common.RecurringTasks; + +/// +/// Configures a task with a priority. +/// +[AttributeUsage(AttributeTargets.Class)] +public class OrderAttribute(float order) : Attribute +{ + public float Order { get; } = order; +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Serialization/ConfigurationJsonConverter.cs b/src/modules/Elsa.Common/Serialization/ConfigurationJsonConverter.cs new file mode 100644 index 000000000..e4a38f799 --- /dev/null +++ b/src/modules/Elsa.Common/Serialization/ConfigurationJsonConverter.cs @@ -0,0 +1,45 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using Microsoft.Extensions.Configuration; + +namespace Elsa.Common.Serialization; + +public class ConfigurationJsonConverter : JsonConverter +{ + public override IConfiguration? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) + { + var jsonElement = JsonElement.ParseValue(ref reader); + using var memoryStream = new MemoryStream(); + using var writer = new Utf8JsonWriter(memoryStream); + jsonElement.WriteTo(writer); + writer.Flush(); + memoryStream.Position = 0; + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddJsonStream(memoryStream); + return configurationBuilder.Build(); + } + + public override void Write(Utf8JsonWriter writer, IConfiguration value, JsonSerializerOptions options) + { + var dictionary = new Dictionary(); + foreach (var child in value.GetChildren()) + dictionary[child.Key] = GetValue(child); + + JsonSerializer.Serialize(writer, dictionary, options); + } + + private static object? GetValue(IConfigurationSection section) + { + var children = section.GetChildren().ToList(); + + if (!children.Any()) + return section.Value; + + var dict = new Dictionary(); + foreach (var child in children) + dict[child.Key] = GetValue(child); + + return dict; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Serialization/SerializerOptions.cs b/src/modules/Elsa.Common/Serialization/SerializerOptions.cs new file mode 100644 index 000000000..f55c1e6ca --- /dev/null +++ b/src/modules/Elsa.Common/Serialization/SerializerOptions.cs @@ -0,0 +1,11 @@ +using System.Text.Json; + +namespace Elsa.Common.Serialization; + +public static class SerializerOptions +{ + public static JsonSerializerOptions ConfigurationJsonSerializerOptions { get; } = new() + { + Converters = { new ConfigurationJsonConverter() } + }; +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Serialization/Serializers.cs b/src/modules/Elsa.Common/Serialization/Serializers.cs new file mode 100644 index 000000000..f4c9bd745 --- /dev/null +++ b/src/modules/Elsa.Common/Serialization/Serializers.cs @@ -0,0 +1,25 @@ +using System.Text.Json; +using Microsoft.Extensions.Configuration; + +namespace Elsa.Common.Serialization; + +public static class Serializers +{ + public static IConfiguration DeserializeConfiguration(JsonElement? json) + { + return DeserializeConfiguration(json?.GetRawText()); + } + + public static IConfiguration DeserializeConfiguration(string? json) + { + if (json == null) + return new ConfigurationBuilder().Build(); + + return JsonSerializer.Deserialize(json, SerializerOptions.ConfigurationJsonSerializerOptions)!; + } + + public static string SerializeConfiguration(IConfiguration configuration) + { + return JsonSerializer.Serialize(configuration, SerializerOptions.ConfigurationJsonSerializerOptions); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Common/Services/MemoryStore.cs b/src/modules/Elsa.Common/Services/MemoryStore.cs index d08fc8603..cbbb7f84f 100644 --- a/src/modules/Elsa.Common/Services/MemoryStore.cs +++ b/src/modules/Elsa.Common/Services/MemoryStore.cs @@ -10,6 +10,11 @@ namespace Elsa.Common.Services; public class MemoryStore { private IDictionary Entities { get; set; } = new ConcurrentDictionary(); + + /// + /// Gets a queryable of all entities. + /// + public IQueryable Queryable => Entities.Values.AsQueryable(); /// /// Adds an entity. diff --git a/src/modules/Elsa.EntityFrameworkCore.Common/PersistenceFeatureBase.cs b/src/modules/Elsa.EntityFrameworkCore.Common/PersistenceFeatureBase.cs index f788eda18..031f69397 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Common/PersistenceFeatureBase.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Common/PersistenceFeatureBase.cs @@ -1,4 +1,5 @@ using Elsa.Common.Entities; +using Elsa.Extensions; using Elsa.Features.Abstractions; using Elsa.Features.Attributes; using Elsa.Features.Services; @@ -44,11 +45,10 @@ public abstract class PersistenceFeatureBase : FeatureBase .MigrationsAssembly("Elsa.EntityFrameworkCore.Sqlite") .MigrationsHistoryTable(ElsaDbContextBase.MigrationsHistoryTable, ElsaDbContextBase.ElsaSchema)); - /// public override void ConfigureHostedServices() { if (RunMigrations) - Module.ConfigureHostedService>(-100); // Migrations need to run before other hosted services that depend on DB access. + ConfigureMigrations(); } /// @@ -60,6 +60,11 @@ public abstract class PersistenceFeatureBase : FeatureBase Services.AddDbContextFactory(DbContextOptionsBuilder, DbContextFactoryLifetime); } + protected virtual void ConfigureMigrations() + { + Services.AddStartupTask>(); + } + /// /// Adds a store to the service collection. /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Common/RunMigrationsHostedService.cs b/src/modules/Elsa.EntityFrameworkCore.Common/RunMigrationsHostedService.cs index a2dc5fddd..9bd0f1ac6 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Common/RunMigrationsHostedService.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Common/RunMigrationsHostedService.cs @@ -1,21 +1,33 @@ -using Elsa.Common.Multitenancy; -using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; namespace Elsa.EntityFrameworkCore; /// /// Executes EF Core migrations using the specified type. /// -[UsedImplicitly] -public class RunMigrationsHostedService(IServiceScopeFactory scopeFactory) : MultitenantHostedService(scopeFactory) where TDbContext : DbContext +public class RunMigrationsHostedService : IHostedService where TDbContext : DbContext { - /// - protected override async Task StartAsync(TenantScope tenantScope, CancellationToken cancellationToken) + private readonly IServiceScopeFactory _scopeFactory; + + /// + /// Initializes a new instance of the class. + /// + public RunMigrationsHostedService(IServiceScopeFactory scopeFactory) { - var dbContextFactory = tenantScope.ServiceProvider.GetRequiredService>(); - var tenantDbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); - await tenantDbContext.Database.MigrateAsync(cancellationToken); + _scopeFactory = scopeFactory; } + + /// + public async Task StartAsync(CancellationToken cancellationToken) + { + using var scope = _scopeFactory.CreateScope(); + var dbContextFactory = scope.ServiceProvider.GetRequiredService>(); + await using var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken); + await dbContext.Database.MigrateAsync(cancellationToken); + } + + /// + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; } \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore.Common/RunMigrationsStartupTask.cs b/src/modules/Elsa.EntityFrameworkCore.Common/RunMigrationsStartupTask.cs new file mode 100644 index 000000000..f55e3b647 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.Common/RunMigrationsStartupTask.cs @@ -0,0 +1,22 @@ +using Elsa.Common; +using Elsa.Common.RecurringTasks; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; + +namespace Elsa.EntityFrameworkCore; + +/// +/// Executes EF Core migrations using the specified type. +/// +[UsedImplicitly] +[SingleNodeTask] +[Order(-100)] +public class RunMigrationsStartupTask(IDbContextFactory dbContextFactory) : IStartupTask where TDbContext : DbContext +{ + /// -{ -} +public class IdentityDbContextFactory : MySqlDesignTimeDbContextFactory; [UsedImplicitly] -public class ManagementDbContextFactory : MySqlDesignTimeDbContextFactory -{ -} +public class ManagementDbContextFactory : MySqlDesignTimeDbContextFactory; [UsedImplicitly] -public class RuntimeDbContextFactory : MySqlDesignTimeDbContextFactory -{ -} +public class RuntimeDbContextFactory : MySqlDesignTimeDbContextFactory; [UsedImplicitly] -public class LabelsDbContextFactory : MySqlDesignTimeDbContextFactory -{ -} +public class LabelsDbContextFactory : MySqlDesignTimeDbContextFactory; [UsedImplicitly] -public class AlterationsDbContextFactories : MySqlDesignTimeDbContextFactory -{ -} +public class AlterationsDbContextFactories : MySqlDesignTimeDbContextFactory; + +[UsedImplicitly] +public class TenantsDbContextFactories : MySqlDesignTimeDbContextFactory; public class MySqlDesignTimeDbContextFactory : DesignTimeDbContextFactoryBase where TDbContext : DbContext { diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20231015122151_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20231015122151_Initial.cs index b9525e6a1..09c731fb3 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20231015122151_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20231015122151_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20240329200618_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20240329200618_V3_1.cs index cf0ac526b..56b1865c0 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20240329200618_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20240329200618_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Alterations /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20240610184517_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20240610184517_V3_2.cs index 8c899c691..ca400108f 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20240610184517_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20240610184517_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Alterations /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20241109102522_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20241202095004_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20241109102522_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20241202095004_V3_3.Designer.cs index 672fe5299..fcf2b0c5f 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20241109102522_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20241202095004_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.MySql.Migrations.Alterations { [DbContext(typeof(AlterationsElsaDbContext))] - [Migration("20241109102522_V3_3")] + [Migration("20241202095004_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20241109102522_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20241202095004_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20241109102522_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Alterations/20241202095004_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20231015122238_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20231015122238_Initial.cs index f0fd329be..69da23b23 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20231015122238_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20231015122238_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20240329200746_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20240329200746_V3_1.cs index f519f355e..36999aa94 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20240329200746_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20240329200746_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Identity /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20240610184903_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20240610184903_V3_2.cs index 2b88f9202..d514c417f 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20240610184903_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20240610184903_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Identity /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20241109102921_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20241202095402_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20241109102921_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20241202095402_V3_3.Designer.cs index 0f4c662c1..bcbe58c02 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20241109102921_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20241202095402_V3_3.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.MySql.Migrations.Identity { [DbContext(typeof(IdentityElsaDbContext))] - [Migration("20241109102921_V3_3")] + [Migration("20241202095402_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20241109102921_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20241202095402_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20241109102921_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Identity/20241202095402_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20231015122253_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20231015122253_Initial.cs index 3e3ff9f5d..16cb06abf 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20231015122253_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20231015122253_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20240329200817_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20240329200817_V3_1.cs index f72754325..c6ee183fe 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20240329200817_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20240329200817_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Labels /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20240610184957_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20240610184957_V3_2.cs index 4c0682521..290adf17a 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20240610184957_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20240610184957_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Labels /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20241109103004_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20241202095515_V3_3.Designer.cs similarity index 98% rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20241109103004_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20241202095515_V3_3.Designer.cs index 66576ea43..660df2293 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20241109103004_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20241202095515_V3_3.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.MySql.Migrations.Labels { [DbContext(typeof(LabelsElsaDbContext))] - [Migration("20241109103004_V3_3")] + [Migration("20241202095515_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20241109103004_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20241202095515_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20241109103004_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Labels/20241202095515_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20231015122223_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20231015122223_Initial.cs index 0fdb07282..7c52ef258 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20231015122223_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20231015122223_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20240329200720_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20240329200720_V3_1.cs index 2dcaa7ffe..e4fa114e6 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20240329200720_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20240329200720_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Management /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20240610184802_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20240610184802_V3_2.cs index 51ff05acd..523fc6f4a 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20240610184802_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20240610184802_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Management /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20241109102839_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20241202095327_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20241109102839_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20241202095327_V3_3.Designer.cs index 228653e6c..94cb2a082 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20241109102839_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20241202095327_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.MySql.Migrations.Management { [DbContext(typeof(ManagementElsaDbContext))] - [Migration("20241109102839_V3_3")] + [Migration("20241202095327_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20241109102839_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20241202095327_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20241109102839_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Management/20241202095327_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20231024160940_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20231024160940_Initial.cs index cf3eb698a..41b67f76a 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20231024160940_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20231024160940_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240329200651_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240329200651_V3_1.cs index 0092d23c1..dcda40b23 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240329200651_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240329200651_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240610184652_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240610184652_V3_2.cs index 978783615..891b4ba00 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240610184652_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20240610184652_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20241109102737_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20241202095210_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20241109102737_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20241202095210_V3_3.Designer.cs index f137c8184..8148fb929 100644 --- a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20241109102737_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20241202095210_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.MySql.Migrations.Runtime { [DbContext(typeof(RuntimeElsaDbContext))] - [Migration("20241109102737_V3_3")] + [Migration("20241202095210_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20241109102737_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20241202095210_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20241109102737_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Runtime/20241202095210_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Tenants/20241202095623_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Tenants/20241202095623_V3_3.Designer.cs new file mode 100644 index 000000000..31e86a582 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Tenants/20241202095623_V3_3.Designer.cs @@ -0,0 +1,54 @@ +// +using Elsa.EntityFrameworkCore.Modules.Tenants; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.MySql.Migrations.Tenants +{ + [DbContext(typeof(TenantsElsaDbContext))] + [Migration("20241202095623_V3_3")] + partial class V3_3 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Elsa.Common.Multitenancy.Tenant", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("TenantId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_Tenant_TenantId"); + + b.ToTable("Tenants", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Tenants/20241202095623_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Tenants/20241202095623_V3_3.cs new file mode 100644 index 000000000..a33332d75 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Tenants/20241202095623_V3_3.cs @@ -0,0 +1,68 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.MySql.Migrations.Tenants +{ + /// + public partial class V3_3 : Migration + { + private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + + /// + public V3_3(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + { + _schema = schema; + } + + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "Elsa"); + + migrationBuilder.AlterDatabase() + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateTable( + name: "Tenants", + schema: _schema.Schema, + columns: table => new + { + Id = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Name = table.Column(type: "varchar(255)", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + Configuration = table.Column(type: "longtext", nullable: false) + .Annotation("MySql:CharSet", "utf8mb4"), + TenantId = table.Column(type: "varchar(255)", nullable: true) + .Annotation("MySql:CharSet", "utf8mb4") + }, + constraints: table => + { + table.PrimaryKey("PK_Tenants", x => x.Id); + }) + .Annotation("MySql:CharSet", "utf8mb4"); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_Name", + schema: _schema.Schema, + table: "Tenants", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_TenantId", + schema: _schema.Schema, + table: "Tenants", + column: "TenantId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Tenants", + schema: _schema.Schema); + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs new file mode 100644 index 000000000..b02d009ad --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.MySql/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs @@ -0,0 +1,51 @@ +// +using Elsa.EntityFrameworkCore.Modules.Tenants; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.MySql.Migrations.Tenants +{ + [DbContext(typeof(TenantsElsaDbContext))] + partial class TenantsElsaDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 64); + + modelBuilder.Entity("Elsa.Common.Multitenancy.Tenant", b => + { + b.Property("Id") + .HasColumnType("varchar(255)"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("longtext"); + + b.Property("Name") + .IsRequired() + .HasColumnType("varchar(255)"); + + b.Property("TenantId") + .HasColumnType("varchar(255)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_Tenant_TenantId"); + + b.ToTable("Tenants", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/DbContextFactories.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/DbContextFactories.cs index 12bfa3a4f..6270c9d91 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Oracle/DbContextFactories.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Oracle/DbContextFactories.cs @@ -5,6 +5,7 @@ using Elsa.EntityFrameworkCore.Modules.Identity; using Elsa.EntityFrameworkCore.Modules.Labels; using Elsa.EntityFrameworkCore.Modules.Management; using Elsa.EntityFrameworkCore.Modules.Runtime; +using Elsa.EntityFrameworkCore.Modules.Tenants; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; @@ -13,29 +14,22 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.EntityFrameworkCore.Oracle; [UsedImplicitly] -public class IdentityDbContextFactory : OracleDesignTimeDbContextFactory -{ -} +public class IdentityDbContextFactory : OracleDesignTimeDbContextFactory; [UsedImplicitly] -public class ManagementDbContextFactory : OracleDesignTimeDbContextFactory -{ -} +public class ManagementDbContextFactory : OracleDesignTimeDbContextFactory; [UsedImplicitly] -public class RuntimeDbContextFactory : OracleDesignTimeDbContextFactory -{ -} +public class RuntimeDbContextFactory : OracleDesignTimeDbContextFactory; [UsedImplicitly] -public class LabelsDbContextFactory : OracleDesignTimeDbContextFactory -{ -} +public class LabelsDbContextFactory : OracleDesignTimeDbContextFactory; [UsedImplicitly] -public class AlterationsDbContextFactories : OracleDesignTimeDbContextFactory -{ -} +public class AlterationsDbContextFactories : OracleDesignTimeDbContextFactory; + +[UsedImplicitly] +public class TenantsDbContextFactories : OracleDesignTimeDbContextFactory; public class OracleDesignTimeDbContextFactory : DesignTimeDbContextFactoryBase where TDbContext : DbContext { diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Alterations/20241109102700_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Alterations/20241202095130_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Alterations/20241109102700_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Alterations/20241202095130_V3_3.Designer.cs index bf1e8bf59..1f5ba70a5 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Alterations/20241109102700_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Alterations/20241202095130_V3_3.Designer.cs @@ -12,7 +12,7 @@ using Oracle.EntityFrameworkCore.Metadata; namespace Elsa.EntityFrameworkCore.Oracle.Migrations.Alterations { [DbContext(typeof(AlterationsElsaDbContext))] - [Migration("20241109102700_V3_3")] + [Migration("20241202095130_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Alterations/20241109102700_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Alterations/20241202095130_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Alterations/20241109102700_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Alterations/20241202095130_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Identity/20241109102957_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Identity/20241202095500_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Identity/20241109102957_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Identity/20241202095500_V3_3.Designer.cs index 45de3c4db..5032e7618 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Identity/20241109102957_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Identity/20241202095500_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Oracle.EntityFrameworkCore.Metadata; namespace Elsa.EntityFrameworkCore.Oracle.Migrations.Identity { [DbContext(typeof(IdentityElsaDbContext))] - [Migration("20241109102957_V3_3")] + [Migration("20241202095500_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Identity/20241109102957_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Identity/20241202095500_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Identity/20241109102957_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Identity/20241202095500_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Labels/20241109103034_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Labels/20241202095616_V3_3.Designer.cs similarity index 98% rename from src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Labels/20241109103034_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Labels/20241202095616_V3_3.Designer.cs index 8ca380671..fa998614a 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Labels/20241109103034_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Labels/20241202095616_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Oracle.EntityFrameworkCore.Metadata; namespace Elsa.EntityFrameworkCore.Oracle.Migrations.Labels { [DbContext(typeof(LabelsElsaDbContext))] - [Migration("20241109103034_V3_3")] + [Migration("20241202095616_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Labels/20241109103034_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Labels/20241202095616_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Labels/20241109103034_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Labels/20241202095616_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Management/20241109102913_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Management/20241202095357_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Management/20241109102913_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Management/20241202095357_V3_3.Designer.cs index bb97d1bf6..6ebf69eb5 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Management/20241109102913_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Management/20241202095357_V3_3.Designer.cs @@ -12,7 +12,7 @@ using Oracle.EntityFrameworkCore.Metadata; namespace Elsa.EntityFrameworkCore.Oracle.Migrations.Management { [DbContext(typeof(ManagementElsaDbContext))] - [Migration("20241109102913_V3_3")] + [Migration("20241202095357_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Management/20241109102913_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Management/20241202095357_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Management/20241109102913_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Management/20241202095357_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Runtime/20241109102830_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Runtime/20241202095317_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Runtime/20241109102830_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Runtime/20241202095317_V3_3.Designer.cs index 8241699d7..945ec26f6 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Runtime/20241109102830_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Runtime/20241202095317_V3_3.Designer.cs @@ -12,7 +12,7 @@ using Oracle.EntityFrameworkCore.Metadata; namespace Elsa.EntityFrameworkCore.Oracle.Migrations.Runtime { [DbContext(typeof(RuntimeElsaDbContext))] - [Migration("20241109102830_V3_3")] + [Migration("20241202095317_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Runtime/20241109102830_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Runtime/20241202095317_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Runtime/20241109102830_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Runtime/20241202095317_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Tenants/20241202095654_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Tenants/20241202095654_V3_3.Designer.cs new file mode 100644 index 000000000..4ea554b11 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Tenants/20241202095654_V3_3.Designer.cs @@ -0,0 +1,57 @@ +// +using Elsa.EntityFrameworkCore.Modules.Tenants; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Oracle.EntityFrameworkCore.Metadata; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.Oracle.Migrations.Tenants +{ + [DbContext(typeof(TenantsElsaDbContext))] + [Migration("20241202095654_V3_3")] + partial class V3_3 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + OracleModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Elsa.Common.Multitenancy.Tenant", b => + { + b.Property("Id") + .HasColumnType("NVARCHAR2(450)"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("NVARCHAR2(2000)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("NVARCHAR2(450)"); + + b.Property("TenantId") + .HasColumnType("NVARCHAR2(450)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_Tenant_TenantId"); + + b.ToTable("Tenants", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Tenants/20241202095654_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Tenants/20241202095654_V3_3.cs new file mode 100644 index 000000000..8ca9f1711 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Tenants/20241202095654_V3_3.cs @@ -0,0 +1,60 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.Oracle.Migrations.Tenants +{ + /// + public partial class V3_3 : Migration + { + private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + + /// + public V3_3(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + { + _schema = schema; + } + + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "Elsa"); + + migrationBuilder.CreateTable( + name: "Tenants", + schema: _schema.Schema, + columns: table => new + { + Id = table.Column(type: "NVARCHAR2(450)", nullable: false), + Name = table.Column(type: "NVARCHAR2(450)", nullable: false), + Configuration = table.Column(type: "NVARCHAR2(2000)", nullable: false), + TenantId = table.Column(type: "NVARCHAR2(450)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Tenants", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_Name", + schema: _schema.Schema, + table: "Tenants", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_TenantId", + schema: _schema.Schema, + table: "Tenants", + column: "TenantId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Tenants", + schema: _schema.Schema); + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs new file mode 100644 index 000000000..464fe8f3f --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.Oracle/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs @@ -0,0 +1,54 @@ +// +using Elsa.EntityFrameworkCore.Modules.Tenants; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Oracle.EntityFrameworkCore.Metadata; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.Oracle.Migrations.Tenants +{ + [DbContext(typeof(TenantsElsaDbContext))] + partial class TenantsElsaDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + OracleModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Elsa.Common.Multitenancy.Tenant", b => + { + b.Property("Id") + .HasColumnType("NVARCHAR2(450)"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("NVARCHAR2(2000)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("NVARCHAR2(450)"); + + b.Property("TenantId") + .HasColumnType("NVARCHAR2(450)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_Tenant_TenantId"); + + b.ToTable("Tenants", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/DbContextFactories.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/DbContextFactories.cs index 6a629ba88..dfe77cf43 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/DbContextFactories.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/DbContextFactories.cs @@ -5,6 +5,7 @@ using Elsa.EntityFrameworkCore.Modules.Identity; using Elsa.EntityFrameworkCore.Modules.Labels; using Elsa.EntityFrameworkCore.Modules.Management; using Elsa.EntityFrameworkCore.Modules.Runtime; +using Elsa.EntityFrameworkCore.Modules.Tenants; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; @@ -13,29 +14,22 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.EntityFrameworkCore.PostgreSql; [UsedImplicitly] -public class IdentityDbContextFactory : PostgreSqlDesignTimeDbContextFactory -{ -} +public class IdentityDbContextFactory : PostgreSqlDesignTimeDbContextFactory; [UsedImplicitly] -public class ManagementDbContextFactory : PostgreSqlDesignTimeDbContextFactory -{ -} +public class ManagementDbContextFactory : PostgreSqlDesignTimeDbContextFactory; [UsedImplicitly] -public class RuntimeDbContextFactory : PostgreSqlDesignTimeDbContextFactory -{ -} +public class RuntimeDbContextFactory : PostgreSqlDesignTimeDbContextFactory; [UsedImplicitly] -public class LabelsDbContextFactory : PostgreSqlDesignTimeDbContextFactory -{ -} +public class LabelsDbContextFactory : PostgreSqlDesignTimeDbContextFactory; [UsedImplicitly] -public class AlterationsDbContextFactories : PostgreSqlDesignTimeDbContextFactory -{ -} +public class AlterationsDbContextFactories : PostgreSqlDesignTimeDbContextFactory; + +[UsedImplicitly] +public class TenantsDbContextFactories : PostgreSqlDesignTimeDbContextFactory; public class PostgreSqlDesignTimeDbContextFactory : DesignTimeDbContextFactoryBase where TDbContext : DbContext { diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20231015122203_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20231015122203_Initial.cs index 69208cdd5..e321ce433 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20231015122203_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20231015122203_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20240329200643_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20240329200643_V3_1.cs index fe20a7fa6..ccb181c7b 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20240329200643_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20240329200643_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Alterations /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20240610184626_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20240610184626_V3_2.cs index c18d33c11..28f51ffff 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20240610184626_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20240610184626_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Alterations /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20241109102646_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20241202095104_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20241109102646_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20241202095104_V3_3.Designer.cs index 6e3295992..dcead6a8a 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20241109102646_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20241202095104_V3_3.Designer.cs @@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Alterations { [DbContext(typeof(AlterationsElsaDbContext))] - [Migration("20241109102646_V3_3")] + [Migration("20241202095104_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20241109102646_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20241202095104_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20241109102646_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Alterations/20241202095104_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20231015122250_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20231015122250_Initial.cs index 937382787..4ae51be42 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20231015122250_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20231015122250_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20240329200810_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20240329200810_V3_1.cs index cb5dac026..f6f30f7dc 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20240329200810_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20240329200810_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Identity /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20240610184941_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20240610184941_V3_2.cs index 021c8cb52..d06fe1bca 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20240610184941_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20240610184941_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Identity /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20241109102950_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20241202095450_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20241109102950_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20241202095450_V3_3.Designer.cs index dfbd6043b..f02b7bf5e 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20241109102950_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20241202095450_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Identity { [DbContext(typeof(IdentityElsaDbContext))] - [Migration("20241109102950_V3_3")] + [Migration("20241202095450_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20241109102950_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20241202095450_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20241109102950_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Identity/20241202095450_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20231015122304_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20231015122304_Initial.cs index 3cfeff302..f8d3a3f9f 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20231015122304_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20231015122304_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20240329200841_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20240329200841_V3_1.cs index 9822c1ed8..84c472bdd 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20240329200841_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20240329200841_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Labels /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20240610185037_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20240610185037_V3_2.cs index 3a9a9f685..e41d8e45d 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20240610185037_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20240610185037_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Labels /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20241109103027_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20241202095605_V3_3.Designer.cs similarity index 98% rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20241109103027_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20241202095605_V3_3.Designer.cs index 08eb659ca..5e37abbff 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20241109103027_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20241202095605_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Labels { [DbContext(typeof(LabelsElsaDbContext))] - [Migration("20241109103027_V3_3")] + [Migration("20241202095605_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20241109103027_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20241202095605_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20241109103027_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Labels/20241202095605_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20231015122234_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20231015122234_Initial.cs index 79255a952..6b24c6531 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20231015122234_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20231015122234_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20240329200740_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20240329200740_V3_1.cs index 2d2898c34..dcc4b0e75 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20240329200740_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20240329200740_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Management /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20240610184839_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20240610184839_V3_2.cs index 24fcf84a7..5ca565daa 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20240610184839_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20240610184839_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Management /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20241109102906_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20241202095351_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20241109102906_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20241202095351_V3_3.Designer.cs index a642e2c26..f3f0bc0bb 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20241109102906_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20241202095351_V3_3.Designer.cs @@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Management { [DbContext(typeof(ManagementElsaDbContext))] - [Migration("20241109102906_V3_3")] + [Migration("20241202095351_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20241109102906_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20241202095351_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20241109102906_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Management/20241202095351_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20231024160952_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20231024160952_Initial.cs index 8412c641b..ddf80899d 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20231024160952_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20231024160952_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240329200711_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240329200711_V3_1.cs index 821d29f2e..00dfc44e8 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240329200711_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240329200711_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240610184748_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240610184748_V3_2.cs index d61e4aec4..830c75445 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240610184748_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20240610184748_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20241109102821_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20241202095308_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20241109102821_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20241202095308_V3_3.Designer.cs index 1ca048a88..20033eb33 100644 --- a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20241109102821_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20241202095308_V3_3.Designer.cs @@ -12,7 +12,7 @@ using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Runtime { [DbContext(typeof(RuntimeElsaDbContext))] - [Migration("20241109102821_V3_3")] + [Migration("20241202095308_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20241109102821_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20241202095308_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20241109102821_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Runtime/20241202095308_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Tenants/20241202095647_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Tenants/20241202095647_V3_3.Designer.cs new file mode 100644 index 000000000..9e1361596 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Tenants/20241202095647_V3_3.Designer.cs @@ -0,0 +1,57 @@ +// +using Elsa.EntityFrameworkCore.Modules.Tenants; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Tenants +{ + [DbContext(typeof(TenantsElsaDbContext))] + [Migration("20241202095647_V3_3")] + partial class V3_3 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Elsa.Common.Multitenancy.Tenant", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_Tenant_TenantId"); + + b.ToTable("Tenants", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Tenants/20241202095647_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Tenants/20241202095647_V3_3.cs new file mode 100644 index 000000000..6b0ed9d5e --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Tenants/20241202095647_V3_3.cs @@ -0,0 +1,60 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Tenants +{ + /// + public partial class V3_3 : Migration + { + private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + + /// + public V3_3(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + { + _schema = schema; + } + + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "Elsa"); + + migrationBuilder.CreateTable( + name: "Tenants", + schema: _schema.Schema, + columns: table => new + { + Id = table.Column(type: "text", nullable: false), + Name = table.Column(type: "text", nullable: false), + Configuration = table.Column(type: "text", nullable: false), + TenantId = table.Column(type: "text", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Tenants", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_Name", + schema: _schema.Schema, + table: "Tenants", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_TenantId", + schema: _schema.Schema, + table: "Tenants", + column: "TenantId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Tenants", + schema: _schema.Schema); + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs new file mode 100644 index 000000000..668b69844 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.PostgreSql/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs @@ -0,0 +1,54 @@ +// +using Elsa.EntityFrameworkCore.Modules.Tenants; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.PostgreSql.Migrations.Tenants +{ + [DbContext(typeof(TenantsElsaDbContext))] + partial class TenantsElsaDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 63); + + NpgsqlModelBuilderExtensions.UseIdentityByDefaultColumns(modelBuilder); + + modelBuilder.Entity("Elsa.Common.Multitenancy.Tenant", b => + { + b.Property("Id") + .HasColumnType("text"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("text"); + + b.Property("Name") + .IsRequired() + .HasColumnType("text"); + + b.Property("TenantId") + .HasColumnType("text"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_Tenant_TenantId"); + + b.ToTable("Tenants", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/DbContextFactories.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/DbContextFactories.cs index 07ef6aebc..389dfea6a 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/DbContextFactories.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/DbContextFactories.cs @@ -5,6 +5,7 @@ using Elsa.EntityFrameworkCore.Modules.Identity; using Elsa.EntityFrameworkCore.Modules.Labels; using Elsa.EntityFrameworkCore.Modules.Management; using Elsa.EntityFrameworkCore.Modules.Runtime; +using Elsa.EntityFrameworkCore.Modules.Tenants; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; @@ -13,29 +14,22 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.EntityFrameworkCore.SqlServer; [UsedImplicitly] -public class IdentityDbContextFactory : SqlServerDesignTimeDbContextFactory -{ -} +public class IdentityDbContextFactory : SqlServerDesignTimeDbContextFactory; [UsedImplicitly] -public class ManagementDbContextFactory : SqlServerDesignTimeDbContextFactory -{ -} +public class ManagementDbContextFactory : SqlServerDesignTimeDbContextFactory; [UsedImplicitly] -public class RuntimeDbContextFactory : SqlServerDesignTimeDbContextFactory -{ -} +public class RuntimeDbContextFactory : SqlServerDesignTimeDbContextFactory; [UsedImplicitly] -public class LabelsDbContextFactory : SqlServerDesignTimeDbContextFactory -{ -} +public class LabelsDbContextFactory : SqlServerDesignTimeDbContextFactory; [UsedImplicitly] -public class AlterationsDbContextFactories : SqlServerDesignTimeDbContextFactory -{ -} +public class AlterationsDbContextFactories : SqlServerDesignTimeDbContextFactory; + +[UsedImplicitly] +public class TenantsDbContextFactories : SqlServerDesignTimeDbContextFactory; public class SqlServerDesignTimeDbContextFactory : DesignTimeDbContextFactoryBase where TDbContext : DbContext { diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20231015122155_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20231015122155_Initial.cs index 753c472ee..7e0abe564 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20231015122155_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20231015122155_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20240329200626_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20240329200626_V3_1.cs index c21ccef5c..6da00c13d 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20240329200626_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20240329200626_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Alterations /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20240610184545_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20240610184545_V3_2.cs index 6d06f36c3..7aa2f7e99 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20240610184545_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20240610184545_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Alterations /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20241109102610_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20241202095036_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20241109102610_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20241202095036_V3_3.Designer.cs index e8bf71598..4d7157086 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20241109102610_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20241202095036_V3_3.Designer.cs @@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Alterations { [DbContext(typeof(AlterationsElsaDbContext))] - [Migration("20241109102610_V3_3")] + [Migration("20241202095036_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20241109102610_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20241202095036_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20241109102610_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Alterations/20241202095036_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20231015122242_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20231015122242_Initial.cs index 50ade9cf4..96d07566f 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20231015122242_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20231015122242_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20240329200753_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20240329200753_V3_1.cs index 11a90a6e3..651a17c81 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20240329200753_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20240329200753_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Identity /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20240610184919_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20240610184919_V3_2.cs index 303283c60..f174820f5 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20240610184919_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20240610184919_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Identity /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20241109102931_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20241202095420_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20241109102931_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20241202095420_V3_3.Designer.cs index a6e4eaa39..3a7e1bc51 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20241109102931_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20241202095420_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Identity { [DbContext(typeof(IdentityElsaDbContext))] - [Migration("20241109102931_V3_3")] + [Migration("20241202095420_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20241109102931_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20241202095420_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20241109102931_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Identity/20241202095420_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20231015122257_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20231015122257_Initial.cs index cb9b030d2..fe9690515 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20231015122257_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20231015122257_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20240329200825_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20240329200825_V3_1.cs index 923f2a6ae..d7253cb7a 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20240329200825_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20240329200825_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Labels /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20240610185009_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20240610185009_V3_2.cs index 5e12d4225..ef6e41cbe 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20240610185009_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20240610185009_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Labels /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20241109103011_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20241202095545_V3_3.Designer.cs similarity index 98% rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20241109103011_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20241202095545_V3_3.Designer.cs index 402e5974d..b7fcb321b 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20241109103011_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20241202095545_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Labels { [DbContext(typeof(LabelsElsaDbContext))] - [Migration("20241109103011_V3_3")] + [Migration("20241202095545_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20241109103011_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20241202095545_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20241109103011_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Labels/20241202095545_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20231015122227_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20231015122227_Initial.cs index b4415441c..372bead26 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20231015122227_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20231015122227_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20240329200727_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20240329200727_V3_1.cs index e2544fa74..4ab327712 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20240329200727_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20240329200727_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Management /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20240610184814_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20240610184814_V3_2.cs index 0c134db30..a2bab765b 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20240610184814_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20240610184814_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Management /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20241109102850_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20241202095340_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20241109102850_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20241202095340_V3_3.Designer.cs index 83ae34d9b..f1379d5dd 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20241109102850_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20241202095340_V3_3.Designer.cs @@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Management { [DbContext(typeof(ManagementElsaDbContext))] - [Migration("20241109102850_V3_3")] + [Migration("20241202095340_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20241109102850_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20241202095340_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20241109102850_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Management/20241202095340_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20231024160944_Initial.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20231024160944_Initial.cs index 85ae54f44..899bf632b 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20231024160944_Initial.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20231024160944_Initial.cs @@ -1,5 +1,4 @@ -using Elsa.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Migrations; #nullable disable diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240329200657_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240329200657_V3_1.cs index 342469eec..abaac578e 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240329200657_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240329200657_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Runtime /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240610184719_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240610184719_V3_2.cs index 4f0f4c3ef..67ba36b5f 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240610184719_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20240610184719_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Runtime /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20241109102758_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20241202095236_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20241109102758_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20241202095236_V3_3.Designer.cs index 27cb62198..863317795 100644 --- a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20241109102758_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20241202095236_V3_3.Designer.cs @@ -12,7 +12,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Runtime { [DbContext(typeof(RuntimeElsaDbContext))] - [Migration("20241109102758_V3_3")] + [Migration("20241202095236_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20241109102758_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20241202095236_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20241109102758_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Runtime/20241202095236_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Tenants/20241202095629_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Tenants/20241202095629_V3_3.Designer.cs new file mode 100644 index 000000000..8b313d41e --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Tenants/20241202095629_V3_3.Designer.cs @@ -0,0 +1,57 @@ +// +using Elsa.EntityFrameworkCore.Modules.Tenants; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Tenants +{ + [DbContext(typeof(TenantsElsaDbContext))] + [Migration("20241202095629_V3_3")] + partial class V3_3 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Elsa.Common.Multitenancy.Tenant", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("TenantId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_Tenant_TenantId"); + + b.ToTable("Tenants", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Tenants/20241202095629_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Tenants/20241202095629_V3_3.cs new file mode 100644 index 000000000..3b72b2b83 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Tenants/20241202095629_V3_3.cs @@ -0,0 +1,60 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Tenants +{ + /// + public partial class V3_3 : Migration + { + private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + + /// + public V3_3(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + { + _schema = schema; + } + + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.EnsureSchema( + name: "Elsa"); + + migrationBuilder.CreateTable( + name: "Tenants", + schema: _schema.Schema, + columns: table => new + { + Id = table.Column(type: "nvarchar(450)", nullable: false), + Name = table.Column(type: "nvarchar(450)", nullable: false), + Configuration = table.Column(type: "nvarchar(max)", nullable: false), + TenantId = table.Column(type: "nvarchar(450)", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Tenants", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_Name", + schema: _schema.Schema, + table: "Tenants", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_TenantId", + schema: _schema.Schema, + table: "Tenants", + column: "TenantId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Tenants", + schema: _schema.Schema); + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs new file mode 100644 index 000000000..2f58ff947 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.SqlServer/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs @@ -0,0 +1,54 @@ +// +using Elsa.EntityFrameworkCore.Modules.Tenants; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.SqlServer.Migrations.Tenants +{ + [DbContext(typeof(TenantsElsaDbContext))] + partial class TenantsElsaDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder + .HasDefaultSchema("Elsa") + .HasAnnotation("ProductVersion", "7.0.20") + .HasAnnotation("Relational:MaxIdentifierLength", 128); + + SqlServerModelBuilderExtensions.UseIdentityColumns(modelBuilder); + + modelBuilder.Entity("Elsa.Common.Multitenancy.Tenant", b => + { + b.Property("Id") + .HasColumnType("nvarchar(450)"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("nvarchar(max)"); + + b.Property("Name") + .IsRequired() + .HasColumnType("nvarchar(450)"); + + b.Property("TenantId") + .HasColumnType("nvarchar(450)"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_Tenant_TenantId"); + + b.ToTable("Tenants", "Elsa"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/DbContextFactories.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/DbContextFactories.cs index 9447034b3..3b73dee21 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/DbContextFactories.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/DbContextFactories.cs @@ -5,6 +5,7 @@ using Elsa.EntityFrameworkCore.Modules.Identity; using Elsa.EntityFrameworkCore.Modules.Labels; using Elsa.EntityFrameworkCore.Modules.Management; using Elsa.EntityFrameworkCore.Modules.Runtime; +using Elsa.EntityFrameworkCore.Modules.Tenants; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore; @@ -13,29 +14,22 @@ using Microsoft.EntityFrameworkCore; namespace Elsa.EntityFrameworkCore.Sqlite; [UsedImplicitly] -public class IdentityDbContextFactory : SqliteDesignTimeDbContextFactory -{ -} +public class IdentityDbContextFactory : SqliteDesignTimeDbContextFactory; [UsedImplicitly] -public class ManagementDbContextFactory : SqliteDesignTimeDbContextFactory -{ -} +public class ManagementDbContextFactory : SqliteDesignTimeDbContextFactory; [UsedImplicitly] -public class RuntimeDbContextFactory : SqliteDesignTimeDbContextFactory -{ -} +public class RuntimeDbContextFactory : SqliteDesignTimeDbContextFactory; [UsedImplicitly] -public class LabelsDbContextFactory : SqliteDesignTimeDbContextFactory -{ -} +public class LabelsDbContextFactory : SqliteDesignTimeDbContextFactory; [UsedImplicitly] -public class AlterationsDbContextFactories : SqliteDesignTimeDbContextFactory -{ -} +public class AlterationsDbContextFactories : SqliteDesignTimeDbContextFactory; + +[UsedImplicitly] +public class TenantsDbContextFactories : SqliteDesignTimeDbContextFactory; public class SqliteDesignTimeDbContextFactory : DesignTimeDbContextFactoryBase where TDbContext : DbContext { diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20240329200634_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20240329200634_V3_1.cs index ac3f405b6..c0aa49c2c 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20240329200634_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20240329200634_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Alterations /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20240610184608_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20240610184608_V3_2.cs index b30f310fd..f779303aa 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20240610184608_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20240610184608_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Alterations /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20241109102628_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20241202095051_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20241109102628_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20241202095051_V3_3.Designer.cs index 14c4d7237..a6208f7a6 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20241109102628_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20241202095051_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Alterations { [DbContext(typeof(AlterationsElsaDbContext))] - [Migration("20241109102628_V3_3")] + [Migration("20241202095051_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20241109102628_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20241202095051_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20241109102628_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Alterations/20241202095051_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20240329200800_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20240329200800_V3_1.cs index c6831eeb0..6e2aa0f3a 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20240329200800_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20240329200800_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Identity /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20240610184930_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20240610184930_V3_2.cs index 1359682c2..3c43a28b7 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20240610184930_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20240610184930_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Identity /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20241109102941_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20241202095439_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20241109102941_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20241202095439_V3_3.Designer.cs index b901f47f7..845a6939b 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20241109102941_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20241202095439_V3_3.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Identity { [DbContext(typeof(IdentityElsaDbContext))] - [Migration("20241109102941_V3_3")] + [Migration("20241202095439_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20241109102941_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20241202095439_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20241109102941_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Identity/20241202095439_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20240329200834_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20240329200834_V3_1.cs index 6ead24bbb..1f8f234ef 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20240329200834_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20240329200834_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Labels /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20240610185018_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20240610185018_V3_2.cs index cf60cf64e..73bcf04f5 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20240610185018_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20240610185018_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Labels /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20241109103018_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20241202095558_V3_3.Designer.cs similarity index 98% rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20241109103018_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20241202095558_V3_3.Designer.cs index ba9d16639..89184719e 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20241109103018_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20241202095558_V3_3.Designer.cs @@ -10,7 +10,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Labels { [DbContext(typeof(LabelsElsaDbContext))] - [Migration("20241109103018_V3_3")] + [Migration("20241202095558_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20241109103018_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20241202095558_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20241109103018_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Labels/20241202095558_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20240329200734_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20240329200734_V3_1.cs index 8c60af176..deaad852f 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20240329200734_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20240329200734_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Management /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20240610184829_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20240610184829_V3_2.cs index db37bac56..003ae489f 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20240610184829_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20240610184829_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Management /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20241109102858_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20241202095346_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20241109102858_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20241202095346_V3_3.Designer.cs index 449cecdb8..fe7084b4f 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20241109102858_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20241202095346_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Management { [DbContext(typeof(ManagementElsaDbContext))] - [Migration("20241109102858_V3_3")] + [Migration("20241202095346_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20241109102858_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20241202095346_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20241109102858_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Management/20241202095346_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240329200705_V3_1.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240329200705_V3_1.cs index c6e73b527..ddea13893 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240329200705_V3_1.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240329200705_V3_1.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Runtime /// public partial class V3_1 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_1(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_1(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240610184731_V3_2.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240610184731_V3_2.cs index d84cfc43f..af8aed20a 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240610184731_V3_2.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20240610184731_V3_2.cs @@ -7,10 +7,10 @@ namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Runtime /// public partial class V3_2 : Migration { - private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + private readonly IElsaDbContextSchema _schema; /// - public V3_2(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + public V3_2(IElsaDbContextSchema schema) { _schema = schema; } diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20241109102811_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20241202095254_V3_3.Designer.cs similarity index 99% rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20241109102811_V3_3.Designer.cs rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20241202095254_V3_3.Designer.cs index 0c4389321..06b817177 100644 --- a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20241109102811_V3_3.Designer.cs +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20241202095254_V3_3.Designer.cs @@ -11,7 +11,7 @@ using Microsoft.EntityFrameworkCore.Storage.ValueConversion; namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Runtime { [DbContext(typeof(RuntimeElsaDbContext))] - [Migration("20241109102811_V3_3")] + [Migration("20241202095254_V3_3")] partial class V3_3 { /// diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20241109102811_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20241202095254_V3_3.cs similarity index 100% rename from src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20241109102811_V3_3.cs rename to src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Runtime/20241202095254_V3_3.cs diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Tenants/20241202095639_V3_3.Designer.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Tenants/20241202095639_V3_3.Designer.cs new file mode 100644 index 000000000..4f0d71653 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Tenants/20241202095639_V3_3.Designer.cs @@ -0,0 +1,51 @@ +// +using Elsa.EntityFrameworkCore.Modules.Tenants; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Migrations; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Tenants +{ + [DbContext(typeof(TenantsElsaDbContext))] + [Migration("20241202095639_V3_3")] + partial class V3_3 + { + /// + protected override void BuildTargetModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.20"); + + modelBuilder.Entity("Elsa.Common.Multitenancy.Tenant", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TenantId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_Tenant_TenantId"); + + b.ToTable("Tenants"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Tenants/20241202095639_V3_3.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Tenants/20241202095639_V3_3.cs new file mode 100644 index 000000000..055d068c2 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Tenants/20241202095639_V3_3.cs @@ -0,0 +1,53 @@ +using Microsoft.EntityFrameworkCore.Migrations; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Tenants +{ + /// + public partial class V3_3 : Migration + { + private readonly Elsa.EntityFrameworkCore.IElsaDbContextSchema _schema; + + /// + public V3_3(Elsa.EntityFrameworkCore.IElsaDbContextSchema schema) + { + _schema = schema; + } + + /// + protected override void Up(MigrationBuilder migrationBuilder) + { + migrationBuilder.CreateTable( + name: "Tenants", + columns: table => new + { + Id = table.Column(type: "TEXT", nullable: false), + Name = table.Column(type: "TEXT", nullable: false), + Configuration = table.Column(type: "TEXT", nullable: false), + TenantId = table.Column(type: "TEXT", nullable: true) + }, + constraints: table => + { + table.PrimaryKey("PK_Tenants", x => x.Id); + }); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_Name", + table: "Tenants", + column: "Name"); + + migrationBuilder.CreateIndex( + name: "IX_Tenant_TenantId", + table: "Tenants", + column: "TenantId"); + } + + /// + protected override void Down(MigrationBuilder migrationBuilder) + { + migrationBuilder.DropTable( + name: "Tenants"); + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs new file mode 100644 index 000000000..734a0bc10 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore.Sqlite/Migrations/Tenants/TenantsElsaDbContextModelSnapshot.cs @@ -0,0 +1,48 @@ +// +using Elsa.EntityFrameworkCore.Modules.Tenants; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; + +#nullable disable + +namespace Elsa.EntityFrameworkCore.Sqlite.Migrations.Tenants +{ + [DbContext(typeof(TenantsElsaDbContext))] + partial class TenantsElsaDbContextModelSnapshot : ModelSnapshot + { + protected override void BuildModel(ModelBuilder modelBuilder) + { +#pragma warning disable 612, 618 + modelBuilder.HasAnnotation("ProductVersion", "7.0.20"); + + modelBuilder.Entity("Elsa.Common.Multitenancy.Tenant", b => + { + b.Property("Id") + .HasColumnType("TEXT"); + + b.Property("Configuration") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("Name") + .IsRequired() + .HasColumnType("TEXT"); + + b.Property("TenantId") + .HasColumnType("TEXT"); + + b.HasKey("Id"); + + b.HasIndex("Name") + .HasDatabaseName("IX_Tenant_Name"); + + b.HasIndex("TenantId") + .HasDatabaseName("IX_Tenant_TenantId"); + + b.ToTable("Tenants"); + }); +#pragma warning restore 612, 618 + } + } +} diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Identity/UserStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Identity/UserStore.cs index bd3e64463..882599c0e 100644 --- a/src/modules/Elsa.EntityFrameworkCore/Modules/Identity/UserStore.cs +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Identity/UserStore.cs @@ -7,34 +7,24 @@ namespace Elsa.EntityFrameworkCore.Modules.Identity; /// /// An EF Core implementation of . /// -public class EFCoreUserStore : IUserStore +public class EFCoreUserStore(EntityStore userStore) : IUserStore { - private readonly EntityStore _userStore; - - /// - /// Initializes a new instance of . - /// - public EFCoreUserStore(EntityStore userStore) - { - _userStore = userStore; - } - /// public async Task SaveAsync(User user, CancellationToken cancellationToken = default) { - await _userStore.SaveAsync(user, cancellationToken); + await userStore.SaveAsync(user, cancellationToken); } /// public async Task DeleteAsync(UserFilter filter, CancellationToken cancellationToken = default) { - await _userStore.DeleteWhereAsync(query => Filter(query, filter), cancellationToken); + await userStore.DeleteWhereAsync(query => Filter(query, filter), cancellationToken); } /// public async Task FindAsync(UserFilter filter, CancellationToken cancellationToken = default) { - return await _userStore.FindAsync(query => Filter(query, filter), cancellationToken); + return await userStore.FindAsync(query => Filter(query, filter), cancellationToken); } private static IQueryable Filter(IQueryable query, UserFilter filter) => filter.Apply(query); diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/ConfigurationJsonConverter.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/ConfigurationJsonConverter.cs new file mode 100644 index 000000000..572585c66 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/ConfigurationJsonConverter.cs @@ -0,0 +1,22 @@ +using Elsa.Common.Serialization; +using Microsoft.EntityFrameworkCore.Storage.ValueConversion; +using Microsoft.Extensions.Configuration; + +namespace Elsa.EntityFrameworkCore.Modules.Tenants; + +public class ConfigurationJsonConverter() : ValueConverter( + config => SerializeConfiguration(config), + json => DeserializeConfiguration(json)) +{ + private static string SerializeConfiguration(IConfiguration configuration) + { + if (configuration == null) throw new ArgumentNullException(nameof(configuration)); + return Serializers.SerializeConfiguration(configuration); + } + + private static IConfiguration DeserializeConfiguration(string json) + { + if (json == null) throw new ArgumentNullException(nameof(json)); + return Serializers.DeserializeConfiguration(json); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/Configurations.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/Configurations.cs new file mode 100644 index 000000000..8b81f348a --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/Configurations.cs @@ -0,0 +1,19 @@ +using Elsa.Common.Multitenancy; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Metadata.Builders; + +namespace Elsa.EntityFrameworkCore.Modules.Tenants; + +/// +/// EF Core configuration for . +/// +public class Configurations : IEntityTypeConfiguration +{ + public void Configure(EntityTypeBuilder builder) + { + builder.Property(x => x.Name).IsRequired(); + builder.Property(x => x.Configuration).HasConversion(); + builder.HasIndex(x => x.Name).HasDatabaseName($"IX_{nameof(Tenant)}_{nameof(Tenant.Name)}"); + builder.HasIndex(x => x.TenantId).HasDatabaseName($"IX_{nameof(Tenant)}_{nameof(Tenant.TenantId)}"); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/DbContext.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/DbContext.cs new file mode 100644 index 000000000..90d4dad28 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/DbContext.cs @@ -0,0 +1,31 @@ +using Elsa.Common.Multitenancy; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore; +using NJsonSchema.Annotations; + +namespace Elsa.EntityFrameworkCore.Modules.Tenants; + +/// +/// DB context for the runtime module. +/// +[UsedImplicitly] +public class TenantsElsaDbContext : ElsaDbContextBase +{ + /// + public TenantsElsaDbContext(DbContextOptions options, IServiceProvider serviceProvider) : base(options, serviceProvider) + { + } + + /// + /// The alteration plans. + /// + public DbSet Tenants { get; set; } = default!; + + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + var configuration = new Configurations(); + modelBuilder.ApplyConfiguration(configuration); + base.OnModelCreating(modelBuilder); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/Extensions.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/Extensions.cs new file mode 100644 index 000000000..abb09f263 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/Extensions.cs @@ -0,0 +1,18 @@ +using Elsa.Tenants.Features; + +namespace Elsa.EntityFrameworkCore.Modules.Tenants; + +/// +/// Provides extensions to various management related features. +/// +public static class TenantManagementFeatureExtensions +{ + /// + /// Sets up the EF Core persistence provider. + /// + public static TenantManagementFeature UseEntityFrameworkCore(this TenantManagementFeature feature, Action? configure = default) + { + feature.Module.Configure(configure); + return feature; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/Feature.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/Feature.cs new file mode 100644 index 000000000..56cab4378 --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/Feature.cs @@ -0,0 +1,35 @@ +using Elsa.Common.Multitenancy; +using Elsa.Features.Attributes; +using Elsa.Features.Services; +using Elsa.Tenants.Features; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.EntityFrameworkCore.Modules.Tenants; + +/// +/// Configures the feature with an Entity Framework Core persistence provider. +/// +[DependsOn(typeof(TenantManagementFeature))] +public class EFCoreTenantManagementFeature(IModule module) : PersistenceFeatureBase(module) +{ + /// + public override void Configure() + { + Module.Configure(feature => + { + feature.WithTenantStore(sp => sp.GetRequiredService()); + }); + } + + /// + public override void Apply() + { + base.Apply(); + AddEntityStore(); + } + + protected override void ConfigureMigrations() + { + Module.ConfigureHostedService>(-100); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/TenantStore.cs b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/TenantStore.cs new file mode 100644 index 000000000..b3e4eba9e --- /dev/null +++ b/src/modules/Elsa.EntityFrameworkCore/Modules/Tenants/TenantStore.cs @@ -0,0 +1,54 @@ +using Elsa.Common.Multitenancy; +using Elsa.Tenants; +using JetBrains.Annotations; + +namespace Elsa.EntityFrameworkCore.Modules.Tenants; + +[UsedImplicitly] +public class EFCoreTenantStore(EntityStore store) : ITenantStore +{ + public async Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + return await store.FindAsync(query => Filter(query, filter), cancellationToken); + } + + public async Task FindAsync(string id, CancellationToken cancellationToken = default) + { + var filter = TenantFilter.ById(id); + return await FindAsync(filter, cancellationToken); + } + + public async Task> FindManyAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + return await store.QueryAsync(queryable => Filter(queryable, filter), cancellationToken); + } + + public async Task> ListAsync(CancellationToken cancellationToken = default) + { + return await store.ListAsync(cancellationToken); + } + + public async Task AddAsync(Tenant tenant, CancellationToken cancellationToken = default) + { + await store.AddAsync(tenant, cancellationToken); + } + + public async Task UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default) + { + await store.UpdateAsync(tenant, cancellationToken); + } + + public async Task DeleteAsync(string id, CancellationToken cancellationToken = default) + { + var filter = TenantFilter.ById(id); + var count = await DeleteAsync(filter, cancellationToken); + return count > 0; + } + + public async Task DeleteAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + return await store.DeleteWhereAsync(query => Filter(query, filter), cancellationToken); + } + + private static IQueryable Filter(IQueryable query, TenantFilter filter) => filter.Apply(query); +} \ No newline at end of file diff --git a/src/modules/Elsa.Http/Services/DefaultHttpEndpointBasePathProvider.cs b/src/modules/Elsa.Http/Services/DefaultHttpEndpointBasePathProvider.cs index 29390a1e3..de570dbe2 100644 --- a/src/modules/Elsa.Http/Services/DefaultHttpEndpointBasePathProvider.cs +++ b/src/modules/Elsa.Http/Services/DefaultHttpEndpointBasePathProvider.cs @@ -9,7 +9,7 @@ public class DefaultHttpEndpointBasePathProvider(IOptions o { public string GetBasePath() { - var baseUrl = new Uri(options.Value.BaseUrl.ToString().TrimEnd('/')); + var baseUrl = new Uri(options.Value.BaseUrl.ToString().TrimEnd('/') + '/'); var basePath = options.Value.BasePath?.ToString().Trim('/'); var completeBaseUrl = new Uri(baseUrl, basePath); diff --git a/src/modules/Elsa.Kafka/Activities/ProduceMessage.cs b/src/modules/Elsa.Kafka/Activities/ProduceMessage.cs index 381c423b1..01d92bceb 100644 --- a/src/modules/Elsa.Kafka/Activities/ProduceMessage.cs +++ b/src/modules/Elsa.Kafka/Activities/ProduceMessage.cs @@ -68,7 +68,7 @@ public class ProduceMessage : CodeActivity var producerDefinitionEnumerator = context.GetRequiredService(); var producerDefinition = await producerDefinitionEnumerator.GetByIdAsync(producerDefinitionId); var content = Content.Get(context); - var key = Key.Get(context); + var key = Key.GetOrDefault(context); if (key is string keyString && string.IsNullOrWhiteSpace(keyString)) key = null; diff --git a/src/modules/Elsa.MongoDb/Common/MongoDbStore.cs b/src/modules/Elsa.MongoDb/Common/MongoDbStore.cs index 296f0bd14..c070fd188 100644 --- a/src/modules/Elsa.MongoDb/Common/MongoDbStore.cs +++ b/src/modules/Elsa.MongoDb/Common/MongoDbStore.cs @@ -44,7 +44,7 @@ public class MongoDbStore(IMongoCollection collection, ITe var documentsList = documents.ToList(); if (!documentsList.Any()) - return; + return; ApplyTenantId(documentsList); await collection.InsertManyAsync(documentsList, new InsertManyOptions(), cancellationToken); @@ -281,7 +281,16 @@ public class MongoDbStore(IMongoCollection collection, ITe public async Task CountAsync(Func, IQueryable> query, Expression> propertySelector, bool tenantAgnostic = false, CancellationToken cancellationToken = default) { var queryable = GetQueryableCollection(tenantAgnostic); - return await query((IQueryable)queryable.DistinctBy(propertySelector)).LongCountAsync(cancellationToken); + return await query(queryable.DistinctBy(propertySelector)).LongCountAsync(cancellationToken); + } + + /// + /// Lists all documents. + /// + public async Task> ListAsync(bool tenantAgnostic = false, CancellationToken cancellationToken = default) + { + var queryable = GetQueryableCollection(tenantAgnostic); + return await queryable.ToListAsync(cancellationToken); } /// @@ -385,21 +394,21 @@ public class MongoDbStore(IMongoCollection collection, ITe return count; } - + private IQueryable GetQueryableCollection(bool tenantAgnostic = false) { var queryable = collection.AsQueryable(); - - if(tenantAgnostic) + + if (tenantAgnostic) return queryable; - - if(typeof(Entity).IsAssignableFrom(typeof(TDocument))) + + if (typeof(Entity).IsAssignableFrom(typeof(TDocument))) { var tenant = tenantAccessor.Tenant; var tenantId = tenant?.Id; queryable = queryable.Where(x => (x as Entity)!.TenantId == tenantId); } - + return queryable; } diff --git a/src/modules/Elsa.MongoDb/Modules/Tenants/CreateIndices.cs b/src/modules/Elsa.MongoDb/Modules/Tenants/CreateIndices.cs new file mode 100644 index 000000000..1c5df3079 --- /dev/null +++ b/src/modules/Elsa.MongoDb/Modules/Tenants/CreateIndices.cs @@ -0,0 +1,38 @@ +using Elsa.Common.Multitenancy; +using Elsa.MongoDb.Helpers; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using MongoDB.Driver; + +namespace Elsa.MongoDb.Modules.Tenants; + +internal class CreateIndices(IServiceProvider serviceProvider) : IHostedService +{ + public Task StartAsync(CancellationToken cancellationToken) + { + using var scope = serviceProvider.CreateScope(); + return CreateTenantIndices(scope, cancellationToken); + } + + public Task StopAsync(CancellationToken cancellationToken) + { + return Task.CompletedTask; + } + + private static Task CreateTenantIndices(IServiceScope serviceScope, CancellationToken cancellationToken) + { + var mongoCollection = serviceScope.ServiceProvider.GetService>(); + if (mongoCollection == null) return Task.CompletedTask; + + return IndexHelpers.CreateAsync( + mongoCollection, + async (collection, indexBuilder) => + await collection.Indexes.CreateManyAsync( + new List> + { + new(indexBuilder.Ascending(x => x.Name)), + new(indexBuilder.Ascending(x => x.TenantId)) + }, + cancellationToken)); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.MongoDb/Modules/Tenants/Extensions.cs b/src/modules/Elsa.MongoDb/Modules/Tenants/Extensions.cs new file mode 100644 index 000000000..4833d7634 --- /dev/null +++ b/src/modules/Elsa.MongoDb/Modules/Tenants/Extensions.cs @@ -0,0 +1,19 @@ +using Elsa.Identity.Features; +using Elsa.Tenants.Features; + +namespace Elsa.MongoDb.Modules.Tenants; + +/// +/// Provides extensions for +/// +public static class Extensions +{ + /// + /// Sets up the MongoDB persistence provider for the User, Application and Role stores. + /// + public static TenantManagementFeature UseMongoDb(this TenantManagementFeature feature, Action? configure = default) + { + feature.Module.Configure(configure); + return feature; + } +} \ No newline at end of file diff --git a/src/modules/Elsa.MongoDb/Modules/Tenants/Feature.cs b/src/modules/Elsa.MongoDb/Modules/Tenants/Feature.cs new file mode 100644 index 000000000..6f835e040 --- /dev/null +++ b/src/modules/Elsa.MongoDb/Modules/Tenants/Feature.cs @@ -0,0 +1,37 @@ +using Elsa.Common.Multitenancy; +using Elsa.Features.Attributes; +using Elsa.Features.Services; +using Elsa.MongoDb.Common; +using Elsa.Tenants.Features; +using JetBrains.Annotations; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.MongoDb.Modules.Tenants; + +/// +/// Configures the feature with MongoDB persistence providers. +/// +[DependsOn(typeof(TenantManagementFeature))] +[UsedImplicitly] +public class MongoTenantsPersistenceFeature(IModule module) : PersistenceFeatureBase(module) +{ + /// + public override void Configure() + { + Module.Configure(feature => + { + feature.WithTenantStore(sp => sp.GetRequiredService()); + }); + } + + /// + public override void Apply() + { + base.Apply(); + + AddCollection("tenants"); + AddStore(); + + Services.AddHostedService(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.MongoDb/Modules/Tenants/TenantStore.cs b/src/modules/Elsa.MongoDb/Modules/Tenants/TenantStore.cs new file mode 100644 index 000000000..2ec131dd0 --- /dev/null +++ b/src/modules/Elsa.MongoDb/Modules/Tenants/TenantStore.cs @@ -0,0 +1,69 @@ +using Elsa.Common.Multitenancy; +using Elsa.Identity.Contracts; +using Elsa.MongoDb.Common; +using Elsa.Tenants; +using JetBrains.Annotations; + +namespace Elsa.MongoDb.Modules.Tenants; + +/// +/// A MongoDb implementation of . +/// +[UsedImplicitly] +public class MongoTenantStore(MongoDbStore store) : ITenantStore +{ + /// + public Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + return store.FindAsync(query => Filter(query, filter), cancellationToken); + } + + /// + public Task FindAsync(string id, CancellationToken cancellationToken = default) + { + var filter = TenantFilter.ById(id); + return FindAsync(filter, cancellationToken); + } + + /// + public Task> FindManyAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + return store.FindManyAsync(queryable => Filter(queryable, filter), cancellationToken); + } + + public async Task> ListAsync(CancellationToken cancellationToken = default) + { + return await store.ListAsync(cancellationToken: cancellationToken); + } + + /// + public Task AddAsync(Tenant tenant, CancellationToken cancellationToken = default) + { + return store.AddAsync(tenant, cancellationToken); + } + + /// + public Task UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default) + { + return store.SaveAsync(tenant, cancellationToken); + } + + /// + public async Task DeleteAsync(string id, CancellationToken cancellationToken = default) + { + var filter = TenantFilter.ById(id); + var count = await DeleteAsync(filter, cancellationToken); + return count > 0; + } + + /// + public Task DeleteAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + return store.DeleteWhereAsync(query => Filter(query, filter), x => x.Id, cancellationToken); + } + + private static IQueryable Filter(IQueryable query, TenantFilter filter) + { + return filter.Apply(query); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Quartz.EntityFrameworkCore.MySql/MySqlQuartzExtensions.cs b/src/modules/Elsa.Quartz.EntityFrameworkCore.MySql/MySqlQuartzExtensions.cs index 3d22c6d49..8ade3f33e 100644 --- a/src/modules/Elsa.Quartz.EntityFrameworkCore.MySql/MySqlQuartzExtensions.cs +++ b/src/modules/Elsa.Quartz.EntityFrameworkCore.MySql/MySqlQuartzExtensions.cs @@ -35,8 +35,7 @@ public static class MySqlQuartzExtensions store.UseClustering(); }); }; - - // Configure the Quartz hosted service to run migrations. + feature.Module.ConfigureHostedService>(-100); return feature; diff --git a/src/modules/Elsa.Quartz.EntityFrameworkCore.PostgreSql/PostgreSqlQuartzExtensions.cs b/src/modules/Elsa.Quartz.EntityFrameworkCore.PostgreSql/PostgreSqlQuartzExtensions.cs index 97199393a..a3e3e25dd 100644 --- a/src/modules/Elsa.Quartz.EntityFrameworkCore.PostgreSql/PostgreSqlQuartzExtensions.cs +++ b/src/modules/Elsa.Quartz.EntityFrameworkCore.PostgreSql/PostgreSqlQuartzExtensions.cs @@ -41,7 +41,6 @@ public static class PostgreSqlQuartzExtensions }); }; - // Configure the Quartz hosted service to run migrations. feature.Module.ConfigureHostedService>(-100); return feature; diff --git a/src/modules/Elsa.Quartz.EntityFrameworkCore.SqlServer/SqlServerQuartzExtensions.cs b/src/modules/Elsa.Quartz.EntityFrameworkCore.SqlServer/SqlServerQuartzExtensions.cs index 65f91a6f7..2358dfe8c 100644 --- a/src/modules/Elsa.Quartz.EntityFrameworkCore.SqlServer/SqlServerQuartzExtensions.cs +++ b/src/modules/Elsa.Quartz.EntityFrameworkCore.SqlServer/SqlServerQuartzExtensions.cs @@ -33,7 +33,6 @@ public static class SqlServerQuartzExtensions { options.ConnectionString = connectionString; options.TablePrefix = "[quartz].qrtz_"; - }); if (useClustering) @@ -41,7 +40,6 @@ public static class SqlServerQuartzExtensions }); }; - // Configure the Quartz hosted service to run migrations. feature.Module.ConfigureHostedService>(-100); return feature; diff --git a/src/modules/Elsa.Quartz.EntityFrameworkCore.Sqlite/SqliteQuartzExtensions.cs b/src/modules/Elsa.Quartz.EntityFrameworkCore.Sqlite/SqliteQuartzExtensions.cs index fd709dd8c..c7796b28d 100644 --- a/src/modules/Elsa.Quartz.EntityFrameworkCore.Sqlite/SqliteQuartzExtensions.cs +++ b/src/modules/Elsa.Quartz.EntityFrameworkCore.Sqlite/SqliteQuartzExtensions.cs @@ -33,7 +33,6 @@ public static class SqliteQuartzExtensions }); }; - // Configure the Quartz hosted service to run migrations. feature.Module.ConfigureHostedService>(-100); return feature; diff --git a/src/modules/Elsa.Quartz/Extensions/JobExecutionExtensions.cs b/src/modules/Elsa.Quartz/Extensions/JobExecutionExtensions.cs index 736c535a5..32a1564cd 100644 --- a/src/modules/Elsa.Quartz/Extensions/JobExecutionExtensions.cs +++ b/src/modules/Elsa.Quartz/Extensions/JobExecutionExtensions.cs @@ -7,6 +7,9 @@ internal static class JobExecutionExtensions { public static async Task GetTenantAsync(this IJobExecutionContext context, ITenantFinder tenantFinder) { + if(!context.MergedJobDataMap.ContainsKey("TenantId")) + return null; + if(!context.MergedJobDataMap.TryGetString("TenantId", out var tenantId)) return null; diff --git a/src/modules/Elsa.Quartz/Features/QuartzSchedulerFeature.cs b/src/modules/Elsa.Quartz/Features/QuartzSchedulerFeature.cs index cc3b2f60e..6600dfca0 100644 --- a/src/modules/Elsa.Quartz/Features/QuartzSchedulerFeature.cs +++ b/src/modules/Elsa.Quartz/Features/QuartzSchedulerFeature.cs @@ -37,8 +37,6 @@ public class QuartzSchedulerFeature(IModule module) : FeatureBase(module) Services.AddSingleton(); Services.AddSingleton(); Services.AddSingleton(); - Services.AddQuartz(quartz => quartz - .AddJob(job => job.WithIdentity(RunWorkflowJob.JobKey).StoreDurably()) - .AddJob(job => job.WithIdentity(ResumeWorkflowJob.JobKey).StoreDurably())); + Services.AddQuartz(); } } \ No newline at end of file diff --git a/src/modules/Elsa.Quartz/Jobs/ResumeWorkflowJob.cs b/src/modules/Elsa.Quartz/Jobs/ResumeWorkflowJob.cs index 72637d9f0..11551a2c6 100644 --- a/src/modules/Elsa.Quartz/Jobs/ResumeWorkflowJob.cs +++ b/src/modules/Elsa.Quartz/Jobs/ResumeWorkflowJob.cs @@ -14,27 +14,25 @@ namespace Elsa.Quartz.Jobs; /// public class ResumeWorkflowJob(IWorkflowRuntime workflowRuntime, IJsonSerializer jsonSerializer, ITenantFinder tenantFinder, ITenantAccessor tenantAccessor) : IJob { - /// - /// The job key. - /// - public static readonly JobKey JobKey = new(nameof(ResumeWorkflowJob)); - /// public async Task Execute(IJobExecutionContext context) { - tenantAccessor.Tenant = await context.GetTenantAsync(tenantFinder); - var map = context.MergedJobDataMap; - var serializedActivityHandle = (string)map.Get(nameof(ScheduleExistingWorkflowInstanceRequest.ActivityHandle)); - var activityHandle = serializedActivityHandle != null ? jsonSerializer.Deserialize(serializedActivityHandle) : null; - var workflowInstanceId = (string)map.Get(nameof(ScheduleExistingWorkflowInstanceRequest.WorkflowInstanceId)); - var workflowClient = await workflowRuntime.CreateClientAsync(workflowInstanceId, context.CancellationToken); - var request = new RunWorkflowInstanceRequest + var tenant = await context.GetTenantAsync(tenantFinder); + using (tenantAccessor.PushContext(tenant)) { - BookmarkId = (string)map.Get(nameof(ScheduleExistingWorkflowInstanceRequest.BookmarkId)), - ActivityHandle = activityHandle, - Input = map.GetDictionary(nameof(ScheduleExistingWorkflowInstanceRequest.Input)), - Properties = map.GetDictionary(nameof(ScheduleExistingWorkflowInstanceRequest.Properties)), - }; - await workflowClient.RunInstanceAsync(request, cancellationToken: context.CancellationToken); + var map = context.MergedJobDataMap; + var serializedActivityHandle = (string)map.Get(nameof(ScheduleExistingWorkflowInstanceRequest.ActivityHandle)); + var activityHandle = serializedActivityHandle != null! ? jsonSerializer.Deserialize(serializedActivityHandle) : null; + var workflowInstanceId = (string)map.Get(nameof(ScheduleExistingWorkflowInstanceRequest.WorkflowInstanceId)); + var workflowClient = await workflowRuntime.CreateClientAsync(workflowInstanceId, context.CancellationToken); + var request = new RunWorkflowInstanceRequest + { + BookmarkId = (string)map.Get(nameof(ScheduleExistingWorkflowInstanceRequest.BookmarkId)), + ActivityHandle = activityHandle, + Input = map.GetDictionary(nameof(ScheduleExistingWorkflowInstanceRequest.Input)), + Properties = map.GetDictionary(nameof(ScheduleExistingWorkflowInstanceRequest.Properties)), + }; + await workflowClient.RunInstanceAsync(request, cancellationToken: context.CancellationToken); + } } } \ No newline at end of file diff --git a/src/modules/Elsa.Quartz/Jobs/RunWorkflowJob.cs b/src/modules/Elsa.Quartz/Jobs/RunWorkflowJob.cs index 2f35060c6..82cc7b8e3 100644 --- a/src/modules/Elsa.Quartz/Jobs/RunWorkflowJob.cs +++ b/src/modules/Elsa.Quartz/Jobs/RunWorkflowJob.cs @@ -3,6 +3,7 @@ using Elsa.Extensions; using Elsa.Scheduling; using Elsa.Workflows.Models; using Elsa.Workflows.Runtime; +using Elsa.Workflows.Runtime.Exceptions; using Microsoft.Extensions.Logging; using Quartz; @@ -17,36 +18,47 @@ public class RunWorkflowJob( IWorkflowStarter workflowStarter, ILogger logger) : IJob { - /// - /// The job key. - /// - public static readonly JobKey JobKey = new(nameof(RunWorkflowJob)); - /// public async Task Execute(IJobExecutionContext context) { - tenantAccessor.Tenant = await context.GetTenantAsync(tenantFinder); - var map = context.MergedJobDataMap; - var cancellationToken = context.CancellationToken; + var tenant = await context.GetTenantAsync(tenantFinder); + using (tenantAccessor.PushContext(tenant)) + { + var map = context.MergedJobDataMap; + var cancellationToken = context.CancellationToken; - var startRequest = new StartWorkflowRequest - { - WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionVersionId((string)map.Get(nameof(ScheduleNewWorkflowInstanceRequest.WorkflowDefinitionHandle.DefinitionVersionId))), - CorrelationId = (string?)map.Get(nameof(ScheduleNewWorkflowInstanceRequest.CorrelationId)), - TriggerActivityId = (string?)map.Get(nameof(ScheduleNewWorkflowInstanceRequest.TriggerActivityId)), - Input = map.GetDictionary(nameof(ScheduleNewWorkflowInstanceRequest.Input)), - Properties = map.GetDictionary(nameof(ScheduleNewWorkflowInstanceRequest.Properties)), - ParentId = (string?)map.Get(nameof(ScheduleNewWorkflowInstanceRequest.ParentId)) - }; - - var startResponse = await workflowStarter.StartWorkflowAsync(startRequest, cancellationToken); - - if (startResponse.CannotStart) - { - logger.LogWarning("Workflow activation strategy disallowed starting workflow {WorkflowDefinitionHandle} with correlation ID {CorrelationId}", startRequest.WorkflowDefinitionHandle, startRequest.CorrelationId); - return; + var startRequest = new StartWorkflowRequest + { + WorkflowDefinitionHandle = WorkflowDefinitionHandle.ByDefinitionVersionId((string)map.Get(nameof(ScheduleNewWorkflowInstanceRequest.WorkflowDefinitionHandle.DefinitionVersionId))), + CorrelationId = (string?)map.Get(nameof(ScheduleNewWorkflowInstanceRequest.CorrelationId)), + TriggerActivityId = (string?)map.Get(nameof(ScheduleNewWorkflowInstanceRequest.TriggerActivityId)), + Input = map.GetDictionary(nameof(ScheduleNewWorkflowInstanceRequest.Input)), + Properties = map.GetDictionary(nameof(ScheduleNewWorkflowInstanceRequest.Properties)), + ParentId = (string?)map.Get(nameof(ScheduleNewWorkflowInstanceRequest.ParentId)) + }; + + try + { + var startResponse = await workflowStarter.StartWorkflowAsync(startRequest, cancellationToken); + + if (startResponse.CannotStart) + { + logger.LogWarning("Workflow activation strategy disallowed starting workflow {WorkflowDefinitionHandle} with correlation ID {CorrelationId}", startRequest.WorkflowDefinitionHandle, startRequest.CorrelationId); + return; + } + + logger.LogInformation("Started workflow {WorkflowInstanceId} with correlation ID {CorrelationId}", startResponse.WorkflowInstanceId, startRequest.CorrelationId); + } + catch (WorkflowGraphNotFoundException e) + { + logger.LogWarning(e, "Could not find workflow graph for workflow definition handle {WorkflowDefinitionHandle}", startRequest.WorkflowDefinitionHandle); + await context.Scheduler.DeleteJob(context.JobDetail.Key, cancellationToken); + } + catch (Exception e) + { + logger.LogError(e, "An error occurred while starting workflow {WorkflowDefinitionHandle} with correlation ID {CorrelationId}", startRequest.WorkflowDefinitionHandle, startRequest.CorrelationId); + await context.Scheduler.DeleteJob(context.JobDetail.Key, cancellationToken); + } } - - logger.LogInformation("Started workflow {WorkflowInstanceId} with correlation ID {CorrelationId}", startResponse.WorkflowInstanceId, startRequest.CorrelationId); } } \ No newline at end of file diff --git a/src/modules/Elsa.Quartz/Services/QuartzWorkflowScheduler.cs b/src/modules/Elsa.Quartz/Services/QuartzWorkflowScheduler.cs index 400028799..a110b9043 100644 --- a/src/modules/Elsa.Quartz/Services/QuartzWorkflowScheduler.cs +++ b/src/modules/Elsa.Quartz/Services/QuartzWorkflowScheduler.cs @@ -16,93 +16,100 @@ public class QuartzWorkflowScheduler(ISchedulerFactory schedulerFactoryFactory, public async ValueTask ScheduleAtAsync(string taskName, ScheduleNewWorkflowInstanceRequest request, DateTimeOffset at, CancellationToken cancellationToken = default) { var scheduler = await schedulerFactoryFactory.GetScheduler(cancellationToken); + var job = JobBuilder.Create() + .WithIdentity(GetRunWorkflowJobKey()) + .Build(); var trigger = TriggerBuilder.Create() - .ForJob(RunWorkflowJob.JobKey) .UsingJobData(CreateJobDataMap(request)) - .WithIdentity(taskName) + .WithIdentity(GetTriggerKey(taskName)) .StartAt(at) .Build(); - if (!await scheduler.CheckExists(trigger.Key, cancellationToken)) - await scheduler.ScheduleJob(trigger, cancellationToken); + if (!await scheduler.CheckExists(job.Key, cancellationToken)) + await scheduler.ScheduleJob(job, trigger, cancellationToken); } /// public async ValueTask ScheduleAtAsync(string taskName, ScheduleExistingWorkflowInstanceRequest request, DateTimeOffset at, CancellationToken cancellationToken = default) { var scheduler = await schedulerFactoryFactory.GetScheduler(cancellationToken); + var job = JobBuilder.Create().WithIdentity(GetResumeWorkflowJobKey()).Build(); var trigger = TriggerBuilder.Create() - .ForJob(ResumeWorkflowJob.JobKey) .UsingJobData(CreateJobDataMap(request)) - .WithIdentity(taskName) + .WithIdentity(GetTriggerKey(taskName)) .StartAt(at) .Build(); - if (!await scheduler.CheckExists(trigger.Key, cancellationToken)) - await scheduler.ScheduleJob(trigger, cancellationToken); + if (!await scheduler.CheckExists(job.Key, cancellationToken)) + await scheduler.ScheduleJob(job, trigger, cancellationToken); } /// public async ValueTask ScheduleRecurringAsync(string taskName, ScheduleNewWorkflowInstanceRequest request, DateTimeOffset startAt, TimeSpan interval, CancellationToken cancellationToken = default) { var scheduler = await schedulerFactoryFactory.GetScheduler(cancellationToken); + var job = JobBuilder.Create().WithIdentity(GetRunWorkflowJobKey()).Build(); var trigger = TriggerBuilder.Create() - .ForJob(RunWorkflowJob.JobKey) - .WithIdentity(taskName) + .WithIdentity(GetTriggerKey(taskName)) .UsingJobData(CreateJobDataMap(request)) .StartAt(startAt) .WithSimpleSchedule(schedule => schedule.WithInterval(interval).RepeatForever()) .Build(); - if (!await scheduler.CheckExists(trigger.Key, cancellationToken)) - await scheduler.ScheduleJob(trigger, cancellationToken); + if (!await scheduler.CheckExists(job.Key, cancellationToken)) + await scheduler.ScheduleJob(job, trigger, cancellationToken); } /// public async ValueTask ScheduleRecurringAsync(string taskName, ScheduleExistingWorkflowInstanceRequest request, DateTimeOffset startAt, TimeSpan interval, CancellationToken cancellationToken = default) { var scheduler = await schedulerFactoryFactory.GetScheduler(cancellationToken); + var job = JobBuilder.Create().WithIdentity(GetResumeWorkflowJobKey()).Build(); var trigger = TriggerBuilder.Create() - .ForJob(ResumeWorkflowJob.JobKey) - .WithIdentity(taskName) + .WithIdentity(GetTriggerKey(taskName)) .UsingJobData(CreateJobDataMap(request)) .StartAt(startAt) .WithSimpleSchedule(schedule => schedule.WithInterval(interval).RepeatForever()) .Build(); - if (!await scheduler.CheckExists(trigger.Key, cancellationToken)) - await scheduler.ScheduleJob(trigger, cancellationToken); + if (!await scheduler.CheckExists(job.Key, cancellationToken)) + await scheduler.ScheduleJob(job, trigger, cancellationToken); } /// public async ValueTask ScheduleCronAsync(string taskName, ScheduleNewWorkflowInstanceRequest request, string cronExpression, CancellationToken cancellationToken = default) { var scheduler = await schedulerFactoryFactory.GetScheduler(cancellationToken); - var trigger = TriggerBuilder.Create().ForJob(RunWorkflowJob.JobKey).UsingJobData(CreateJobDataMap(request)).WithIdentity(taskName).WithCronSchedule(cronExpression).Build(); + var job = JobBuilder.Create().WithIdentity(GetRunWorkflowJobKey()).Build(); + var trigger = TriggerBuilder.Create() + .UsingJobData(CreateJobDataMap(request)) + .WithIdentity(GetTriggerKey(taskName)) + .WithCronSchedule(cronExpression) + .Build(); - if (!await scheduler.CheckExists(trigger.Key, cancellationToken)) - await scheduler.ScheduleJob(trigger, cancellationToken); + if (!await scheduler.CheckExists(job.Key, cancellationToken)) + await scheduler.ScheduleJob(job, trigger, cancellationToken); } /// public async ValueTask ScheduleCronAsync(string taskName, ScheduleExistingWorkflowInstanceRequest request, string cronExpression, CancellationToken cancellationToken = default) { var scheduler = await schedulerFactoryFactory.GetScheduler(cancellationToken); + var job = JobBuilder.Create().WithIdentity(GetResumeWorkflowJobKey()).Build(); var trigger = TriggerBuilder.Create() - .ForJob(ResumeWorkflowJob.JobKey) .UsingJobData(CreateJobDataMap(request)) - .WithIdentity(taskName) + .WithIdentity(GetTriggerKey(taskName)) .WithCronSchedule(cronExpression).Build(); - if (!await scheduler.CheckExists(trigger.Key, cancellationToken)) - await scheduler.ScheduleJob(trigger, cancellationToken); + if (!await scheduler.CheckExists(job.Key, cancellationToken)) + await scheduler.ScheduleJob(job, trigger, cancellationToken); } /// public async ValueTask UnscheduleAsync(string taskName, CancellationToken cancellationToken = default) { var scheduler = await schedulerFactoryFactory.GetScheduler(cancellationToken); - var triggerKey = new TriggerKey(taskName); + var triggerKey = GetTriggerKey(taskName); await scheduler.UnscheduleJob(triggerKey, cancellationToken); } @@ -131,4 +138,18 @@ public class QuartzWorkflowScheduler(ISchedulerFactory schedulerFactoryFactory, .AddIfNotEmpty(nameof(ScheduleExistingWorkflowInstanceRequest.ActivityHandle), serializedActivityHandle) .AddIfNotEmpty(nameof(ScheduleExistingWorkflowInstanceRequest.BookmarkId), request.BookmarkId); } + + private JobKey GetRunWorkflowJobKey() => new(nameof(RunWorkflowJob), GetGroupName()); + private JobKey GetResumeWorkflowJobKey() => new(nameof(ResumeWorkflowJob), GetGroupName()); + + private string GetGroupName() + { + var tenantId = tenantAccessor.Tenant?.Id; + return string.IsNullOrWhiteSpace(tenantId) ? "Default" : tenantId; + } + + private TriggerKey GetTriggerKey(string taskName) + { + return new TriggerKey(taskName, GetGroupName()); + } } \ No newline at end of file diff --git a/src/modules/Elsa.Scheduling/Features/SchedulingFeature.cs b/src/modules/Elsa.Scheduling/Features/SchedulingFeature.cs index 92d62a57b..9cbd1a11c 100644 --- a/src/modules/Elsa.Scheduling/Features/SchedulingFeature.cs +++ b/src/modules/Elsa.Scheduling/Features/SchedulingFeature.cs @@ -1,4 +1,5 @@ using Elsa.Common.Features; +using Elsa.Common.Multitenancy; using Elsa.Extensions; using Elsa.Features.Abstractions; using Elsa.Features.Attributes; @@ -36,6 +37,9 @@ public class SchedulingFeature : FeatureBase public override void Apply() { Services + .AddSingleton() + .AddSingleton(sp => sp.GetRequiredService()) + .AddSingleton(sp => sp.GetRequiredService()) .AddSingleton() .AddSingleton() .AddSingleton(CronParser) diff --git a/src/modules/Elsa.Scheduling/Handlers/DeleteSchedules.cs b/src/modules/Elsa.Scheduling/Handlers/DeleteSchedules.cs index 111258f0a..7c6b38b47 100644 --- a/src/modules/Elsa.Scheduling/Handlers/DeleteSchedules.cs +++ b/src/modules/Elsa.Scheduling/Handlers/DeleteSchedules.cs @@ -9,34 +9,19 @@ namespace Elsa.Scheduling.Handlers; /// /// Deletes scheduled jobs based on deleted workflow triggers and bookmarks. /// -public class DeleteSchedules : - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler, - INotificationHandler +public class DeleteSchedules(ITriggerScheduler triggerScheduler, IBookmarkScheduler bookmarkScheduler, ITriggerStore triggerStore, IBookmarkStore bookmarkStore) + : + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler, + INotificationHandler { - private readonly ITriggerScheduler _triggerScheduler; - private readonly IBookmarkScheduler _bookmarkScheduler; - private readonly ITriggerStore _triggerStore; - private readonly IBookmarkStore _bookmarkStore; - - /// - /// Initializes a new instance of the class. - /// - public DeleteSchedules(ITriggerScheduler triggerScheduler, IBookmarkScheduler bookmarkScheduler, ITriggerStore triggerStore, IBookmarkStore bookmarkStore) - { - _triggerScheduler = triggerScheduler; - _bookmarkScheduler = bookmarkScheduler; - _triggerStore = triggerStore; - _bookmarkStore = bookmarkStore; - } - async Task INotificationHandler.HandleAsync(BookmarksDeleting notification, CancellationToken cancellationToken) { var ids = notification.Bookmarks.Select(x => x.Id).ToList(); - var bookmarks = await _bookmarkStore.FindManyAsync(new BookmarkFilter { BookmarkIds = ids }, cancellationToken); - await _bookmarkScheduler.UnscheduleAsync(bookmarks, cancellationToken); + var bookmarks = await bookmarkStore.FindManyAsync(new BookmarkFilter { BookmarkIds = ids }, cancellationToken); + await bookmarkScheduler.UnscheduleAsync(bookmarks, cancellationToken); } async Task INotificationHandler.HandleAsync(WorkflowDefinitionDeleting notification, CancellationToken cancellationToken) => await RemoveSchedulesAsync(new TriggerFilter { WorkflowDefinitionId = notification.DefinitionId }, cancellationToken); @@ -49,7 +34,7 @@ public class DeleteSchedules : private async Task RemoveSchedulesAsync(TriggerFilter filter, CancellationToken cancellationToken) { - var triggers = await _triggerStore.FindManyAsync(filter, cancellationToken); - await _triggerScheduler.UnscheduleAsync(triggers, cancellationToken); + var triggers = await triggerStore.FindManyAsync(filter, cancellationToken); + await triggerScheduler.UnscheduleAsync(triggers, cancellationToken); } } \ No newline at end of file diff --git a/src/modules/Elsa.Scheduling/Handlers/UpdateTenantSchedules.cs b/src/modules/Elsa.Scheduling/Handlers/UpdateTenantSchedules.cs new file mode 100644 index 000000000..bdeecad26 --- /dev/null +++ b/src/modules/Elsa.Scheduling/Handlers/UpdateTenantSchedules.cs @@ -0,0 +1,60 @@ +using Elsa.Common.Multitenancy; +using Elsa.Scheduling.Activities; +using Elsa.Workflows.Helpers; +using Elsa.Workflows.Runtime; +using Elsa.Workflows.Runtime.Entities; +using Elsa.Workflows.Runtime.Filters; +using Microsoft.Extensions.DependencyInjection; +using Timer = Elsa.Scheduling.Activities.Timer; + +namespace Elsa.Scheduling.Handlers; + +public class UpdateTenantSchedules : ITenantActivatedEvent, ITenantDeactivatedEvent +{ + private static readonly string[] ActivityTypeNames = + [ + ActivityTypeNameHelper.GenerateTypeName(), + ActivityTypeNameHelper.GenerateTypeName(), + ActivityTypeNameHelper.GenerateTypeName() + ]; + + public async Task TenantActivatedAsync(TenantActivatedEventArgs args) + { + var serviceProvider = args.TenantScope.ServiceProvider; + var cancellationToken = args.CancellationToken; + var triggerScheduler = args.TenantScope.ServiceProvider.GetRequiredService(); + var bookmarkScheduler = args.TenantScope.ServiceProvider.GetRequiredService(); + var triggers = (await GetTriggersAsync(serviceProvider, cancellationToken)).ToList(); + var bookmarks = (await GetBookmarksAsync(serviceProvider, cancellationToken)).ToList(); + + await triggerScheduler.ScheduleAsync(triggers, args.CancellationToken); + await bookmarkScheduler.ScheduleAsync(bookmarks, args.CancellationToken); + } + + public async Task TenantDeactivatedAsync(TenantDeactivatedEventArgs args) + { + var serviceProvider = args.TenantScope.ServiceProvider; + var cancellationToken = args.CancellationToken; + var triggerScheduler = args.TenantScope.ServiceProvider.GetRequiredService(); + var bookmarkScheduler = args.TenantScope.ServiceProvider.GetRequiredService(); + var triggers = (await GetTriggersAsync(serviceProvider, cancellationToken)).ToList(); + var bookmarks = (await GetBookmarksAsync(serviceProvider, cancellationToken)).ToList(); + + await triggerScheduler.UnscheduleAsync(triggers, args.CancellationToken); + await bookmarkScheduler.UnscheduleAsync(bookmarks, args.CancellationToken); + } + + private async Task> GetTriggersAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + var triggerStore = serviceProvider.GetRequiredService(); + var filter = TriggerFilter.ByNames(ActivityTypeNames); + return await triggerStore.FindManyAsync(filter, cancellationToken); + } + + private async Task> GetBookmarksAsync(IServiceProvider serviceProvider, CancellationToken cancellationToken) + { + var bookmarkStore = serviceProvider.GetRequiredService(); + var filter = BookmarkFilter.ByActivityTypeNames(ActivityTypeNames); + return await bookmarkStore.FindManyAsync(filter, cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants.AspNetCore/Services/TenantPrefixHttpEndpointBasePathProvider.cs b/src/modules/Elsa.Tenants.AspNetCore/Services/TenantPrefixHttpEndpointBasePathProvider.cs index 7ccf3f809..813435790 100644 --- a/src/modules/Elsa.Tenants.AspNetCore/Services/TenantPrefixHttpEndpointBasePathProvider.cs +++ b/src/modules/Elsa.Tenants.AspNetCore/Services/TenantPrefixHttpEndpointBasePathProvider.cs @@ -22,7 +22,7 @@ public class TenantPrefixHttpEndpointBasePathProvider( return (baseUrl + basePath).TrimEnd('/') + '/'; var tenant = tenantAccessor.Tenant; - var tenantPrefix = tenant?.GetRoutePrefix(); + var tenantPrefix = tenant?.GetRoutePrefix()?.Trim('/'); if(string.IsNullOrWhiteSpace(tenantPrefix)) return (baseUrl + basePath).TrimEnd('/') + '/'; diff --git a/src/modules/Elsa.Tenants/Contracts/ITenantStore.cs b/src/modules/Elsa.Tenants/Contracts/ITenantStore.cs new file mode 100644 index 000000000..30e8b91e5 --- /dev/null +++ b/src/modules/Elsa.Tenants/Contracts/ITenantStore.cs @@ -0,0 +1,15 @@ +using Elsa.Common.Multitenancy; + +namespace Elsa.Tenants; + +public interface ITenantStore +{ + Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default); + Task FindAsync(string id, CancellationToken cancellationToken = default); + Task> FindManyAsync(TenantFilter filter, CancellationToken cancellationToken = default); + Task> ListAsync(CancellationToken cancellationToken = default); + Task AddAsync(Tenant tenant, CancellationToken cancellationToken = default); + Task UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default); + Task DeleteAsync(string id, CancellationToken cancellationToken = default); + Task DeleteAsync(TenantFilter filter, CancellationToken cancellationToken = default); +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Elsa.Tenants.csproj b/src/modules/Elsa.Tenants/Elsa.Tenants.csproj index 275d67b58..08b3248df 100644 --- a/src/modules/Elsa.Tenants/Elsa.Tenants.csproj +++ b/src/modules/Elsa.Tenants/Elsa.Tenants.csproj @@ -8,6 +8,7 @@ + diff --git a/src/modules/Elsa.Tenants/Elsa.Tenants.csproj.DotSettings b/src/modules/Elsa.Tenants/Elsa.Tenants.csproj.DotSettings index 4c6e2920c..d882ac78d 100644 --- a/src/modules/Elsa.Tenants/Elsa.Tenants.csproj.DotSettings +++ b/src/modules/Elsa.Tenants/Elsa.Tenants.csproj.DotSettings @@ -1,3 +1,4 @@  True - True \ No newline at end of file + True + True \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Endpoints/Tenants/Add/Endpoint.cs b/src/modules/Elsa.Tenants/Endpoints/Tenants/Add/Endpoint.cs new file mode 100644 index 000000000..8a029bdcb --- /dev/null +++ b/src/modules/Elsa.Tenants/Endpoints/Tenants/Add/Endpoint.cs @@ -0,0 +1,39 @@ +using System.Text.Json; +using Elsa.Abstractions; +using Elsa.Common.Multitenancy; +using Elsa.Common.Serialization; +using Elsa.Workflows; + +namespace Elsa.Tenants.Endpoints.Tenants.Add; + +public class Endpoint(ITenantService tenantService, IIdentityGenerator identityGenerator, ITenantStore tenantStore) : ElsaEndpoint +{ + public override void Configure() + { + Post("/tenants"); + ConfigurePermissions("write:tenants"); + } + + public override async Task HandleAsync(NewTenant req, CancellationToken ct) + { + var tenant = new Tenant + { + Id = req.IsDefault ? string.Empty : req.Id ?? identityGenerator.GenerateId(), + TenantId = req.TenantId, + Name = req.Name.Trim(), + Configuration = Serializers.DeserializeConfiguration(req.Configuration) + }; + await tenantStore.AddAsync(tenant, ct); + await tenantService.RefreshAsync(ct); + await SendOkAsync(tenant, ct); + } +} + +public class NewTenant +{ + public bool IsDefault { get; set; } + public string? Id { get; set; } + public string? TenantId { get; set; } + public string Name { get; set; } = default!; + public JsonElement? Configuration { get; set; } +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Endpoints/Tenants/Delete/Endpoint.cs b/src/modules/Elsa.Tenants/Endpoints/Tenants/Delete/Endpoint.cs new file mode 100644 index 000000000..96ea75a32 --- /dev/null +++ b/src/modules/Elsa.Tenants/Endpoints/Tenants/Delete/Endpoint.cs @@ -0,0 +1,28 @@ +using Elsa.Abstractions; +using Elsa.Common.Multitenancy; + +namespace Elsa.Tenants.Endpoints.Tenants.Delete; + +public class Endpoint(ITenantService tenantService, ITenantStore store) : ElsaEndpointWithoutRequest +{ + public override void Configure() + { + Delete("/tenants/{id}"); + ConfigurePermissions("delete:tenants"); + } + + public override async Task HandleAsync(CancellationToken ct) + { + var id = Route("id")!; + var found = await store.DeleteAsync(id, ct); + + if (!found) + { + await SendNotFoundAsync(ct); + return; + } + + await tenantService.RefreshAsync(ct); + await SendOkAsync(ct); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Endpoints/Tenants/Get/Endpoint.cs b/src/modules/Elsa.Tenants/Endpoints/Tenants/Get/Endpoint.cs new file mode 100644 index 000000000..ba8b0dc16 --- /dev/null +++ b/src/modules/Elsa.Tenants/Endpoints/Tenants/Get/Endpoint.cs @@ -0,0 +1,28 @@ +using Elsa.Abstractions; +using Elsa.Common.Multitenancy; +using Elsa.Models; + +namespace Elsa.Tenants.Endpoints.Tenants.Get; + +public class Endpoint(ITenantService tenantService) : ElsaEndpointWithoutRequest +{ + public override void Configure() + { + Get("/tenants/{id}"); + ConfigurePermissions("read:tenants"); + } + + public override async Task HandleAsync(CancellationToken ct) + { + var id = Route("id")!; + var tenant = await tenantService.FindAsync(id, ct); + + if (tenant == null) + { + await SendNotFoundAsync(ct); + return; + } + + await SendOkAsync(tenant, ct); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Endpoints/Tenants/List/Endpoint.cs b/src/modules/Elsa.Tenants/Endpoints/Tenants/List/Endpoint.cs new file mode 100644 index 000000000..489a581c6 --- /dev/null +++ b/src/modules/Elsa.Tenants/Endpoints/Tenants/List/Endpoint.cs @@ -0,0 +1,25 @@ +using System.Net.Mime; +using System.Text.Json; +using Elsa.Abstractions; +using Elsa.Common.Multitenancy; +using Elsa.Common.Serialization; +using Elsa.Models; + +namespace Elsa.Tenants.Endpoints.Tenants.List; + +public class Endpoint(ITenantService tenantService) : ElsaEndpointWithoutRequest> +{ + public override void Configure() + { + Get("/tenants"); + ConfigurePermissions("read:tenants"); + } + + public override async Task HandleAsync(CancellationToken ct) + { + var tenants = await tenantService.ListAsync(ct); + var response = new ListResponse(tenants.ToList()); + var json = JsonSerializer.Serialize(response, SerializerOptions.ConfigurationJsonSerializerOptions); + await SendStringAsync(json, contentType: MediaTypeNames.Application.Json, cancellation: ct); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Endpoints/Tenants/Refresh/Endpoint.cs b/src/modules/Elsa.Tenants/Endpoints/Tenants/Refresh/Endpoint.cs new file mode 100644 index 000000000..aa7a3494e --- /dev/null +++ b/src/modules/Elsa.Tenants/Endpoints/Tenants/Refresh/Endpoint.cs @@ -0,0 +1,19 @@ +using Elsa.Abstractions; +using Elsa.Common.Multitenancy; + +namespace Elsa.Tenants.Endpoints.Tenants.Refresh; + +public class Endpoint(ITenantService tenantService) : ElsaEndpointWithoutRequest +{ + public override void Configure() + { + Post("/tenants/refresh"); + ConfigurePermissions("execute:tenants:refresh"); + } + + public override async Task HandleAsync(CancellationToken ct) + { + await tenantService.RefreshAsync(ct); + await SendOkAsync(ct); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Endpoints/Tenants/Update/Endpoint.cs b/src/modules/Elsa.Tenants/Endpoints/Tenants/Update/Endpoint.cs new file mode 100644 index 000000000..255eeba88 --- /dev/null +++ b/src/modules/Elsa.Tenants/Endpoints/Tenants/Update/Endpoint.cs @@ -0,0 +1,43 @@ +using System.Text.Json; +using Elsa.Abstractions; +using Elsa.Common.Multitenancy; +using Elsa.Common.Serialization; + +namespace Elsa.Tenants.Endpoints.Tenants.Update; + +public class Endpoint(ITenantService tenantService, ITenantStore tenantStore) : ElsaEndpoint +{ + public override void Configure() + { + Post("/tenants/{id}"); + ConfigurePermissions("write:tenants"); + } + + public override async Task HandleAsync(UpdatedTenant req, CancellationToken ct) + { + var id = Route("id")!; + if (id == "-") id = string.Empty; + var tenant = await tenantStore.FindAsync(id, ct); + + if (tenant == null) + { + await SendNotFoundAsync(ct); + return; + } + + tenant.TenantId = req.TenantId; + tenant.Name = req.Name.Trim(); + tenant.Configuration = Serializers.DeserializeConfiguration(req.Configuration); + await tenantStore.UpdateAsync(tenant, ct); + await tenantService.RefreshAsync(ct); + await SendOkAsync(tenant, ct); + } +} + +public class UpdatedTenant +{ + public string Id { get; set; } = default!; + public string? TenantId { get; set; } + public string Name { get; set; } = default!; + public JsonElement? Configuration { get; set; } +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Extensions/ModuleExtensions.cs b/src/modules/Elsa.Tenants/Extensions/ModuleExtensions.cs index 052a29495..b8c03eaa0 100644 --- a/src/modules/Elsa.Tenants/Extensions/ModuleExtensions.cs +++ b/src/modules/Elsa.Tenants/Extensions/ModuleExtensions.cs @@ -17,4 +17,22 @@ public static class ModuleExtensions module.Configure(configure); return module; } + + /// + /// Installs and configures the feature. + /// + public static TenantsFeature UseTenantManagementEndpoints(this TenantsFeature feature, Action? configure = default) + { + feature.Module.Configure(configure); + return feature; + } + + /// + /// Installs and configures the feature. + /// + public static TenantsFeature UseTenantManagement(this TenantsFeature feature, Action? configure = default) + { + feature.Module.Configure(configure); + return feature; + } } \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Features/TenantManagementEndpointsFeature.cs b/src/modules/Elsa.Tenants/Features/TenantManagementEndpointsFeature.cs new file mode 100644 index 000000000..af61a1b4e --- /dev/null +++ b/src/modules/Elsa.Tenants/Features/TenantManagementEndpointsFeature.cs @@ -0,0 +1,19 @@ +using Elsa.Extensions; +using Elsa.Features.Abstractions; +using Elsa.Features.Attributes; +using Elsa.Features.Services; + +namespace Elsa.Tenants.Features; + +/// +/// Enables tenant management endpoints. +/// +[DependsOn(typeof(TenantManagementFeature))] +public class TenantManagementEndpointsFeature(IModule serviceConfiguration) : FeatureBase(serviceConfiguration) +{ + /// + public override void Configure() + { + Module.AddFastEndpointsAssembly(); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Features/TenantManagementFeature.cs b/src/modules/Elsa.Tenants/Features/TenantManagementFeature.cs new file mode 100644 index 000000000..f957ba567 --- /dev/null +++ b/src/modules/Elsa.Tenants/Features/TenantManagementFeature.cs @@ -0,0 +1,29 @@ +using Elsa.Common.Multitenancy; +using Elsa.Extensions; +using Elsa.Features.Abstractions; +using Elsa.Features.Services; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Tenants.Features; + +/// +/// Enables tenant management endpoints. +/// +public class TenantManagementFeature(IModule serviceConfiguration) : FeatureBase(serviceConfiguration) +{ + private Func _tenantStoreFactory = sp => sp.GetRequiredService(); + + public TenantManagementFeature WithTenantStore(Func factory) + { + _tenantStoreFactory = factory; + return this; + } + + /// + public override void Apply() + { + Services + .AddMemoryStore() + .AddScoped(_tenantStoreFactory); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Features/TenantsFeature.cs b/src/modules/Elsa.Tenants/Features/TenantsFeature.cs index 95f6f4d41..a1a850eb4 100644 --- a/src/modules/Elsa.Tenants/Features/TenantsFeature.cs +++ b/src/modules/Elsa.Tenants/Features/TenantsFeature.cs @@ -4,7 +4,6 @@ using Elsa.Features.Abstractions; using Elsa.Features.Services; using Elsa.Tenants.Options; using Elsa.Tenants.Providers; -using Elsa.Tenants.Services; using Microsoft.Extensions.DependencyInjection; namespace Elsa.Tenants.Features; @@ -17,23 +16,37 @@ public class TenantsFeature(IModule serviceConfiguration) : FeatureBase(serviceC /// /// Configures the Tenants options. /// - private Action TenantsOptions { get; set; } = _ => { }; + private Action MultitenancyOptions { get; set; } = _ => { }; - public TenantsFeature ConfigureOptions(Action configure) + private Action TenantsOptions { get; set; } = _ => { }; + + public TenantsFeature ConfigureMultitenancy(Action configure) { Services.Configure(configure); return this; } - public void UseConfigurationBasedTenantsProvider() + public TenantsFeature ConfigureTenants(Action configure) { + Services.Configure(configure); + return this; + } + + public void UseConfigurationBasedTenantsProvider(Action configure) + { + ConfigureTenants(configure); Module.Configure(feature => feature.UseTenantsProvider()); } + + public void UseStoreBasedTenantsProvider() + { + Module.Configure(feature => feature.UseTenantsProvider()); + } /// public override void Apply() { - Services.Configure(TenantsOptions); + Services.Configure(MultitenancyOptions); Services .AddScoped() diff --git a/src/modules/Elsa.Tenants/Options/MultitenancyOptions.cs b/src/modules/Elsa.Tenants/Options/MultitenancyOptions.cs index f701bb425..2ad139414 100644 --- a/src/modules/Elsa.Tenants/Options/MultitenancyOptions.cs +++ b/src/modules/Elsa.Tenants/Options/MultitenancyOptions.cs @@ -1,6 +1,4 @@ using Elsa.Common.Multitenancy; -using Elsa.Tenants.Providers; -using Elsa.Tenants.Services; namespace Elsa.Tenants.Options; @@ -9,11 +7,6 @@ namespace Elsa.Tenants.Options; /// public class MultitenancyOptions { - /// - /// Gets or sets the tenants through configuration. Will be used by the - /// - public ICollection Tenants { get; set; } = new List(); - /// /// Gets or sets the tenant resolution pipeline builder. /// diff --git a/src/modules/Elsa.Tenants/Options/TenantsOptions.cs b/src/modules/Elsa.Tenants/Options/TenantsOptions.cs new file mode 100644 index 000000000..131c67264 --- /dev/null +++ b/src/modules/Elsa.Tenants/Options/TenantsOptions.cs @@ -0,0 +1,15 @@ +using Elsa.Common.Multitenancy; +using Elsa.Tenants.Providers; + +namespace Elsa.Tenants.Options; + +/// +/// Options for configuring the Tenants. +/// +public class TenantsOptions +{ + /// + /// Gets or sets the tenants through configuration. Will be used by the + /// + public ICollection Tenants { get; set; } = new List(); +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Providers/ConfigurationTenantsProvider.cs b/src/modules/Elsa.Tenants/Providers/ConfigurationTenantsProvider.cs index 9401bd894..80d272edb 100644 --- a/src/modules/Elsa.Tenants/Providers/ConfigurationTenantsProvider.cs +++ b/src/modules/Elsa.Tenants/Providers/ConfigurationTenantsProvider.cs @@ -1,7 +1,8 @@ -using System.Diagnostics.CodeAnalysis; -using Elsa.Common.Multitenancy; +using Elsa.Common.Multitenancy; using Elsa.Tenants.Options; +using JetBrains.Annotations; using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; namespace Elsa.Tenants.Providers; @@ -9,36 +10,61 @@ namespace Elsa.Tenants.Providers; /// /// Provides the implementation to retrieve tenant information from a configuration. /// +[UsedImplicitly] public class ConfigurationTenantsProvider : ITenantsProvider { - private readonly ICollection _tenants; + private readonly IConfiguration _configuration; + private readonly ITenantService _tenantService; + private readonly ILogger _logger; + private ICollection _tenants = new List(); /// /// Initializes a new instance of the class. /// - public ConfigurationTenantsProvider(IOptions options, IConfiguration configuration) + public ConfigurationTenantsProvider(IOptionsMonitor options, IConfiguration configuration, ITenantService tenantService, ILogger logger) { - var tenants = options.Value.Tenants.ToList(); + _configuration = configuration; + _tenantService = tenantService; + _logger = logger; + UpdateTenants(options.CurrentValue); + options.OnChange(OnOptionsChanged); + } + + private async void OnOptionsChanged(TenantsOptions options, string? name) + { + try + { + UpdateTenants(options); + await _tenantService.RefreshAsync(); + } + catch (Exception e) + { + _logger.LogError(e, "An error occurred while updating tenants."); + } + } + + private void UpdateTenants(TenantsOptions options) + { + var tenants = options.Tenants.ToList(); // Rebind each Tenant's Configuration property manually using array indices for (int i = 0; i < tenants.Count; i++) - tenants[i].Configuration = configuration.GetSection($"Multitenancy:Tenants:{i}:Configuration"); + tenants[i].Configuration = _configuration.GetSection($"Multitenancy:Tenants:{i}:Configuration"); _tenants = tenants; } /// - public ValueTask> ListAsync(CancellationToken cancellationToken = default) + public Task> ListAsync(CancellationToken cancellationToken = default) { - return new(_tenants); + return Task.FromResult>(_tenants); } /// - [RequiresUnreferencedCode("The caller of this method may require dynamic access to the tenant properties.")] - public ValueTask FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) + public Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) { var tenantsQueryable = _tenants.AsQueryable(); var tenant = filter.Apply(tenantsQueryable).FirstOrDefault(); - return new(tenant); + return Task.FromResult(tenant); } } diff --git a/src/modules/Elsa.Tenants/Providers/StoreTenantsProvider.cs b/src/modules/Elsa.Tenants/Providers/StoreTenantsProvider.cs new file mode 100644 index 000000000..5c8371059 --- /dev/null +++ b/src/modules/Elsa.Tenants/Providers/StoreTenantsProvider.cs @@ -0,0 +1,18 @@ +using Elsa.Common.Multitenancy; +using JetBrains.Annotations; + +namespace Elsa.Tenants.Providers; + +[UsedImplicitly] +public class StoreTenantsProvider(ITenantStore store) : ITenantsProvider +{ + public async Task> ListAsync(CancellationToken cancellationToken = default) + { + return await store.ListAsync(cancellationToken); + } + + public Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + return store.FindAsync(filter, cancellationToken); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Services/DefaultTenantResolverPipelineInvoker.cs b/src/modules/Elsa.Tenants/Services/DefaultTenantResolverPipelineInvoker.cs index 6df875af2..c0b832f32 100644 --- a/src/modules/Elsa.Tenants/Services/DefaultTenantResolverPipelineInvoker.cs +++ b/src/modules/Elsa.Tenants/Services/DefaultTenantResolverPipelineInvoker.cs @@ -3,7 +3,7 @@ using Elsa.Tenants.Options; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -namespace Elsa.Tenants.Services; +namespace Elsa.Tenants; /// /// Resolves the tenant using a pipeline of resolvers. diff --git a/src/modules/Elsa.Tenants/Services/MemoryTenantStore.cs b/src/modules/Elsa.Tenants/Services/MemoryTenantStore.cs new file mode 100644 index 000000000..d071674a5 --- /dev/null +++ b/src/modules/Elsa.Tenants/Services/MemoryTenantStore.cs @@ -0,0 +1,61 @@ +using Elsa.Common.Multitenancy; +using Elsa.Common.Services; +using JetBrains.Annotations; + +namespace Elsa.Tenants; + +[UsedImplicitly] +public class MemoryTenantStore(MemoryStore store) : ITenantStore +{ + public Task FindAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + var result = store.Query(query => Filter(query, filter)).FirstOrDefault(); + return Task.FromResult(result); + } + + public Task FindAsync(string id, CancellationToken cancellationToken = default) + { + var filter = TenantFilter.ById(id); + return FindAsync(filter, cancellationToken); + } + + public Task> FindManyAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + var result = store.Query(query => Filter(query, filter)).ToList().AsEnumerable(); + return Task.FromResult(result); + } + + public Task> ListAsync(CancellationToken cancellationToken = default) + { + var result = store.List(); + return Task.FromResult(result); + } + + public Task AddAsync(Tenant tenant, CancellationToken cancellationToken = default) + { + store.Add(tenant, GetId); + return Task.CompletedTask; + } + + public Task UpdateAsync(Tenant tenant, CancellationToken cancellationToken = default) + { + store.Update(tenant, GetId); + return Task.CompletedTask; + } + + public Task DeleteAsync(string id, CancellationToken cancellationToken = default) + { + var found = store.Delete(id); + return Task.FromResult(found); + } + + public Task DeleteAsync(TenantFilter filter, CancellationToken cancellationToken = default) + { + var deletedCount = store.DeleteMany(filter.Apply(store.Queryable), GetId); + return Task.FromResult(deletedCount); + } + + private IQueryable Filter(IQueryable queryable, TenantFilter filter) => filter.Apply(queryable); + + private string GetId(Tenant tenant) => tenant.Id; +} \ No newline at end of file diff --git a/src/modules/Elsa.Tenants/Services/TenantResolutionPipelineBuilder.cs b/src/modules/Elsa.Tenants/Services/TenantResolutionPipelineBuilder.cs index 82a4bd135..acb456a00 100644 --- a/src/modules/Elsa.Tenants/Services/TenantResolutionPipelineBuilder.cs +++ b/src/modules/Elsa.Tenants/Services/TenantResolutionPipelineBuilder.cs @@ -1,7 +1,7 @@ using Elsa.Common.Multitenancy; using Microsoft.Extensions.DependencyInjection; -namespace Elsa.Tenants.Services; +namespace Elsa.Tenants; /// public class TenantResolverPipelineBuilder : ITenantResolverPipelineBuilder diff --git a/src/modules/Elsa.Workflows.Management/Stores/MemoryWorkflowDefinitionStore.cs b/src/modules/Elsa.Workflows.Management/Stores/MemoryWorkflowDefinitionStore.cs index 557d1e136..d30153a23 100644 --- a/src/modules/Elsa.Workflows.Management/Stores/MemoryWorkflowDefinitionStore.cs +++ b/src/modules/Elsa.Workflows.Management/Stores/MemoryWorkflowDefinitionStore.cs @@ -10,138 +10,128 @@ namespace Elsa.Workflows.Management.Stores; /// /// A memory implementation of . /// -public class MemoryWorkflowDefinitionStore : IWorkflowDefinitionStore +public class MemoryWorkflowDefinitionStore(MemoryStore store) : IWorkflowDefinitionStore { - private readonly MemoryStore _store; - - /// - /// Constructor. - /// - public MemoryWorkflowDefinitionStore(MemoryStore store) - { - _store = store; - } - /// public Task FindAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default) { - var result = _store.Query(query => Filter(query, filter)).FirstOrDefault(); + var result = store.Query(query => Filter(query, filter)).FirstOrDefault(); return Task.FromResult(result); } /// public Task FindAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, CancellationToken cancellationToken = default) { - var result = _store.Query(query => Filter(query, filter).OrderBy(order)).FirstOrDefault(); + var result = store.Query(query => Filter(query, filter).OrderBy(order)).FirstOrDefault(); return Task.FromResult(result); } /// public Task> FindManyAsync(WorkflowDefinitionFilter filter, PageArgs pageArgs, CancellationToken cancellationToken = default) { - var count = _store.Query(query => Filter(query, filter)).LongCount(); - var result = _store.Query(query => Filter(query, filter).Paginate(pageArgs)).ToList(); + var count = store.Query(query => Filter(query, filter)).LongCount(); + var result = store.Query(query => Filter(query, filter).Paginate(pageArgs)).ToList(); return Task.FromResult(Page.Of(result, count)); } /// public Task> FindManyAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, PageArgs pageArgs, CancellationToken cancellationToken = default) { - var count = _store.Query(query => Filter(query, filter).OrderBy(order)).LongCount(); - var result = _store.Query(query => Filter(query, filter).Paginate(pageArgs)).ToList(); + var count = store.Query(query => Filter(query, filter).OrderBy(order)).LongCount(); + var result = store.Query(query => Filter(query, filter).Paginate(pageArgs)).ToList(); return Task.FromResult(Page.Of(result, count)); } /// public Task> FindManyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default) { - var result = _store.Query(query => Filter(query, filter)).ToList().AsEnumerable(); + var result = store.Query(query => Filter(query, filter)).ToList().AsEnumerable(); return Task.FromResult(result); } /// public Task> FindManyAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, CancellationToken cancellationToken = default) { - var result = _store.Query(query => Filter(query, filter).OrderBy(order)).ToList().AsEnumerable(); + var result = store.Query(query => Filter(query, filter).OrderBy(order)).ToList().AsEnumerable(); return Task.FromResult(result); } /// public Task> FindSummariesAsync(WorkflowDefinitionFilter filter, PageArgs pageArgs, CancellationToken cancellationToken = default) { - var count = _store.Query(query => Filter(query, filter)).LongCount(); - var result = _store.Query(query => Filter(query, filter).Paginate(pageArgs)).Select(WorkflowDefinitionSummary.FromDefinition).ToList(); + var count = store.Query(query => Filter(query, filter)).LongCount(); + var result = store.Query(query => Filter(query, filter).Paginate(pageArgs)).Select(WorkflowDefinitionSummary.FromDefinition).ToList(); return Task.FromResult(Page.Of(result, count)); } /// public Task> FindSummariesAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, PageArgs pageArgs, CancellationToken cancellationToken = default) { - var count = _store.Query(query => Filter(query, filter).OrderBy(order)).LongCount(); - var result = _store.Query(query => Filter(query, filter).Paginate(pageArgs)).Select(WorkflowDefinitionSummary.FromDefinition).ToList(); + var count = store.Query(query => Filter(query, filter).OrderBy(order)).LongCount(); + var result = store.Query(query => Filter(query, filter).Paginate(pageArgs)).Select(WorkflowDefinitionSummary.FromDefinition).ToList(); return Task.FromResult(Page.Of(result, count)); } /// public Task> FindSummariesAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default) { - var result = _store.Query(query => Filter(query, filter)).Select(WorkflowDefinitionSummary.FromDefinition).ToList().AsEnumerable(); + var result = store.Query(query => Filter(query, filter)).Select(WorkflowDefinitionSummary.FromDefinition).ToList().AsEnumerable(); return Task.FromResult(result); } /// public Task> FindSummariesAsync(WorkflowDefinitionFilter filter, WorkflowDefinitionOrder order, CancellationToken cancellationToken = default) { - var result = _store.Query(query => Filter(query, filter).OrderBy(order)).Select(WorkflowDefinitionSummary.FromDefinition).ToList().AsEnumerable(); + var result = store.Query(query => Filter(query, filter).OrderBy(order)).Select(WorkflowDefinitionSummary.FromDefinition).ToList().AsEnumerable(); return Task.FromResult(result); } /// public Task FindLastVersionAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken) { - var result = _store.Query(query => Filter(query, filter)).MaxBy(x => x.Version); + var result = store.Query(query => Filter(query, filter)).MaxBy(x => x.Version); return Task.FromResult(result); } /// public Task SaveAsync(WorkflowDefinition definition, CancellationToken cancellationToken = default) { - _store.Save(definition, GetId); + store.Save(definition, GetId); return Task.CompletedTask; } /// public Task SaveManyAsync(IEnumerable definitions, CancellationToken cancellationToken = default) { - _store.SaveMany(definitions, GetId); + store.SaveMany(definitions, GetId); return Task.CompletedTask; } /// public Task DeleteAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default) { - var workflowDefinitionIds = _store.Query(query => Filter(query, filter)).Select(x => x.DefinitionId).Distinct().ToList(); - _store.DeleteWhere(x => workflowDefinitionIds.Contains(x.DefinitionId)); + var workflowDefinitionIds = store.Query(query => Filter(query, filter)).Select(x => x.DefinitionId).Distinct().ToList(); + store.DeleteWhere(x => workflowDefinitionIds.Contains(x.DefinitionId)); return Task.FromResult(workflowDefinitionIds.LongCount()); } /// public Task AnyAsync(WorkflowDefinitionFilter filter, CancellationToken cancellationToken = default) { - var exists = _store.Query(query => Filter(query, filter)).Any(); + var exists = store.Query(query => Filter(query, filter)).Any(); return Task.FromResult(exists); } /// public Task CountDistinctAsync(CancellationToken cancellationToken = default) { - return Task.FromResult(_store.Count(x => true, x => x.DefinitionId)); + return Task.FromResult(store.Count(x => true, x => x.DefinitionId)); } /// public Task GetIsNameUnique(string name, string? definitionId = default, CancellationToken cancellationToken = default) { - var exists = _store.Any(x => x.Name == name && x.DefinitionId != definitionId); + var exists = store.Any(x => x.Name == name && x.DefinitionId != definitionId); return Task.FromResult(!exists); } diff --git a/src/modules/Elsa.Workflows.Runtime/Exceptions/WorkflowGraphNotFoundException.cs b/src/modules/Elsa.Workflows.Runtime/Exceptions/WorkflowGraphNotFoundException.cs new file mode 100644 index 000000000..1d0f302fe --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/Exceptions/WorkflowGraphNotFoundException.cs @@ -0,0 +1,8 @@ +using Elsa.Workflows.Models; + +namespace Elsa.Workflows.Runtime.Exceptions; + +public class WorkflowGraphNotFoundException(string message, WorkflowDefinitionHandle workflowDefinitionHandle) : Exception(message) +{ + public WorkflowDefinitionHandle WorkflowDefinitionHandle { get; } = workflowDefinitionHandle; +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs b/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs index 7f9d301d4..5bf4f976d 100644 --- a/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs +++ b/src/modules/Elsa.Workflows.Runtime/Features/WorkflowRuntimeFeature.cs @@ -31,7 +31,6 @@ namespace Elsa.Workflows.Runtime.Features; /// Installs and configures workflow runtime features. /// [DependsOn(typeof(SystemClockFeature))] -[DependsOn(typeof(BackgroundTasksFeature))] public class WorkflowRuntimeFeature : FeatureBase { /// diff --git a/src/modules/Elsa.Workflows.Runtime/Filters/BookmarkFilter.cs b/src/modules/Elsa.Workflows.Runtime/Filters/BookmarkFilter.cs index 3b43f2128..79823840b 100644 --- a/src/modules/Elsa.Workflows.Runtime/Filters/BookmarkFilter.cs +++ b/src/modules/Elsa.Workflows.Runtime/Filters/BookmarkFilter.cs @@ -81,4 +81,9 @@ public class BookmarkFilter return query; } -} + + public static BookmarkFilter ByActivityTypeNames(IEnumerable activityTypeNames) => new() + { + ActivityTypeNames = activityTypeNames.ToList() + }; +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Filters/TriggerFilter.cs b/src/modules/Elsa.Workflows.Runtime/Filters/TriggerFilter.cs index a93b7573e..cab0d7536 100644 --- a/src/modules/Elsa.Workflows.Runtime/Filters/TriggerFilter.cs +++ b/src/modules/Elsa.Workflows.Runtime/Filters/TriggerFilter.cs @@ -73,4 +73,9 @@ public class TriggerFilter if (Hash != null) queryable = queryable.Where(x => x.Hash == Hash); return queryable; } -} + + public static TriggerFilter ByNames(IEnumerable names) => new() + { + Names = names.ToList() + }; +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowStarter.cs b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowStarter.cs index 47d974760..11d861160 100644 --- a/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowStarter.cs +++ b/src/modules/Elsa.Workflows.Runtime/Services/DefaultWorkflowStarter.cs @@ -1,6 +1,7 @@ using Elsa.Workflows.Activities; using Elsa.Workflows.Management; using Elsa.Workflows.Models; +using Elsa.Workflows.Runtime.Exceptions; using Elsa.Workflows.Runtime.Messages; namespace Elsa.Workflows.Runtime; @@ -55,7 +56,7 @@ public class DefaultWorkflowStarter(IWorkflowDefinitionService workflowDefinitio var workflowGraph = await workflowDefinitionService.FindWorkflowGraphAsync(request.WorkflowDefinitionHandle, cancellationToken); if (workflowGraph == null) - throw new InvalidOperationException($"Workflow definition with handle {request.WorkflowDefinitionHandle} was not found."); + throw new WorkflowGraphNotFoundException($"Workflow definition not found.", request.WorkflowDefinitionHandle); return workflowGraph.Workflow; }