From b8cd8559160cb2caa77926ce898bafbda0c5ef47 Mon Sep 17 00:00:00 2001 From: Aaron Po Date: Thu, 15 Jan 2026 13:23:41 -0500 Subject: [PATCH] Refactor user entities and repositories, update seeders Standardized property naming in user-related entities to use 'Id' suffix (e.g., UserAccountId). Moved and updated repository interfaces and implementations to the DataAccessLayer.Repositories namespace. Refactored DBSeed seeders to use repository classes and improved structure. Updated .gitignore and project references --- .gitignore | 3 +- BusinessLayer/Services/UserService.cs | 1 + DALTests/UserAccountRepositoryTests.cs | 40 +- DBSeed/DBSeed.csproj | 6 +- DBSeed/ISeeder.cs | 7 +- DBSeed/LocationSeeder.cs | 624 +++++++++--------- DBSeed/UserSeeder.cs | 546 +++++++-------- DataAccessLayer/Entities/UserAccount.cs | 2 +- DataAccessLayer/Entities/UserCredential.cs | 4 +- DataAccessLayer/Entities/UserVerification.cs | 4 +- .../{ => Repositories}/IRepository.cs | 9 +- .../Repositories/IUserAccountRepository.cs | 4 +- .../Repositories/UserAccountRepository.cs | 107 ++- DataAccessLayer/Sql/DatabaseHelper.cs | 50 +- WebAPI/Controllers/UsersController.cs | 12 +- WebAPI/Program.cs | 1 + 16 files changed, 660 insertions(+), 760 deletions(-) rename DataAccessLayer/{ => Repositories}/IRepository.cs (66%) diff --git a/.gitignore b/.gitignore index 69bbe5d..37f801a 100644 --- a/.gitignore +++ b/.gitignore @@ -430,4 +430,5 @@ FodyWeavers.xsd .DS_Store */data_source/other -.fake \ No newline at end of file +.fake +.idea \ No newline at end of file diff --git a/BusinessLayer/Services/UserService.cs b/BusinessLayer/Services/UserService.cs index 1ab5e1a..a923393 100644 --- a/BusinessLayer/Services/UserService.cs +++ b/BusinessLayer/Services/UserService.cs @@ -1,5 +1,6 @@ using DataAccessLayer; using DataAccessLayer.Entities; +using DataAccessLayer.Repositories; namespace BusinessLayer.Services { diff --git a/DALTests/UserAccountRepositoryTests.cs b/DALTests/UserAccountRepositoryTests.cs index d02d34f..34e5229 100644 --- a/DALTests/UserAccountRepositoryTests.cs +++ b/DALTests/UserAccountRepositoryTests.cs @@ -1,20 +1,12 @@ -using System; -using System.Collections.Generic; -using System.Linq; using DataAccessLayer; using DataAccessLayer.Entities; -using Xunit; +using DataAccessLayer.Repositories; namespace DALTests { public class UserAccountRepositoryTests { - private readonly IUserAccountRepository _repository; - - public UserAccountRepositoryTests() - { - _repository = new UserAccountRepository(); - } + private readonly IUserAccountRepository _repository = new UserAccountRepository(); [Fact] public void Add_ShouldInsertUserAccount() @@ -22,7 +14,7 @@ namespace DALTests // Arrange var userAccount = new UserAccount { - UserAccountID = Guid.NewGuid(), + UserAccountId = Guid.NewGuid(), Username = "testuser", FirstName = "Test", LastName = "User", @@ -33,7 +25,7 @@ namespace DALTests // Act _repository.Add(userAccount); - var retrievedUser = _repository.GetById(userAccount.UserAccountID); + var retrievedUser = _repository.GetById(userAccount.UserAccountId); // Assert Assert.NotNull(retrievedUser); @@ -47,7 +39,7 @@ namespace DALTests var userId = Guid.NewGuid(); var userAccount = new UserAccount { - UserAccountID = userId, + UserAccountId = userId, Username = "existinguser", FirstName = "Existing", LastName = "User", @@ -62,7 +54,7 @@ namespace DALTests // Assert Assert.NotNull(retrievedUser); - Assert.Equal(userId, retrievedUser.UserAccountID); + Assert.Equal(userId, retrievedUser.UserAccountId); } [Fact] @@ -71,7 +63,7 @@ namespace DALTests // Arrange var userAccount = new UserAccount { - UserAccountID = Guid.NewGuid(), + UserAccountId = Guid.NewGuid(), Username = "updatableuser", FirstName = "Updatable", LastName = "User", @@ -84,7 +76,7 @@ namespace DALTests // Act userAccount.FirstName = "Updated"; _repository.Update(userAccount); - var updatedUser = _repository.GetById(userAccount.UserAccountID); + var updatedUser = _repository.GetById(userAccount.UserAccountId); // Assert Assert.NotNull(updatedUser); @@ -97,7 +89,7 @@ namespace DALTests // Arrange var userAccount = new UserAccount { - UserAccountID = Guid.NewGuid(), + UserAccountId = Guid.NewGuid(), Username = "deletableuser", FirstName = "Deletable", LastName = "User", @@ -108,8 +100,8 @@ namespace DALTests _repository.Add(userAccount); // Act - _repository.Delete(userAccount.UserAccountID); - var deletedUser = _repository.GetById(userAccount.UserAccountID); + _repository.Delete(userAccount.UserAccountId); + var deletedUser = _repository.GetById(userAccount.UserAccountId); // Assert Assert.Null(deletedUser); @@ -121,7 +113,7 @@ namespace DALTests // Arrange var user1 = new UserAccount { - UserAccountID = Guid.NewGuid(), + UserAccountId = Guid.NewGuid(), Username = "user1", FirstName = "User", LastName = "One", @@ -131,7 +123,7 @@ namespace DALTests }; var user2 = new UserAccount { - UserAccountID = Guid.NewGuid(), + UserAccountId = Guid.NewGuid(), Username = "user2", FirstName = "User", LastName = "Two", @@ -158,7 +150,7 @@ namespace DALTests { new UserAccount { - UserAccountID = Guid.NewGuid(), + UserAccountId = Guid.NewGuid(), Username = $"pageuser_{Guid.NewGuid():N}", FirstName = "Page", LastName = "User", @@ -168,7 +160,7 @@ namespace DALTests }, new UserAccount { - UserAccountID = Guid.NewGuid(), + UserAccountId = Guid.NewGuid(), Username = $"pageuser_{Guid.NewGuid():N}", FirstName = "Page", LastName = "User", @@ -178,7 +170,7 @@ namespace DALTests }, new UserAccount { - UserAccountID = Guid.NewGuid(), + UserAccountId = Guid.NewGuid(), Username = $"pageuser_{Guid.NewGuid():N}", FirstName = "Page", LastName = "User", diff --git a/DBSeed/DBSeed.csproj b/DBSeed/DBSeed.csproj index 958ec61..54b70c3 100644 --- a/DBSeed/DBSeed.csproj +++ b/DBSeed/DBSeed.csproj @@ -12,6 +12,10 @@ Include="Konscious.Security.Cryptography.Argon2" Version="1.3.1" /> - + + + + + diff --git a/DBSeed/ISeeder.cs b/DBSeed/ISeeder.cs index 4624827..1ecf897 100644 --- a/DBSeed/ISeeder.cs +++ b/DBSeed/ISeeder.cs @@ -1,6 +1,9 @@ using Microsoft.Data.SqlClient; -interface ISeeder +namespace DBSeed { - Task SeedAsync(SqlConnection connection); + internal interface ISeeder + { + Task SeedAsync(SqlConnection connection); + } } \ No newline at end of file diff --git a/DBSeed/LocationSeeder.cs b/DBSeed/LocationSeeder.cs index 983921b..52d3d78 100644 --- a/DBSeed/LocationSeeder.cs +++ b/DBSeed/LocationSeeder.cs @@ -1,330 +1,328 @@ using System.Data; using Microsoft.Data.SqlClient; -namespace DBSeed; - -class LocationSeeder : ISeeder +namespace DBSeed { - private static readonly IReadOnlyList<( - string CountryName, - string CountryCode - )> Countries = - [ - ("Canada", "CA"), - ("Mexico", "MX"), - ("United States", "US"), - ]; - private static readonly IReadOnlyList<( - string StateProvinceName, - string StateProvinceCode, - string CountryCode - )> States = - [ - ("Alabama", "US-AL", "US"), - ("Alaska", "US-AK", "US"), - ("Arizona", "US-AZ", "US"), - ("Arkansas", "US-AR", "US"), - ("California", "US-CA", "US"), - ("Colorado", "US-CO", "US"), - ("Connecticut", "US-CT", "US"), - ("Delaware", "US-DE", "US"), - ("Florida", "US-FL", "US"), - ("Georgia", "US-GA", "US"), - ("Hawaii", "US-HI", "US"), - ("Idaho", "US-ID", "US"), - ("Illinois", "US-IL", "US"), - ("Indiana", "US-IN", "US"), - ("Iowa", "US-IA", "US"), - ("Kansas", "US-KS", "US"), - ("Kentucky", "US-KY", "US"), - ("Louisiana", "US-LA", "US"), - ("Maine", "US-ME", "US"), - ("Maryland", "US-MD", "US"), - ("Massachusetts", "US-MA", "US"), - ("Michigan", "US-MI", "US"), - ("Minnesota", "US-MN", "US"), - ("Mississippi", "US-MS", "US"), - ("Missouri", "US-MO", "US"), - ("Montana", "US-MT", "US"), - ("Nebraska", "US-NE", "US"), - ("Nevada", "US-NV", "US"), - ("New Hampshire", "US-NH", "US"), - ("New Jersey", "US-NJ", "US"), - ("New Mexico", "US-NM", "US"), - ("New York", "US-NY", "US"), - ("North Carolina", "US-NC", "US"), - ("North Dakota", "US-ND", "US"), - ("Ohio", "US-OH", "US"), - ("Oklahoma", "US-OK", "US"), - ("Oregon", "US-OR", "US"), - ("Pennsylvania", "US-PA", "US"), - ("Rhode Island", "US-RI", "US"), - ("South Carolina", "US-SC", "US"), - ("South Dakota", "US-SD", "US"), - ("Tennessee", "US-TN", "US"), - ("Texas", "US-TX", "US"), - ("Utah", "US-UT", "US"), - ("Vermont", "US-VT", "US"), - ("Virginia", "US-VA", "US"), - ("Washington", "US-WA", "US"), - ("West Virginia", "US-WV", "US"), - ("Wisconsin", "US-WI", "US"), - ("Wyoming", "US-WY", "US"), - ("District of Columbia", "US-DC", "US"), - ("Puerto Rico", "US-PR", "US"), - ("U.S. Virgin Islands", "US-VI", "US"), - ("Guam", "US-GU", "US"), - ("Northern Mariana Islands", "US-MP", "US"), - ("American Samoa", "US-AS", "US"), - ("Ontario", "CA-ON", "CA"), - ("Québec", "CA-QC", "CA"), - ("Nova Scotia", "CA-NS", "CA"), - ("New Brunswick", "CA-NB", "CA"), - ("Manitoba", "CA-MB", "CA"), - ("British Columbia", "CA-BC", "CA"), - ("Prince Edward Island", "CA-PE", "CA"), - ("Saskatchewan", "CA-SK", "CA"), - ("Alberta", "CA-AB", "CA"), - ("Newfoundland and Labrador", "CA-NL", "CA"), - ("Northwest Territories", "CA-NT", "CA"), - ("Yukon", "CA-YT", "CA"), - ("Nunavut", "CA-NU", "CA"), - ("Aguascalientes", "MX-AGU", "MX"), - ("Baja California", "MX-BCN", "MX"), - ("Baja California Sur", "MX-BCS", "MX"), - ("Campeche", "MX-CAM", "MX"), - ("Chiapas", "MX-CHP", "MX"), - ("Chihuahua", "MX-CHH", "MX"), - ("Coahuila de Zaragoza", "MX-COA", "MX"), - ("Colima", "MX-COL", "MX"), - ("Durango", "MX-DUR", "MX"), - ("Guanajuato", "MX-GUA", "MX"), - ("Guerrero", "MX-GRO", "MX"), - ("Hidalgo", "MX-HID", "MX"), - ("Jalisco", "MX-JAL", "MX"), - ("México State", "MX-MEX", "MX"), - ("Michoacán de Ocampo", "MX-MIC", "MX"), - ("Morelos", "MX-MOR", "MX"), - ("Nayarit", "MX-NAY", "MX"), - ("Nuevo León", "MX-NLE", "MX"), - ("Oaxaca", "MX-OAX", "MX"), - ("Puebla", "MX-PUE", "MX"), - ("Querétaro", "MX-QUE", "MX"), - ("Quintana Roo", "MX-ROO", "MX"), - ("San Luis Potosí", "MX-SLP", "MX"), - ("Sinaloa", "MX-SIN", "MX"), - ("Sonora", "MX-SON", "MX"), - ("Tabasco", "MX-TAB", "MX"), - ("Tamaulipas", "MX-TAM", "MX"), - ("Tlaxcala", "MX-TLA", "MX"), - ("Veracruz de Ignacio de la Llave", "MX-VER", "MX"), - ("Yucatán", "MX-YUC", "MX"), - ("Zacatecas", "MX-ZAC", "MX"), - ("Ciudad de México", "MX-CMX", "MX"), - ]; - - private static readonly IReadOnlyList<( - string StateProvinceCode, - string CityName - )> Cities = - [ - ("US-CA", "Los Angeles"), - ("US-CA", "San Diego"), - ("US-CA", "San Francisco"), - ("US-CA", "Sacramento"), - ("US-TX", "Houston"), - ("US-TX", "Dallas"), - ("US-TX", "Austin"), - ("US-TX", "San Antonio"), - ("US-FL", "Miami"), - ("US-FL", "Orlando"), - ("US-FL", "Tampa"), - ("US-NY", "New York"), - ("US-NY", "Buffalo"), - ("US-NY", "Rochester"), - ("US-IL", "Chicago"), - ("US-IL", "Springfield"), - ("US-PA", "Philadelphia"), - ("US-PA", "Pittsburgh"), - ("US-AZ", "Phoenix"), - ("US-AZ", "Tucson"), - ("US-CO", "Denver"), - ("US-CO", "Colorado Springs"), - ("US-MA", "Boston"), - ("US-MA", "Worcester"), - ("US-WA", "Seattle"), - ("US-WA", "Spokane"), - ("US-GA", "Atlanta"), - ("US-GA", "Savannah"), - ("US-NV", "Las Vegas"), - ("US-NV", "Reno"), - ("US-MI", "Detroit"), - ("US-MI", "Grand Rapids"), - ("US-MN", "Minneapolis"), - ("US-MN", "Saint Paul"), - ("US-OH", "Columbus"), - ("US-OH", "Cleveland"), - ("US-OR", "Portland"), - ("US-OR", "Salem"), - ("US-TN", "Nashville"), - ("US-TN", "Memphis"), - ("US-VA", "Richmond"), - ("US-VA", "Virginia Beach"), - ("US-MD", "Baltimore"), - ("US-MD", "Frederick"), - ("US-DC", "Washington"), - ("US-UT", "Salt Lake City"), - ("US-UT", "Provo"), - ("US-LA", "New Orleans"), - ("US-LA", "Baton Rouge"), - ("US-KY", "Louisville"), - ("US-KY", "Lexington"), - ("US-IA", "Des Moines"), - ("US-IA", "Cedar Rapids"), - ("US-OK", "Oklahoma City"), - ("US-OK", "Tulsa"), - ("US-NE", "Omaha"), - ("US-NE", "Lincoln"), - ("US-MO", "Kansas City"), - ("US-MO", "St. Louis"), - ("US-NC", "Charlotte"), - ("US-NC", "Raleigh"), - ("US-SC", "Columbia"), - ("US-SC", "Charleston"), - ("US-WI", "Milwaukee"), - ("US-WI", "Madison"), - ("US-MN", "Duluth"), - ("US-AK", "Anchorage"), - ("US-HI", "Honolulu"), - ("CA-ON", "Toronto"), - ("CA-ON", "Ottawa"), - ("CA-QC", "Montréal"), - ("CA-QC", "Québec City"), - ("CA-BC", "Vancouver"), - ("CA-BC", "Victoria"), - ("CA-AB", "Calgary"), - ("CA-AB", "Edmonton"), - ("CA-MB", "Winnipeg"), - ("CA-NS", "Halifax"), - ("CA-SK", "Saskatoon"), - ("CA-SK", "Regina"), - ("CA-NB", "Moncton"), - ("CA-NB", "Saint John"), - ("CA-PE", "Charlottetown"), - ("CA-NL", "St. John's"), - ("CA-ON", "Hamilton"), - ("CA-ON", "London"), - ("CA-QC", "Gatineau"), - ("CA-QC", "Laval"), - ("CA-BC", "Kelowna"), - ("CA-AB", "Red Deer"), - ("CA-MB", "Brandon"), - ("MX-CMX", "Ciudad de México"), - ("MX-JAL", "Guadalajara"), - ("MX-NLE", "Monterrey"), - ("MX-PUE", "Puebla"), - ("MX-ROO", "Cancún"), - ("MX-GUA", "Guanajuato"), - ("MX-MIC", "Morelia"), - ("MX-BCN", "Tijuana"), - ("MX-JAL", "Zapopan"), - ("MX-NLE", "San Nicolás"), - ("MX-CAM", "Campeche"), - ("MX-TAB", "Villahermosa"), - ("MX-VER", "Veracruz"), - ("MX-OAX", "Oaxaca"), - ("MX-SLP", "San Luis Potosí"), - ("MX-CHH", "Chihuahua"), - ("MX-AGU", "Aguascalientes"), - ("MX-MEX", "Toluca"), - ("MX-COA", "Saltillo"), - ("MX-BCS", "La Paz"), - ("MX-NAY", "Tepic"), - ("MX-ZAC", "Zacatecas"), - ]; - - public async Task SeedAsync(SqlConnection connection) + internal class LocationSeeder : ISeeder { - foreach (var (countryName, countryCode) in Countries) + private static readonly IReadOnlyList<( + string CountryName, + string CountryCode + )> Countries = + [ + ("Canada", "CA"), + ("Mexico", "MX"), + ("United States", "US"), + ]; + + private static IReadOnlyList<(string StateProvinceName, string StateProvinceCode, string CountryCode)> States { - await CreateCountryAsync(connection, countryName, countryCode); + get; + } = + [ + ("Alabama", "US-AL", "US"), + ("Alaska", "US-AK", "US"), + ("Arizona", "US-AZ", "US"), + ("Arkansas", "US-AR", "US"), + ("California", "US-CA", "US"), + ("Colorado", "US-CO", "US"), + ("Connecticut", "US-CT", "US"), + ("Delaware", "US-DE", "US"), + ("Florida", "US-FL", "US"), + ("Georgia", "US-GA", "US"), + ("Hawaii", "US-HI", "US"), + ("Idaho", "US-ID", "US"), + ("Illinois", "US-IL", "US"), + ("Indiana", "US-IN", "US"), + ("Iowa", "US-IA", "US"), + ("Kansas", "US-KS", "US"), + ("Kentucky", "US-KY", "US"), + ("Louisiana", "US-LA", "US"), + ("Maine", "US-ME", "US"), + ("Maryland", "US-MD", "US"), + ("Massachusetts", "US-MA", "US"), + ("Michigan", "US-MI", "US"), + ("Minnesota", "US-MN", "US"), + ("Mississippi", "US-MS", "US"), + ("Missouri", "US-MO", "US"), + ("Montana", "US-MT", "US"), + ("Nebraska", "US-NE", "US"), + ("Nevada", "US-NV", "US"), + ("New Hampshire", "US-NH", "US"), + ("New Jersey", "US-NJ", "US"), + ("New Mexico", "US-NM", "US"), + ("New York", "US-NY", "US"), + ("North Carolina", "US-NC", "US"), + ("North Dakota", "US-ND", "US"), + ("Ohio", "US-OH", "US"), + ("Oklahoma", "US-OK", "US"), + ("Oregon", "US-OR", "US"), + ("Pennsylvania", "US-PA", "US"), + ("Rhode Island", "US-RI", "US"), + ("South Carolina", "US-SC", "US"), + ("South Dakota", "US-SD", "US"), + ("Tennessee", "US-TN", "US"), + ("Texas", "US-TX", "US"), + ("Utah", "US-UT", "US"), + ("Vermont", "US-VT", "US"), + ("Virginia", "US-VA", "US"), + ("Washington", "US-WA", "US"), + ("West Virginia", "US-WV", "US"), + ("Wisconsin", "US-WI", "US"), + ("Wyoming", "US-WY", "US"), + ("District of Columbia", "US-DC", "US"), + ("Puerto Rico", "US-PR", "US"), + ("U.S. Virgin Islands", "US-VI", "US"), + ("Guam", "US-GU", "US"), + ("Northern Mariana Islands", "US-MP", "US"), + ("American Samoa", "US-AS", "US"), + ("Ontario", "CA-ON", "CA"), + ("Québec", "CA-QC", "CA"), + ("Nova Scotia", "CA-NS", "CA"), + ("New Brunswick", "CA-NB", "CA"), + ("Manitoba", "CA-MB", "CA"), + ("British Columbia", "CA-BC", "CA"), + ("Prince Edward Island", "CA-PE", "CA"), + ("Saskatchewan", "CA-SK", "CA"), + ("Alberta", "CA-AB", "CA"), + ("Newfoundland and Labrador", "CA-NL", "CA"), + ("Northwest Territories", "CA-NT", "CA"), + ("Yukon", "CA-YT", "CA"), + ("Nunavut", "CA-NU", "CA"), + ("Aguascalientes", "MX-AGU", "MX"), + ("Baja California", "MX-BCN", "MX"), + ("Baja California Sur", "MX-BCS", "MX"), + ("Campeche", "MX-CAM", "MX"), + ("Chiapas", "MX-CHP", "MX"), + ("Chihuahua", "MX-CHH", "MX"), + ("Coahuila de Zaragoza", "MX-COA", "MX"), + ("Colima", "MX-COL", "MX"), + ("Durango", "MX-DUR", "MX"), + ("Guanajuato", "MX-GUA", "MX"), + ("Guerrero", "MX-GRO", "MX"), + ("Hidalgo", "MX-HID", "MX"), + ("Jalisco", "MX-JAL", "MX"), + ("México State", "MX-MEX", "MX"), + ("Michoacán de Ocampo", "MX-MIC", "MX"), + ("Morelos", "MX-MOR", "MX"), + ("Nayarit", "MX-NAY", "MX"), + ("Nuevo León", "MX-NLE", "MX"), + ("Oaxaca", "MX-OAX", "MX"), + ("Puebla", "MX-PUE", "MX"), + ("Querétaro", "MX-QUE", "MX"), + ("Quintana Roo", "MX-ROO", "MX"), + ("San Luis Potosí", "MX-SLP", "MX"), + ("Sinaloa", "MX-SIN", "MX"), + ("Sonora", "MX-SON", "MX"), + ("Tabasco", "MX-TAB", "MX"), + ("Tamaulipas", "MX-TAM", "MX"), + ("Tlaxcala", "MX-TLA", "MX"), + ("Veracruz de Ignacio de la Llave", "MX-VER", "MX"), + ("Yucatán", "MX-YUC", "MX"), + ("Zacatecas", "MX-ZAC", "MX"), + ("Ciudad de México", "MX-CMX", "MX"), + ]; + + private static IReadOnlyList<(string StateProvinceCode, string CityName)> Cities { get; } = + [ + ("US-CA", "Los Angeles"), + ("US-CA", "San Diego"), + ("US-CA", "San Francisco"), + ("US-CA", "Sacramento"), + ("US-TX", "Houston"), + ("US-TX", "Dallas"), + ("US-TX", "Austin"), + ("US-TX", "San Antonio"), + ("US-FL", "Miami"), + ("US-FL", "Orlando"), + ("US-FL", "Tampa"), + ("US-NY", "New York"), + ("US-NY", "Buffalo"), + ("US-NY", "Rochester"), + ("US-IL", "Chicago"), + ("US-IL", "Springfield"), + ("US-PA", "Philadelphia"), + ("US-PA", "Pittsburgh"), + ("US-AZ", "Phoenix"), + ("US-AZ", "Tucson"), + ("US-CO", "Denver"), + ("US-CO", "Colorado Springs"), + ("US-MA", "Boston"), + ("US-MA", "Worcester"), + ("US-WA", "Seattle"), + ("US-WA", "Spokane"), + ("US-GA", "Atlanta"), + ("US-GA", "Savannah"), + ("US-NV", "Las Vegas"), + ("US-NV", "Reno"), + ("US-MI", "Detroit"), + ("US-MI", "Grand Rapids"), + ("US-MN", "Minneapolis"), + ("US-MN", "Saint Paul"), + ("US-OH", "Columbus"), + ("US-OH", "Cleveland"), + ("US-OR", "Portland"), + ("US-OR", "Salem"), + ("US-TN", "Nashville"), + ("US-TN", "Memphis"), + ("US-VA", "Richmond"), + ("US-VA", "Virginia Beach"), + ("US-MD", "Baltimore"), + ("US-MD", "Frederick"), + ("US-DC", "Washington"), + ("US-UT", "Salt Lake City"), + ("US-UT", "Provo"), + ("US-LA", "New Orleans"), + ("US-LA", "Baton Rouge"), + ("US-KY", "Louisville"), + ("US-KY", "Lexington"), + ("US-IA", "Des Moines"), + ("US-IA", "Cedar Rapids"), + ("US-OK", "Oklahoma City"), + ("US-OK", "Tulsa"), + ("US-NE", "Omaha"), + ("US-NE", "Lincoln"), + ("US-MO", "Kansas City"), + ("US-MO", "St. Louis"), + ("US-NC", "Charlotte"), + ("US-NC", "Raleigh"), + ("US-SC", "Columbia"), + ("US-SC", "Charleston"), + ("US-WI", "Milwaukee"), + ("US-WI", "Madison"), + ("US-MN", "Duluth"), + ("US-AK", "Anchorage"), + ("US-HI", "Honolulu"), + ("CA-ON", "Toronto"), + ("CA-ON", "Ottawa"), + ("CA-QC", "Montréal"), + ("CA-QC", "Québec City"), + ("CA-BC", "Vancouver"), + ("CA-BC", "Victoria"), + ("CA-AB", "Calgary"), + ("CA-AB", "Edmonton"), + ("CA-MB", "Winnipeg"), + ("CA-NS", "Halifax"), + ("CA-SK", "Saskatoon"), + ("CA-SK", "Regina"), + ("CA-NB", "Moncton"), + ("CA-NB", "Saint John"), + ("CA-PE", "Charlottetown"), + ("CA-NL", "St. John's"), + ("CA-ON", "Hamilton"), + ("CA-ON", "London"), + ("CA-QC", "Gatineau"), + ("CA-QC", "Laval"), + ("CA-BC", "Kelowna"), + ("CA-AB", "Red Deer"), + ("CA-MB", "Brandon"), + ("MX-CMX", "Ciudad de México"), + ("MX-JAL", "Guadalajara"), + ("MX-NLE", "Monterrey"), + ("MX-PUE", "Puebla"), + ("MX-ROO", "Cancún"), + ("MX-GUA", "Guanajuato"), + ("MX-MIC", "Morelia"), + ("MX-BCN", "Tijuana"), + ("MX-JAL", "Zapopan"), + ("MX-NLE", "San Nicolás"), + ("MX-CAM", "Campeche"), + ("MX-TAB", "Villahermosa"), + ("MX-VER", "Veracruz"), + ("MX-OAX", "Oaxaca"), + ("MX-SLP", "San Luis Potosí"), + ("MX-CHH", "Chihuahua"), + ("MX-AGU", "Aguascalientes"), + ("MX-MEX", "Toluca"), + ("MX-COA", "Saltillo"), + ("MX-BCS", "La Paz"), + ("MX-NAY", "Tepic"), + ("MX-ZAC", "Zacatecas"), + ]; + + public async Task SeedAsync(SqlConnection connection) + { + foreach (var (countryName, countryCode) in Countries) + { + await CreateCountryAsync(connection, countryName, countryCode); + } + + foreach ( + var (stateProvinceName, stateProvinceCode, countryCode) in States + ) + { + await CreateStateProvinceAsync( + connection, + stateProvinceName, + stateProvinceCode, + countryCode + ); + } + + foreach (var (stateProvinceCode, cityName) in Cities) + { + await CreateCityAsync(connection, cityName, stateProvinceCode); + } } - foreach ( - var (stateProvinceName, stateProvinceCode, countryCode) in States + private static async Task CreateCountryAsync( + SqlConnection connection, + string countryName, + string countryCode ) { - await CreateStateProvinceAsync( - connection, - stateProvinceName, - stateProvinceCode, - countryCode + await using var command = new SqlCommand( + "dbo.USP_CreateCountry", + connection ); + command.CommandType = CommandType.StoredProcedure; + command.Parameters.AddWithValue("@CountryName", countryName); + command.Parameters.AddWithValue("@ISO3616_1", countryCode); + + await command.ExecuteNonQueryAsync(); } - foreach (var (stateProvinceCode, cityName) in Cities) + private static async Task CreateStateProvinceAsync( + SqlConnection connection, + string stateProvinceName, + string stateProvinceCode, + string countryCode + ) { - await CreateCityAsync(connection, cityName, stateProvinceCode); + await using var command = new SqlCommand( + "dbo.USP_CreateStateProvince", + connection + ); + command.CommandType = CommandType.StoredProcedure; + command.Parameters.AddWithValue( + "@StateProvinceName", + stateProvinceName + ); + command.Parameters.AddWithValue("@ISO3616_2", stateProvinceCode); + command.Parameters.AddWithValue("@CountryCode", countryCode); + + await command.ExecuteNonQueryAsync(); + } + + private static async Task CreateCityAsync( + SqlConnection connection, + string cityName, + string stateProvinceCode + ) + { + await using var command = new SqlCommand( + "dbo.USP_CreateCity", + connection + ); + command.CommandType = CommandType.StoredProcedure; + command.Parameters.AddWithValue("@CityName", cityName); + command.Parameters.AddWithValue( + "@StateProvinceCode", + stateProvinceCode + ); + + await command.ExecuteNonQueryAsync(); } } - - private static async Task CreateCountryAsync( - SqlConnection connection, - string countryName, - string countryCode - ) - { - await using var command = new SqlCommand( - "dbo.USP_CreateCountry", - connection - ); - command.CommandType = CommandType.StoredProcedure; - command.Parameters.AddWithValue("@CountryName", countryName); - command.Parameters.AddWithValue("@ISO3616_1", countryCode); - - await command.ExecuteNonQueryAsync(); - } - - private static async Task CreateStateProvinceAsync( - SqlConnection connection, - string stateProvinceName, - string stateProvinceCode, - string countryCode - ) - { - await using var command = new SqlCommand( - "dbo.USP_CreateStateProvince", - connection - ); - command.CommandType = CommandType.StoredProcedure; - command.Parameters.AddWithValue( - "@StateProvinceName", - stateProvinceName - ); - command.Parameters.AddWithValue("@ISO3616_2", stateProvinceCode); - command.Parameters.AddWithValue("@CountryCode", countryCode); - - await command.ExecuteNonQueryAsync(); - } - - private static async Task CreateCityAsync( - SqlConnection connection, - string cityName, - string stateProvinceCode - ) - { - await using var command = new SqlCommand( - "dbo.USP_CreateCity", - connection - ); - command.CommandType = CommandType.StoredProcedure; - command.Parameters.AddWithValue("@CityName", cityName); - command.Parameters.AddWithValue( - "@StateProvinceCode", - stateProvinceCode - ); - - await command.ExecuteNonQueryAsync(); - } -} +} \ No newline at end of file diff --git a/DBSeed/UserSeeder.cs b/DBSeed/UserSeeder.cs index 2918bff..3fd7f64 100644 --- a/DBSeed/UserSeeder.cs +++ b/DBSeed/UserSeeder.cs @@ -1,336 +1,258 @@ using System.Data; using System.Security.Cryptography; using System.Text; +using DataAccessLayer.Entities; +using DataAccessLayer.Repositories; using idunno.Password; using Konscious.Security.Cryptography; using Microsoft.Data.SqlClient; -namespace DBSeed; - -class UserSeeder : ISeeder +namespace DBSeed { - private static readonly IReadOnlyList<( - string FirstName, - string LastName - )> SeedNames = - [ - ("Aarya", "Mathews"), - ("Aiden", "Wells"), - ("Aleena", "Gonzalez"), - ("Alessandra", "Nelson"), - ("Amari", "Tucker"), - ("Ameer", "Huff"), - ("Amirah", "Hicks"), - ("Analia", "Dominguez"), - ("Anne", "Jenkins"), - ("Apollo", "Davis"), - ("Arianna", "White"), - ("Aubree", "Moore"), - ("Aubrielle", "Raymond"), - ("Aydin", "Odom"), - ("Bowen", "Casey"), - ("Brock", "Huber"), - ("Caiden", "Strong"), - ("Cecilia", "Rosales"), - ("Celeste", "Barber"), - ("Chance", "Small"), - ("Clara", "Roberts"), - ("Collins", "Brandt"), - ("Damir", "Wallace"), - ("Declan", "Crawford"), - ("Dennis", "Decker"), - ("Dylan", "Lang"), - ("Eliza", "Kane"), - ("Elle", "Poole"), - ("Elliott", "Miles"), - ("Emelia", "Lucas"), - ("Emilia", "Simpson"), - ("Emmett", "Lugo"), - ("Ethan", "Stephens"), - ("Etta", "Woods"), - ("Gael", "Moran"), - ("Grant", "Benson"), - ("Gwen", "James"), - ("Huxley", "Chen"), - ("Isabella", "Fisher"), - ("Ivan", "Mathis"), - ("Jamir", "McMillan"), - ("Jaxson", "Shields"), - ("Jimmy", "Richmond"), - ("Josiah", "Flores"), - ("Kaden", "Enriquez"), - ("Kai", "Lawson"), - ("Karsyn", "Adkins"), - ("Karsyn", "Proctor"), - ("Kayden", "Henson"), - ("Kaylie", "Spears"), - ("Kinslee", "Jones"), - ("Kora", "Guerra"), - ("Lane", "Skinner"), - ("Laylani", "Christian"), - ("Ledger", "Carroll"), - ("Leilany", "Small"), - ("Leland", "McCall"), - ("Leonard", "Calhoun"), - ("Levi", "Ochoa"), - ("Lillie", "Vang"), - ("Lola", "Sheppard"), - ("Luciana", "Poole"), - ("Maddox", "Hughes"), - ("Mara", "Blackwell"), - ("Marcellus", "Bartlett"), - ("Margo", "Koch"), - ("Maurice", "Gibson"), - ("Maxton", "Dodson"), - ("Mia", "Parrish"), - ("Millie", "Fuentes"), - ("Nellie", "Villanueva"), - ("Nicolas", "Mata"), - ("Nicolas", "Miller"), - ("Oakleigh", "Foster"), - ("Octavia", "Pierce"), - ("Paisley", "Allison"), - ("Quincy", "Andersen"), - ("Quincy", "Frazier"), - ("Raiden", "Roberts"), - ("Raquel", "Lara"), - ("Rudy", "McIntosh"), - ("Salvador", "Stein"), - ("Samantha", "Dickson"), - ("Solomon", "Richards"), - ("Sylvia", "Hanna"), - ("Talia", "Trujillo"), - ("Thalia", "Farrell"), - ("Trent", "Mayo"), - ("Trinity", "Cummings"), - ("Ty", "Perry"), - ("Tyler", "Romero"), - ("Valeria", "Pierce"), - ("Vance", "Neal"), - ("Whitney", "Bell"), - ("Wilder", "Graves"), - ("William", "Logan"), - ("Zara", "Wilkinson"), - ("Zaria", "Gibson"), - ("Zion", "Watkins"), - ("Zoie", "Armstrong"), - ]; - public async Task SeedAsync(SqlConnection connection) + internal class UserSeeder : ISeeder { - var generator = new PasswordGenerator(); - var random = new Random(); - int createdUsers = 0; - int createdCredentials = 0; - int createdVerifications = 0; + private UserAccountRepository _userAccountRepository = new UserAccountRepository(); + + + private static readonly IReadOnlyList<( + string FirstName, + string LastName + )> SeedNames = + [ + ("Aarya", "Mathews"), + ("Aiden", "Wells"), + ("Aleena", "Gonzalez"), + ("Alessandra", "Nelson"), + ("Amari", "Tucker"), + ("Ameer", "Huff"), + ("Amirah", "Hicks"), + ("Analia", "Dominguez"), + ("Anne", "Jenkins"), + ("Apollo", "Davis"), + ("Arianna", "White"), + ("Aubree", "Moore"), + ("Aubrielle", "Raymond"), + ("Aydin", "Odom"), + ("Bowen", "Casey"), + ("Brock", "Huber"), + ("Caiden", "Strong"), + ("Cecilia", "Rosales"), + ("Celeste", "Barber"), + ("Chance", "Small"), + ("Clara", "Roberts"), + ("Collins", "Brandt"), + ("Damir", "Wallace"), + ("Declan", "Crawford"), + ("Dennis", "Decker"), + ("Dylan", "Lang"), + ("Eliza", "Kane"), + ("Elle", "Poole"), + ("Elliott", "Miles"), + ("Emelia", "Lucas"), + ("Emilia", "Simpson"), + ("Emmett", "Lugo"), + ("Ethan", "Stephens"), + ("Etta", "Woods"), + ("Gael", "Moran"), + ("Grant", "Benson"), + ("Gwen", "James"), + ("Huxley", "Chen"), + ("Isabella", "Fisher"), + ("Ivan", "Mathis"), + ("Jamir", "McMillan"), + ("Jaxson", "Shields"), + ("Jimmy", "Richmond"), + ("Josiah", "Flores"), + ("Kaden", "Enriquez"), + ("Kai", "Lawson"), + ("Karsyn", "Adkins"), + ("Karsyn", "Proctor"), + ("Kayden", "Henson"), + ("Kaylie", "Spears"), + ("Kinslee", "Jones"), + ("Kora", "Guerra"), + ("Lane", "Skinner"), + ("Laylani", "Christian"), + ("Ledger", "Carroll"), + ("Leilany", "Small"), + ("Leland", "McCall"), + ("Leonard", "Calhoun"), + ("Levi", "Ochoa"), + ("Lillie", "Vang"), + ("Lola", "Sheppard"), + ("Luciana", "Poole"), + ("Maddox", "Hughes"), + ("Mara", "Blackwell"), + ("Marcellus", "Bartlett"), + ("Margo", "Koch"), + ("Maurice", "Gibson"), + ("Maxton", "Dodson"), + ("Mia", "Parrish"), + ("Millie", "Fuentes"), + ("Nellie", "Villanueva"), + ("Nicolas", "Mata"), + ("Nicolas", "Miller"), + ("Oakleigh", "Foster"), + ("Octavia", "Pierce"), + ("Paisley", "Allison"), + ("Quincy", "Andersen"), + ("Quincy", "Frazier"), + ("Raiden", "Roberts"), + ("Raquel", "Lara"), + ("Rudy", "McIntosh"), + ("Salvador", "Stein"), + ("Samantha", "Dickson"), + ("Solomon", "Richards"), + ("Sylvia", "Hanna"), + ("Talia", "Trujillo"), + ("Thalia", "Farrell"), + ("Trent", "Mayo"), + ("Trinity", "Cummings"), + ("Ty", "Perry"), + ("Tyler", "Romero"), + ("Valeria", "Pierce"), + ("Vance", "Neal"), + ("Whitney", "Bell"), + ("Wilder", "Graves"), + ("William", "Logan"), + ("Zara", "Wilkinson"), + ("Zaria", "Gibson"), + ("Zion", "Watkins"), + ("Zoie", "Armstrong"), + ]; - foreach (var (firstName, lastName) in SeedNames) + public async Task SeedAsync(SqlConnection connection) { - string username = BuildUsername(firstName, lastName); - string email = BuildEmail(firstName, lastName); - Guid? existingId = - await GetUserAccountIdByUsernameAsync(connection, username) - ?? await GetUserAccountIdByEmailAsync(connection, email); + var generator = new PasswordGenerator(); + var rng = new Random(); + int createdUsers = 0; + int createdCredentials = 0; + int createdVerifications = 0; - Guid userAccountId; - if (existingId.HasValue) + foreach (var (firstName, lastName) in SeedNames) { - userAccountId = existingId.Value; - } - else - { - userAccountId = Guid.NewGuid(); - DateTime dateOfBirth = GenerateDateOfBirth(random); - await CreateUserAccountAsync( - connection, - userAccountId, - username, - firstName, - lastName, - email, - dateOfBirth - ); - createdUsers++; - } + // create the user in the database + var ua = new UserAccount + { + FirstName = firstName, + LastName = lastName, + Email = $"{firstName}.{lastName}@thebiergarten.app", + Username = $"{firstName[0]}.{lastName}", + DateOfBirth = GenerateDateOfBirth(rng) + }; - if (!await HasUserCredentialAsync(connection, userAccountId)) - { - string pwd = generator.Generate( - length: 64, - numberOfDigits: 10, - numberOfSymbols: 10 - ); - string hash = GeneratePasswordHash(pwd); - await AddUserCredentialAsync(connection, userAccountId, hash); - createdCredentials++; - } - if (!await HasUserVerificationAsync(connection, userAccountId)) - { + // add user credentials + if (!await HasUserCredentialAsync(connection, userAccountId)) + { + string pwd = generator.Generate( + length: 64, + numberOfDigits: 10, + numberOfSymbols: 10 + ); + string hash = GeneratePasswordHash(pwd); + await AddUserCredentialAsync(connection, userAccountId, hash); + createdCredentials++; + } + + // add user verification + if (await HasUserVerificationAsync(connection, userAccountId)) continue; await AddUserVerificationAsync(connection, userAccountId); createdVerifications++; } + + Console.WriteLine($"Created {createdUsers} user accounts."); + Console.WriteLine($"Added {createdCredentials} user credentials."); + Console.WriteLine($"Added {createdVerifications} user verifications."); } - Console.WriteLine($"Created {createdUsers} user accounts."); - Console.WriteLine($"Added {createdCredentials} user credentials."); - Console.WriteLine($"Added {createdVerifications} user verifications."); - } - - private static string GeneratePasswordHash(string pwd) - { - byte[] salt = RandomNumberGenerator.GetBytes(16); - - var argon2 = new Argon2id(Encoding.UTF8.GetBytes(pwd)) + private static string GeneratePasswordHash(string pwd) { - Salt = salt, - DegreeOfParallelism = Math.Max(Environment.ProcessorCount, 1), - MemorySize = 65536, - Iterations = 4, - }; + byte[] salt = RandomNumberGenerator.GetBytes(16); - byte[] hash = argon2.GetBytes(32); - return $"{Convert.ToBase64String(salt)}:{Convert.ToBase64String(hash)}"; + var argon2 = new Argon2id(Encoding.UTF8.GetBytes(pwd)) + { + Salt = salt, + DegreeOfParallelism = Math.Max(Environment.ProcessorCount, 1), + MemorySize = 65536, + Iterations = 4, + }; + + byte[] hash = argon2.GetBytes(32); + return $"{Convert.ToBase64String(salt)}:{Convert.ToBase64String(hash)}"; + } + + private static async Task HasUserCredentialAsync( + SqlConnection connection, + Guid userAccountId + ) + { + const string sql = $""" + SELECT 1 + FROM dbo.UserCredential + WHERE UserAccountId = @UserAccountId; + """; + await using var command = new SqlCommand(sql, connection); + command.Parameters.AddWithValue("@UserAccountId", userAccountId); + object? result = await command.ExecuteScalarAsync(); + return result is not null; + } + + private static async Task AddUserCredentialAsync( + SqlConnection connection, + Guid userAccountId, + string hash + ) + { + await using var command = new SqlCommand( + "dbo.USP_AddUserCredential", + connection + ); + command.CommandType = CommandType.StoredProcedure; + command.Parameters.AddWithValue("@UserAccountId", userAccountId); + command.Parameters.AddWithValue("@Hash", hash); + + await command.ExecuteNonQueryAsync(); + } + + private static async Task HasUserVerificationAsync( + SqlConnection connection, + Guid userAccountId + ) + { + const string sql = """ + SELECT 1 + FROM dbo.UserVerification + WHERE UserAccountId = @UserAccountId; + """; + await using var command = new SqlCommand(sql, connection); + command.Parameters.AddWithValue("@UserAccountId", userAccountId); + var result = await command.ExecuteScalarAsync(); + return result is not null; + } + + private static async Task AddUserVerificationAsync( + SqlConnection connection, + Guid userAccountId + ) + { + await using var command = new SqlCommand( + "dbo.USP_CreateUserVerification", + connection + ); + command.CommandType = CommandType.StoredProcedure; + command.Parameters.AddWithValue("@UserAccountID", userAccountId); + + await command.ExecuteNonQueryAsync(); + } + + private static DateTime GenerateDateOfBirth(Random random) + { + int age = 19 + random.Next(0, 30); + DateTime baseDate = DateTime.UtcNow.Date.AddYears(-age); + int offsetDays = random.Next(0, 365); + return baseDate.AddDays(-offsetDays); + } } - - private static async Task GetUserAccountIdByUsernameAsync( - SqlConnection connection, - string username - ) - { - await using var command = new SqlCommand( - "usp_GetUserAccountByUsername", - connection - ); - command.CommandType = CommandType.StoredProcedure; - command.Parameters.AddWithValue("@Username", username); - - await using var reader = await command.ExecuteReaderAsync(); - return await reader.ReadAsync() ? reader.GetGuid(0) : null; - } - - private static async Task GetUserAccountIdByEmailAsync( - SqlConnection connection, - string email - ) - { - await using var command = new SqlCommand( - "usp_GetUserAccountByEmail", - connection - ); - command.CommandType = CommandType.StoredProcedure; - command.Parameters.AddWithValue("@Email", email); - - await using var reader = await command.ExecuteReaderAsync(); - return await reader.ReadAsync() ? reader.GetGuid(0) : null; - } - - private static async Task CreateUserAccountAsync( - SqlConnection connection, - Guid userAccountId, - string username, - string firstName, - string lastName, - string email, - DateTime dateOfBirth - ) - { - await using var command = new SqlCommand( - "usp_CreateUserAccount", - connection - ); - command.CommandType = CommandType.StoredProcedure; - command.Parameters.AddWithValue("@UserAccountId", userAccountId); - command.Parameters.AddWithValue("@Username", username); - command.Parameters.AddWithValue("@FirstName", firstName); - command.Parameters.AddWithValue("@LastName", lastName); - command.Parameters.AddWithValue("@DateOfBirth", dateOfBirth); - command.Parameters.AddWithValue("@Email", email); - - await command.ExecuteNonQueryAsync(); - } - - private static async Task HasUserCredentialAsync( - SqlConnection connection, - Guid userAccountId - ) - { - const string sql = """ -SELECT 1 -FROM dbo.UserCredential -WHERE UserAccountId = @UserAccountId; -"""; - await using var command = new SqlCommand(sql, connection); - command.Parameters.AddWithValue("@UserAccountId", userAccountId); - object? result = await command.ExecuteScalarAsync(); - return result is not null; - } - - private static async Task AddUserCredentialAsync( - SqlConnection connection, - Guid userAccountId, - string hash - ) - { - await using var command = new SqlCommand( - "dbo.USP_AddUserCredential", - connection - ); - command.CommandType = CommandType.StoredProcedure; - command.Parameters.AddWithValue("@UserAccountId", userAccountId); - command.Parameters.AddWithValue("@Hash", hash); - - await command.ExecuteNonQueryAsync(); - } - - private static async Task HasUserVerificationAsync( - SqlConnection connection, - Guid userAccountId - ) - { - const string sql = """ -SELECT 1 -FROM dbo.UserVerification -WHERE UserAccountId = @UserAccountId; -"""; - await using var command = new SqlCommand(sql, connection); - command.Parameters.AddWithValue("@UserAccountId", userAccountId); - object? result = await command.ExecuteScalarAsync(); - return result is not null; - } - - private static async Task AddUserVerificationAsync( - SqlConnection connection, - Guid userAccountId - ) - { - await using var command = new SqlCommand( - "dbo.USP_CreateUserVerification", - connection - ); - command.CommandType = CommandType.StoredProcedure; - command.Parameters.AddWithValue("@UserAccountID", userAccountId); - - await command.ExecuteNonQueryAsync(); - } - - private static string BuildUsername(string firstName, string lastName) - { - string username = $"{firstName}.{lastName}".ToLowerInvariant(); - return username.Length <= 64 ? username : username[..64]; - } - - private static string BuildEmail(string firstName, string lastName) - { - string email = $"{firstName}.{lastName}@example.com".ToLowerInvariant(); - return email.Length <= 128 ? email : email[..128]; - } - - private static DateTime GenerateDateOfBirth(Random random) - { - int age = 19 + random.Next(0, 30); - DateTime baseDate = DateTime.UtcNow.Date.AddYears(-age); - int offsetDays = random.Next(0, 365); - return baseDate.AddDays(-offsetDays); - } -} +} \ No newline at end of file diff --git a/DataAccessLayer/Entities/UserAccount.cs b/DataAccessLayer/Entities/UserAccount.cs index 2b93986..cf9baf5 100644 --- a/DataAccessLayer/Entities/UserAccount.cs +++ b/DataAccessLayer/Entities/UserAccount.cs @@ -2,7 +2,7 @@ namespace DataAccessLayer.Entities; public class UserAccount { - public Guid UserAccountID { get; set; } + public Guid UserAccountId { get; set; } public string Username { get; set; } = string.Empty; public string FirstName { get; set; } = string.Empty; public string LastName { get; set; } = string.Empty; diff --git a/DataAccessLayer/Entities/UserCredential.cs b/DataAccessLayer/Entities/UserCredential.cs index 3f4117a..07468ff 100644 --- a/DataAccessLayer/Entities/UserCredential.cs +++ b/DataAccessLayer/Entities/UserCredential.cs @@ -2,8 +2,8 @@ namespace DataAccessLayer.Entities; public class UserCredential { - public Guid UserCredentialID { get; set; } - public Guid UserAccountID { get; set; } + public Guid UserCredentialId { get; set; } + public Guid UserAccountId { get; set; } public DateTime CreatedAt { get; set; } public DateTime Expiry { get; set; } public string Hash { get; set; } = string.Empty; diff --git a/DataAccessLayer/Entities/UserVerification.cs b/DataAccessLayer/Entities/UserVerification.cs index 3eb0c13..6a503d8 100644 --- a/DataAccessLayer/Entities/UserVerification.cs +++ b/DataAccessLayer/Entities/UserVerification.cs @@ -2,8 +2,8 @@ namespace DataAccessLayer.Entities; public class UserVerification { - public Guid UserVerificationID { get; set; } - public Guid UserAccountID { get; set; } + public Guid UserVerificationId { get; set; } + public Guid UserAccountId { get; set; } public DateTime VerificationDateTime { get; set; } public byte[]? Timer { get; set; } } diff --git a/DataAccessLayer/IRepository.cs b/DataAccessLayer/Repositories/IRepository.cs similarity index 66% rename from DataAccessLayer/IRepository.cs rename to DataAccessLayer/Repositories/IRepository.cs index 5aee600..4b71078 100644 --- a/DataAccessLayer/IRepository.cs +++ b/DataAccessLayer/Repositories/IRepository.cs @@ -1,7 +1,8 @@ -using System; -using System.Collections.Generic; -namespace DataAccessLayer + +using Microsoft.Data.SqlClient; + +namespace DataAccessLayer.Repositories { public interface IRepository where T : class @@ -13,5 +14,7 @@ namespace DataAccessLayer T? GetById(Guid id); void Update(T entity); void Delete(Guid id); + + T MapToEntity(SqlDataReader entity); } } diff --git a/DataAccessLayer/Repositories/IUserAccountRepository.cs b/DataAccessLayer/Repositories/IUserAccountRepository.cs index 2e880a6..da1efbf 100644 --- a/DataAccessLayer/Repositories/IUserAccountRepository.cs +++ b/DataAccessLayer/Repositories/IUserAccountRepository.cs @@ -1,8 +1,6 @@ -using System; -using System.Collections.Generic; using DataAccessLayer.Entities; -namespace DataAccessLayer +namespace DataAccessLayer.Repositories { public interface IUserAccountRepository : IRepository { diff --git a/DataAccessLayer/Repositories/UserAccountRepository.cs b/DataAccessLayer/Repositories/UserAccountRepository.cs index 4790f3e..00103b0 100644 --- a/DataAccessLayer/Repositories/UserAccountRepository.cs +++ b/DataAccessLayer/Repositories/UserAccountRepository.cs @@ -1,30 +1,36 @@ -using System; -using System.Collections.Generic; using DataAccessLayer.Entities; using Microsoft.Data.SqlClient; -namespace DataAccessLayer +namespace DataAccessLayer.Repositories { public class UserAccountRepository : IUserAccountRepository { - private readonly string _connectionString; - - public UserAccountRepository() - { - // Retrieve the connection string from environment variables - _connectionString = - Environment.GetEnvironmentVariable("DB_CONNECTION_STRING") - ?? throw new InvalidOperationException( - "The connection string is not set in the environment variables." - ); - } + private readonly string _connectionString = Environment.GetEnvironmentVariable("DB_CONNECTION_STRING") + ?? throw new InvalidOperationException( + "The connection string is not set in the environment variables." + ); public void Add(UserAccount userAccount) { using SqlConnection connection = new(_connectionString); using SqlCommand command = new("usp_CreateUserAccount", connection); command.CommandType = System.Data.CommandType.StoredProcedure; - AddUserAccountCreateParameters(command, userAccount); + + command.Parameters.AddWithValue( + "@UserAccountId", + userAccount.UserAccountId + ); + command.Parameters.AddWithValue("@Username", userAccount.Username); + command.Parameters.AddWithValue( + "@FirstName", + userAccount.FirstName + ); + command.Parameters.AddWithValue("@LastName", userAccount.LastName); + command.Parameters.AddWithValue("@Email", userAccount.Email); + command.Parameters.AddWithValue( + "@DateOfBirth", + userAccount.DateOfBirth + ); connection.Open(); command.ExecuteNonQuery(); } @@ -41,7 +47,7 @@ namespace DataAccessLayer connection.Open(); using SqlDataReader reader = command.ExecuteReader(); - return reader.Read() ? MapUserAccount(reader) : null; + return reader.Read() ? MapToEntity(reader) : null; } public void Update(UserAccount userAccount) @@ -49,7 +55,25 @@ namespace DataAccessLayer using SqlConnection connection = new(_connectionString); using SqlCommand command = new("usp_UpdateUserAccount", connection); command.CommandType = System.Data.CommandType.StoredProcedure; - AddUserAccountUpdateParameters(command, userAccount); + command.Parameters.AddWithValue( + "@UserAccountId", + userAccount.UserAccountId + ); + command.Parameters.AddWithValue("@Username", userAccount.Username); + command.Parameters.AddWithValue( + "@FirstName", + userAccount.FirstName + ); + command.Parameters.AddWithValue("@LastName", userAccount.LastName); + command.Parameters.AddWithValue("@Email", userAccount.Email); + command.Parameters.AddWithValue( + "@DateOfBirth", + userAccount.DateOfBirth + ); + command.Parameters.AddWithValue( + "@UserAccountId", + userAccount.UserAccountId + ); connection.Open(); command.ExecuteNonQuery(); } @@ -64,9 +88,10 @@ namespace DataAccessLayer command.ExecuteNonQuery(); } + public IEnumerable GetAll(int? limit, int? offset) { - if (limit.HasValue && limit <= 0) + if (limit is <= 0) { throw new ArgumentOutOfRangeException( nameof(limit), @@ -110,7 +135,7 @@ namespace DataAccessLayer List users = new(); while (reader.Read()) { - users.Add(MapUserAccount(reader)); + users.Add(MapToEntity(reader)); } return users; @@ -127,8 +152,8 @@ namespace DataAccessLayer command.Parameters.AddWithValue("@Username", username); connection.Open(); - using SqlDataReader reader = command.ExecuteReader(); - return reader.Read() ? MapUserAccount(reader) : null; + using SqlDataReader? reader = command.ExecuteReader(); + return reader.Read() ? MapToEntity(reader) : null; } public UserAccount? GetByEmail(string email) @@ -143,48 +168,14 @@ namespace DataAccessLayer connection.Open(); using SqlDataReader reader = command.ExecuteReader(); - return reader.Read() ? MapUserAccount(reader) : null; + return reader.Read() ? MapToEntity(reader) : null; } - private static void AddUserAccountCreateParameters( - SqlCommand command, - UserAccount userAccount - ) - { - command.Parameters.AddWithValue( - "@UserAccountId", - userAccount.UserAccountID - ); - command.Parameters.AddWithValue("@Username", userAccount.Username); - command.Parameters.AddWithValue( - "@FirstName", - userAccount.FirstName - ); - command.Parameters.AddWithValue("@LastName", userAccount.LastName); - command.Parameters.AddWithValue("@Email", userAccount.Email); - command.Parameters.AddWithValue( - "@DateOfBirth", - userAccount.DateOfBirth - ); - } - - private static void AddUserAccountUpdateParameters( - SqlCommand command, - UserAccount userAccount - ) - { - AddUserAccountCreateParameters(command, userAccount); - command.Parameters.AddWithValue( - "@UserAccountId", - userAccount.UserAccountID - ); - } - - private static UserAccount MapUserAccount(SqlDataReader reader) + public UserAccount MapToEntity(SqlDataReader reader) { return new UserAccount { - UserAccountID = reader.GetGuid(0), + UserAccountId = reader.GetGuid(0), Username = reader.GetString(1), FirstName = reader.GetString(2), LastName = reader.GetString(3), diff --git a/DataAccessLayer/Sql/DatabaseHelper.cs b/DataAccessLayer/Sql/DatabaseHelper.cs index 53171a0..5096a13 100644 --- a/DataAccessLayer/Sql/DatabaseHelper.cs +++ b/DataAccessLayer/Sql/DatabaseHelper.cs @@ -4,45 +4,31 @@ using Microsoft.Data.SqlClient; namespace DataAccessLayer.Sql { - public class DatabaseHelper + public class DatabaseHelper(string connectionString) { - private readonly string _connectionString; - - public DatabaseHelper(string connectionString) - { - _connectionString = connectionString; - } - public void ExecuteRawSql(string query) { try { - using ( - SqlConnection connection = new SqlConnection( - _connectionString - ) - ) + using var connection = new SqlConnection( + connectionString + ); + + connection.Open(); + + using var command = new SqlCommand(query, connection); + + command.CommandType = CommandType.Text; + + using var reader = command.ExecuteReader(); + + while (reader.Read()) { - connection.Open(); - - using ( - SqlCommand command = new SqlCommand(query, connection) - ) + for (var i = 0; i < reader.FieldCount; i++) { - command.CommandType = CommandType.Text; - - using (SqlDataReader reader = command.ExecuteReader()) - { - while (reader.Read()) - { - for (int i = 0; i < reader.FieldCount; i++) - { - Console.WriteLine( - $"{reader.GetName(i)}: {reader.GetValue(i)}" - ); - } - } - } + Console.WriteLine( + $"{reader.GetName(i)}: {reader.GetValue(i)}" + ); } } } diff --git a/WebAPI/Controllers/UsersController.cs b/WebAPI/Controllers/UsersController.cs index 18c08b3..1764447 100644 --- a/WebAPI/Controllers/UsersController.cs +++ b/WebAPI/Controllers/UsersController.cs @@ -65,15 +65,15 @@ namespace WebAPI.Controllers [HttpPost] public IActionResult CreateUser([FromBody] UserAccount userAccount) { - if (userAccount.UserAccountID == Guid.Empty) + if (userAccount.UserAccountId == Guid.Empty) { - userAccount.UserAccountID = Guid.NewGuid(); + userAccount.UserAccountId = Guid.NewGuid(); } _userService.Add(userAccount); return CreatedAtAction( nameof(GetUserById), - new { id = userAccount.UserAccountID }, + new { id = userAccount.UserAccountId }, userAccount ); } @@ -85,14 +85,14 @@ namespace WebAPI.Controllers ) { if ( - userAccount.UserAccountID != Guid.Empty - && userAccount.UserAccountID != id + userAccount.UserAccountId != Guid.Empty + && userAccount.UserAccountId != id ) { return BadRequest("UserAccountID does not match route id."); } - userAccount.UserAccountID = id; + userAccount.UserAccountId = id; _userService.Update(userAccount); return NoContent(); } diff --git a/WebAPI/Program.cs b/WebAPI/Program.cs index 24618ed..9b595a0 100644 --- a/WebAPI/Program.cs +++ b/WebAPI/Program.cs @@ -1,5 +1,6 @@ using BusinessLayer.Services; using DataAccessLayer; +using DataAccessLayer.Repositories; var builder = WebApplication.CreateBuilder(args);