1
0
mirror of https://github.com/bitwarden/server synced 2026-02-19 10:53:34 +00:00
Files
server/util/Seeder/Pipeline/PresetLoader.cs
2026-02-17 07:42:53 +01:00

103 lines
3.4 KiB
C#

using Bit.Seeder.Models;
using Bit.Seeder.Services;
using Microsoft.Extensions.DependencyInjection;
namespace Bit.Seeder.Pipeline;
/// <summary>
/// Loads preset fixtures and registers them as recipes on <see cref="IServiceCollection"/>.
/// </summary>
internal static class PresetLoader
{
/// <summary>
/// Loads a preset from embedded fixtures and registers its steps as a recipe.
/// </summary>
/// <param name="presetName">Preset name without extension (e.g., "dunder-mifflin-full")</param>
/// <param name="reader">Service for reading embedded seed JSON files</param>
/// <param name="services">The service collection to register steps in</param>
/// <exception cref="InvalidOperationException">Thrown when preset lacks organization configuration</exception>
internal static void RegisterRecipe(string presetName, ISeedReader reader, IServiceCollection services)
{
var preset = reader.Read<SeedPreset>($"presets.{presetName}");
if (preset.Organization is null)
{
throw new InvalidOperationException(
$"Preset '{presetName}' must specify an organization.");
}
BuildRecipe(presetName, preset, reader, services);
}
/// <summary>
/// Builds a recipe from preset configuration, resolving fixtures and generation counts.
/// </summary>
/// <remarks>
/// Resolution order: Org → Owner → Generator → Roster → Users → Groups → Collections → Ciphers
/// </remarks>
private static void BuildRecipe(string presetName, SeedPreset preset, ISeedReader reader, IServiceCollection services)
{
var builder = services.AddRecipe(presetName);
var org = preset.Organization!;
// Resolve domain - either from preset or from fixture
var domain = org.Domain;
if (org.Fixture is not null)
{
builder.UseOrganization(org.Fixture);
// If using a fixture and domain not explicitly provided, read it from the fixture
if (domain is null)
{
var orgFixture = reader.Read<SeedOrganization>($"organizations.{org.Fixture}");
domain = orgFixture.Domain;
}
}
else if (org.Name is not null && org.Domain is not null)
{
builder.CreateOrganization(org.Name, org.Domain, org.Seats);
domain = org.Domain;
}
builder.AddOwner();
// Generator requires a domain and is only needed for generated ciphers
if (domain is not null && preset.Ciphers?.Count > 0)
{
builder.WithGenerator(domain);
}
if (preset.Roster?.Fixture is not null)
{
builder.UseRoster(preset.Roster.Fixture);
}
if (preset.Users is not null)
{
builder.AddUsers(preset.Users.Count, preset.Users.RealisticStatusMix);
}
if (preset.Groups is not null)
{
builder.AddGroups(preset.Groups.Count);
}
if (preset.Collections is not null)
{
builder.AddCollections(preset.Collections.Count);
}
if (preset.Ciphers?.Fixture is not null)
{
builder.UseCiphers(preset.Ciphers.Fixture);
}
else if (preset.Ciphers is not null && preset.Ciphers.Count > 0)
{
builder.AddCiphers(preset.Ciphers.Count);
}
builder.Validate();
}
}