diff --git a/util/Seeder/Commands/GenerateCommand.cs b/util/Seeder/Commands/GenerateCommand.cs index bf589046bc..eb15b828df 100644 --- a/util/Seeder/Commands/GenerateCommand.cs +++ b/util/Seeder/Commands/GenerateCommand.cs @@ -1,8 +1,6 @@ -using Bit.Infrastructure.EntityFramework.Models; -using Bit.Seeder.Factories; +using Bit.Seeder.Recipes; using Bit.Seeder.Settings; using Bit.SharedWeb.Utilities; -using LinqToDB.EntityFrameworkCore; using Microsoft.AspNetCore.DataProtection; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; @@ -19,40 +17,15 @@ public class GenerateCommand ConfigureServices(services); var serviceProvider = services.BuildServiceProvider(); - var logger = serviceProvider.GetRequiredService>(); + // TODO: Can we remove GenerateCommand and provide a RecipeFactory or something. Or wire up DI. + using var scope = serviceProvider.CreateScope(); + var scopedServices = scope.ServiceProvider; + var db = scopedServices.GetRequiredService(); - var organization = OrganizationSeeder.CreateEnterprise(name, domain, users); - var user = UserSeeder.CreateUser($"admin@{domain}"); - var orgUser = organization.CreateOrganizationUser(user); - - var additionalUsers = new List(); - var additionalOrgUsers = new List(); - for (var i = 0; i < users; i++) - { - var additionalUser = UserSeeder.CreateUser($"user{i}@{domain}"); - additionalUsers.Add(additionalUser); - additionalOrgUsers.Add(organization.CreateOrganizationUser(additionalUser)); - } - - - using (var scope = serviceProvider.CreateScope()) - { - var scopedServices = scope.ServiceProvider; - var db = scopedServices.GetRequiredService(); - - db.Add(organization); - db.Add(user); - db.Add(orgUser); - - db.SaveChanges(); - - // Use LinqToDB's BulkCopy for significant better performance - db.BulkCopy(additionalUsers); - db.BulkCopy(additionalOrgUsers); - } + var recipe = new OrganizationWithUsersRecipe(db); + recipe.Seed(name, users, domain); return true; - } private void ConfigureServices(ServiceCollection services) diff --git a/util/Seeder/Recipes/OrganizationWithUsersRecipe.cs b/util/Seeder/Recipes/OrganizationWithUsersRecipe.cs new file mode 100644 index 0000000000..0b9dd44a26 --- /dev/null +++ b/util/Seeder/Recipes/OrganizationWithUsersRecipe.cs @@ -0,0 +1,35 @@ +using Bit.Infrastructure.EntityFramework.Models; +using Bit.Infrastructure.EntityFramework.Repositories; +using Bit.Seeder.Factories; +using LinqToDB.EntityFrameworkCore; + +namespace Bit.Seeder.Recipes; + +public class OrganizationWithUsersRecipe(DatabaseContext db) +{ + public void Seed(string name, int users, string domain) + { + var organization = OrganizationSeeder.CreateEnterprise(name, domain, users); + var user = UserSeeder.CreateUser($"admin@{domain}"); + var orgUser = organization.CreateOrganizationUser(user); + + var additionalUsers = new List(); + var additionalOrgUsers = new List(); + for (var i = 0; i < users; i++) + { + var additionalUser = UserSeeder.CreateUser($"user{i}@{domain}"); + additionalUsers.Add(additionalUser); + additionalOrgUsers.Add(organization.CreateOrganizationUser(additionalUser)); + } + + db.Add(organization); + db.Add(user); + db.Add(orgUser); + + db.SaveChanges(); + + // Use LinqToDB's BulkCopy for significant better performance + db.BulkCopy(additionalUsers); + db.BulkCopy(additionalOrgUsers); + } +} diff --git a/util/Seeder/Services/DatabaseContext.cs b/util/Seeder/Services/DatabaseContext.cs deleted file mode 100644 index b4eb51dc2d..0000000000 --- a/util/Seeder/Services/DatabaseContext.cs +++ /dev/null @@ -1,75 +0,0 @@ -using Bit.Core.Entities; -using Bit.Core.Settings; -using Bit.Core.Vault.Entities; -using Microsoft.EntityFrameworkCore; - -namespace Bit.Seeder.Services; - -public class DatabaseContext : DbContext -{ - private readonly GlobalSettings _globalSettings; - - public DatabaseContext(GlobalSettings globalSettings) - { - _globalSettings = globalSettings; - } - - public DbSet Users { get; set; } - public DbSet Ciphers { get; set; } - - protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) - { - var provider = _globalSettings.DatabaseProvider ?? string.Empty; - Console.WriteLine($"Database Provider: '{provider}'"); - - // Output all available connection strings for debugging - Console.WriteLine($"SqlServer ConnectionString available: {!string.IsNullOrEmpty(_globalSettings.SqlServer?.ConnectionString)}"); - Console.WriteLine($"PostgreSql ConnectionString available: {!string.IsNullOrEmpty(_globalSettings.PostgreSql?.ConnectionString)}"); - Console.WriteLine($"MySql ConnectionString available: {!string.IsNullOrEmpty(_globalSettings.MySql?.ConnectionString)}"); - Console.WriteLine($"Sqlite ConnectionString available: {!string.IsNullOrEmpty(_globalSettings.Sqlite?.ConnectionString)}"); - - var connectionString = _globalSettings.DatabaseProvider switch - { - "postgres" => _globalSettings.PostgreSql?.ConnectionString, - "mysql" => _globalSettings.MySql?.ConnectionString, - "sqlite" => _globalSettings.Sqlite?.ConnectionString, - _ => _globalSettings.SqlServer?.ConnectionString - }; - - Console.WriteLine($"Using connection string: {connectionString}"); - - switch (_globalSettings.DatabaseProvider) - { - case "postgres": - optionsBuilder.UseNpgsql(connectionString); - break; - case "mysql": - optionsBuilder.UseMySql(connectionString, ServerVersion.AutoDetect(connectionString)); - break; - case "sqlite": - optionsBuilder.UseSqlite(connectionString); - break; - default: - optionsBuilder.UseSqlServer(connectionString); - break; - } - } - - protected override void OnModelCreating(ModelBuilder modelBuilder) - { - base.OnModelCreating(modelBuilder); - - modelBuilder.Entity(entity => - { - entity.ToTable("Cipher"); - entity.Property(e => e.Type).HasConversion(); - entity.Property(e => e.Reprompt).HasConversion(); - }); - - modelBuilder.Entity(entity => - { - entity.ToTable("User"); - entity.Property(e => e.Kdf).HasConversion(); - }); - } -}