Format ./web/backend/Database/Database.Seed

This commit is contained in:
Aaron Po
2026-06-20 15:13:32 -04:00
parent bf0d29fa54
commit 27f28d5207
5 changed files with 54 additions and 92 deletions

View File

@@ -8,16 +8,13 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="idunno.Password.Generator" Version="1.0.1"/> <PackageReference Include="idunno.Password.Generator" Version="1.0.1" />
<PackageReference <PackageReference Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" />
Include="Konscious.Security.Cryptography.Argon2" <PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4" />
Version="1.3.1" <PackageReference Include="dbup" Version="5.0.41" />
/>
<PackageReference Include="Microsoft.Data.SqlClient" Version="6.1.4"/>
<PackageReference Include="dbup" Version="5.0.41"/>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ProjectReference Include="..\..\Domain\Domain.Entities\Domain.Entities.csproj"/> <ProjectReference Include="..\..\Domain\Domain.Entities\Domain.Entities.csproj" />
</ItemGroup> </ItemGroup>
</Project> </Project>

View File

@@ -15,24 +15,22 @@ namespace Database.Seed;
internal class LocationSeeder : ISeeder internal class LocationSeeder : ISeeder
{ {
/// <summary>The set of countries to seed, identified by name and ISO 3166-1 code.</summary> /// <summary>The set of countries to seed, identified by name and ISO 3166-1 code.</summary>
private static readonly IReadOnlyList<( private static readonly IReadOnlyList<(string CountryName, string CountryCode)> Countries =
string CountryName,
string CountryCode
)> Countries =
[ [
("Canada", "CA"), ("Canada", "CA"),
("Mexico", "MX"), ("Mexico", "MX"),
("United States", "US") ("United States", "US"),
]; ];
/// <summary> /// <summary>
/// The set of states/provinces to seed, each identified by name, ISO 3166-2 code, /// The set of states/provinces to seed, each identified by name, ISO 3166-2 code,
/// and the ISO 3166-1 code of its parent country. /// and the ISO 3166-1 code of its parent country.
/// </summary> /// </summary>
private static IReadOnlyList<(string StateProvinceName, string StateProvinceCode, string CountryCode)> States private static IReadOnlyList<(
{ string StateProvinceName,
get; string StateProvinceCode,
} = string CountryCode
)> States { get; } =
[ [
("Alabama", "US-AL", "US"), ("Alabama", "US-AL", "US"),
("Alaska", "US-AK", "US"), ("Alaska", "US-AK", "US"),
@@ -134,7 +132,7 @@ internal class LocationSeeder : ISeeder
("Veracruz de Ignacio de la Llave", "MX-VER", "MX"), ("Veracruz de Ignacio de la Llave", "MX-VER", "MX"),
("Yucatán", "MX-YUC", "MX"), ("Yucatán", "MX-YUC", "MX"),
("Zacatecas", "MX-ZAC", "MX"), ("Zacatecas", "MX-ZAC", "MX"),
("Ciudad de México", "MX-CMX", "MX") ("Ciudad de México", "MX-CMX", "MX"),
]; ];
/// <summary> /// <summary>
@@ -255,7 +253,7 @@ internal class LocationSeeder : ISeeder
("MX-COA", "Saltillo"), ("MX-COA", "Saltillo"),
("MX-BCS", "La Paz"), ("MX-BCS", "La Paz"),
("MX-NAY", "Tepic"), ("MX-NAY", "Tepic"),
("MX-ZAC", "Zacatecas") ("MX-ZAC", "Zacatecas"),
]; ];
/// <summary> /// <summary>
@@ -269,9 +267,7 @@ internal class LocationSeeder : ISeeder
foreach ((string countryName, string countryCode) in Countries) foreach ((string countryName, string countryCode) in Countries)
await CreateCountryAsync(connection, countryName, countryCode); await CreateCountryAsync(connection, countryName, countryCode);
foreach ( foreach ((string stateProvinceName, string stateProvinceCode, string countryCode) in States)
(string stateProvinceName, string stateProvinceCode, string countryCode) in States
)
await CreateStateProvinceAsync( await CreateStateProvinceAsync(
connection, connection,
stateProvinceName, stateProvinceName,
@@ -294,10 +290,7 @@ internal class LocationSeeder : ISeeder
string countryCode string countryCode
) )
{ {
await using SqlCommand command = new( await using SqlCommand command = new("dbo.USP_CreateCountry", connection);
"dbo.USP_CreateCountry",
connection
);
command.CommandType = CommandType.StoredProcedure; command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@CountryName", countryName); command.Parameters.AddWithValue("@CountryName", countryName);
command.Parameters.AddWithValue("@ISO3166_1", countryCode); command.Parameters.AddWithValue("@ISO3166_1", countryCode);
@@ -318,15 +311,9 @@ internal class LocationSeeder : ISeeder
string countryCode string countryCode
) )
{ {
await using SqlCommand command = new( await using SqlCommand command = new("dbo.USP_CreateStateProvince", connection);
"dbo.USP_CreateStateProvince",
connection
);
command.CommandType = CommandType.StoredProcedure; command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue( command.Parameters.AddWithValue("@StateProvinceName", stateProvinceName);
"@StateProvinceName",
stateProvinceName
);
command.Parameters.AddWithValue("@ISO3166_2", stateProvinceCode); command.Parameters.AddWithValue("@ISO3166_2", stateProvinceCode);
command.Parameters.AddWithValue("@CountryCode", countryCode); command.Parameters.AddWithValue("@CountryCode", countryCode);
@@ -344,16 +331,10 @@ internal class LocationSeeder : ISeeder
string stateProvinceCode string stateProvinceCode
) )
{ {
await using SqlCommand command = new( await using SqlCommand command = new("dbo.USP_CreateCity", connection);
"dbo.USP_CreateCity",
connection
);
command.CommandType = CommandType.StoredProcedure; command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@CityName", cityName); command.Parameters.AddWithValue("@CityName", cityName);
command.Parameters.AddWithValue( command.Parameters.AddWithValue("@StateProvinceCode", stateProvinceCode);
"@StateProvinceCode",
stateProvinceCode
);
await command.ExecuteNonQueryAsync(); await command.ExecuteNonQueryAsync();
} }

View File

@@ -17,20 +17,24 @@ using Microsoft.Data.SqlClient;
/// </exception> /// </exception>
string BuildConnectionString() string BuildConnectionString()
{ {
string server = Environment.GetEnvironmentVariable("DB_SERVER") string server =
Environment.GetEnvironmentVariable("DB_SERVER")
?? throw new InvalidOperationException("DB_SERVER environment variable is not set"); ?? throw new InvalidOperationException("DB_SERVER environment variable is not set");
string dbName = Environment.GetEnvironmentVariable("DB_NAME") string dbName =
Environment.GetEnvironmentVariable("DB_NAME")
?? throw new InvalidOperationException("DB_NAME environment variable is not set"); ?? throw new InvalidOperationException("DB_NAME environment variable is not set");
string user = Environment.GetEnvironmentVariable("DB_USER") string user =
Environment.GetEnvironmentVariable("DB_USER")
?? throw new InvalidOperationException("DB_USER environment variable is not set"); ?? throw new InvalidOperationException("DB_USER environment variable is not set");
string password = Environment.GetEnvironmentVariable("DB_PASSWORD") string password =
Environment.GetEnvironmentVariable("DB_PASSWORD")
?? throw new InvalidOperationException("DB_PASSWORD environment variable is not set"); ?? throw new InvalidOperationException("DB_PASSWORD environment variable is not set");
string trustServerCertificate = Environment.GetEnvironmentVariable("DB_TRUST_SERVER_CERTIFICATE") string trustServerCertificate =
?? "True"; Environment.GetEnvironmentVariable("DB_TRUST_SERVER_CERTIFICATE") ?? "True";
SqlConnectionStringBuilder builder = new() SqlConnectionStringBuilder builder = new()
{ {
@@ -39,13 +43,12 @@ string BuildConnectionString()
UserID = user, UserID = user,
Password = password, Password = password,
TrustServerCertificate = bool.Parse(trustServerCertificate), TrustServerCertificate = bool.Parse(trustServerCertificate),
Encrypt = true Encrypt = true,
}; };
return builder.ConnectionString; return builder.ConnectionString;
} }
try try
{ {
string connectionString = BuildConnectionString(); string connectionString = BuildConnectionString();
@@ -74,17 +77,14 @@ try
connection = null; connection = null;
} }
if (connection == null) throw new Exception($"Failed to connect to database after {maxRetries} attempts."); if (connection == null)
throw new Exception($"Failed to connect to database after {maxRetries} attempts.");
Console.WriteLine("Starting seeding..."); Console.WriteLine("Starting seeding...");
using (connection) using (connection)
{ {
ISeeder[] seeders = ISeeder[] seeders = [new LocationSeeder(), new UserSeeder()];
[
new LocationSeeder(),
new UserSeeder()
];
foreach (ISeeder seeder in seeders) foreach (ISeeder seeder in seeders)
{ {

View File

@@ -19,10 +19,7 @@ namespace Database.Seed;
internal class UserSeeder : ISeeder internal class UserSeeder : ISeeder
{ {
/// <summary>The first/last name pairs used to generate seed user accounts.</summary> /// <summary>The first/last name pairs used to generate seed user accounts.</summary>
private static readonly IReadOnlyList<( private static readonly IReadOnlyList<(string FirstName, string LastName)> SeedNames =
string FirstName,
string LastName
)> SeedNames =
[ [
("Aarya", "Mathews"), ("Aarya", "Mathews"),
("Aiden", "Wells"), ("Aiden", "Wells"),
@@ -123,7 +120,7 @@ internal class UserSeeder : ISeeder
("Zara", "Wilkinson"), ("Zara", "Wilkinson"),
("Zaria", "Gibson"), ("Zaria", "Gibson"),
("Zion", "Watkins"), ("Zion", "Watkins"),
("Zoie", "Armstrong") ("Zoie", "Armstrong"),
]; ];
/// <summary> /// <summary>
@@ -168,14 +165,9 @@ internal class UserSeeder : ISeeder
DateTime dob = GenerateDateOfBirth(rng); DateTime dob = GenerateDateOfBirth(rng);
// generate a password and hash it // generate a password and hash it
string pwd = generator.Generate( string pwd = generator.Generate(64, 10, 10);
64,
10,
10
);
string hash = GeneratePasswordHash(pwd); string hash = GeneratePasswordHash(pwd);
// register the user (creates account + credential) // register the user (creates account + credential)
Guid id = await RegisterUserAsync( Guid id = await RegisterUserAsync(
connection, connection,
@@ -189,9 +181,9 @@ internal class UserSeeder : ISeeder
createdUsers++; createdUsers++;
createdCredentials++; createdCredentials++;
// add user verification // add user verification
if (await HasUserVerificationAsync(connection, id)) continue; if (await HasUserVerificationAsync(connection, id))
continue;
await AddUserVerificationAsync(connection, id); await AddUserVerificationAsync(connection, id);
createdVerifications++; createdVerifications++;
@@ -227,7 +219,6 @@ internal class UserSeeder : ISeeder
await using SqlCommand command = new("dbo.USP_RegisterUser", connection); await using SqlCommand command = new("dbo.USP_RegisterUser", connection);
command.CommandType = CommandType.StoredProcedure; command.CommandType = CommandType.StoredProcedure;
command.Parameters.Add("@Username", SqlDbType.VarChar, 64).Value = username; command.Parameters.Add("@Username", SqlDbType.VarChar, 64).Value = username;
command.Parameters.Add("@FirstName", SqlDbType.NVarChar, 128).Value = firstName; command.Parameters.Add("@FirstName", SqlDbType.NVarChar, 128).Value = firstName;
command.Parameters.Add("@LastName", SqlDbType.NVarChar, 128).Value = lastName; command.Parameters.Add("@LastName", SqlDbType.NVarChar, 128).Value = lastName;
@@ -237,7 +228,6 @@ internal class UserSeeder : ISeeder
object? result = await command.ExecuteScalarAsync(); object? result = await command.ExecuteScalarAsync();
return (Guid)result!; return (Guid)result!;
} }
@@ -257,7 +247,7 @@ internal class UserSeeder : ISeeder
Salt = salt, Salt = salt,
DegreeOfParallelism = Math.Max(Environment.ProcessorCount, 1), DegreeOfParallelism = Math.Max(Environment.ProcessorCount, 1),
MemorySize = 65536, MemorySize = 65536,
Iterations = 4 Iterations = 4,
}; };
byte[] hash = argon2.GetBytes(32); byte[] hash = argon2.GetBytes(32);
@@ -288,15 +278,9 @@ internal class UserSeeder : ISeeder
/// <param name="connection">An open connection to the target database.</param> /// <param name="connection">An open connection to the target database.</param>
/// <param name="userAccountId">The identifier of the user account to verify.</param> /// <param name="userAccountId">The identifier of the user account to verify.</param>
/// <returns>A task that completes when the verification record has been created.</returns> /// <returns>A task that completes when the verification record has been created.</returns>
private static async Task AddUserVerificationAsync( private static async Task AddUserVerificationAsync(SqlConnection connection, Guid userAccountId)
SqlConnection connection,
Guid userAccountId
)
{ {
await using SqlCommand command = new( await using SqlCommand command = new("dbo.USP_CreateUserVerification", connection);
"dbo.USP_CreateUserVerification",
connection
);
command.CommandType = CommandType.StoredProcedure; command.CommandType = CommandType.StoredProcedure;
command.Parameters.AddWithValue("@UserAccountID_", userAccountId); command.Parameters.AddWithValue("@UserAccountID_", userAccountId);