using System; using System.Collections.Generic; using AyaNova.Models; using AyaNova.Biz; using Microsoft.Extensions.Logging; using Microsoft.EntityFrameworkCore; using Bogus; using AyaNova.Api.ControllerHelpers; using System.Diagnostics; namespace AyaNova.Util { public static class Seeder { public enum SeedLevel { SmallOneManShopTrialDataSet, MediumLocalServiceCompanyTrialDataSet, LargeCorporateMultiRegionalTrialDataSet }; ////////////////////////////////////////////////////// //Seed database for trial and testing purposes // public static void SeedDatabase(SeedLevel slevel) { ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger("Seeder"); ApiServerState apiServerState = (ApiServerState)ServiceProviderProvider.Provider.GetService(typeof(ApiServerState)); //get the current server state so can set back to it later ApiServerState.ServerState wasServerState = apiServerState.GetState(); string wasReason = apiServerState.Reason; try { log.LogInformation("SEEDER: SeedDatabase, level is: " + slevel.ToString()); //Only allow this in a trial database if (!AyaNova.Core.License.ActiveKey.TrialLicense) { throw new System.NotSupportedException("This database has a registered license key and can't be seeded."); } log.LogInformation("Setting server state to OpsOnly"); apiServerState.SetOpsOnly("Seeding database"); //Erase all the data except for the license, schema and the manager user DbUtil.PrepareDatabaseForSeeding(log); //Seed special test data for integration testing //log.LogInformation("Seeding known users"); SeedKnownUsers(log); //log.LogInformation("Seeding all other data"); switch (slevel) { //This is for a busy but one man shop with a single office person handling stuff back at the shop case SeedLevel.SmallOneManShopTrialDataSet: //Generate owner and lead tech GenSeedUser(log, 1, AuthorizationRoles.BizAdminFull | AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryFull | AuthorizationRoles.OpsAdminFull, UserType.Schedulable); //Generate one office person / secretary GenSeedUser(log, 1, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryFull | AuthorizationRoles.AccountingFull, UserType.NonSchedulable); //200 widgets GenSeedWidget(log, 200); break; //This is for a typical AyaNova medium busy shop //has one location, many techs and full staff for each department case SeedLevel.MediumLocalServiceCompanyTrialDataSet: //One IT administrator, can change ops but nothing else GenSeedUser(log, 1, AuthorizationRoles.OpsAdminFull, UserType.NonSchedulable); //One business administrator, can view ops issues GenSeedUser(log, 1, AuthorizationRoles.BizAdminFull | AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable); //One owner who doesn't control anything but views stuff GenSeedUser(log, 1, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited | AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable); //20 techs GenSeedUser(log, 20, AuthorizationRoles.TechFull | AuthorizationRoles.DispatchLimited, UserType.Schedulable); //2 subcontractors GenSeedUser(log, 2, AuthorizationRoles.SubContractorFull, UserType.Subcontractor); //3 sales / generic office people people GenSeedUser(log, 3, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //1 dispatch manager GenSeedUser(log, 1, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //1 Inventory manager GenSeedUser(log, 1, AuthorizationRoles.InventoryFull | AuthorizationRoles.DispatchLimited, UserType.NonSchedulable); //1 accountant / bookkeeper GenSeedUser(log, 1, AuthorizationRoles.AccountingFull | AuthorizationRoles.BizAdminLimited, UserType.NonSchedulable); //10 full on client users GenSeedUser(log, 10, AuthorizationRoles.ClientLimited, UserType.Client); //10 limited client users GenSeedUser(log, 10, AuthorizationRoles.ClientLimited, UserType.Client); //2000 widgets GenSeedWidget(log, 2000); break; //this is a large corporation with multiple branches in multiple locations all in the same country //Each location has a full staff and corporate head office has an overarching staff member in charge of each location case SeedLevel.LargeCorporateMultiRegionalTrialDataSet: //PERF log.LogInformation($"Seeding user(s)...."); var watch = new Stopwatch(); watch.Start(); //IT administrator, can change ops but nothing else GenSeedUser(log, 2, AuthorizationRoles.OpsAdminFull, UserType.NonSchedulable); //business administrator, can view ops issues GenSeedUser(log, 2, AuthorizationRoles.BizAdminFull | AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable); //owner / upper management who doesn't control anything but views stuff GenSeedUser(log, 5, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited | AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable); //techs //GenSeedUser(log, 100, AuthorizationRoles.TechFull | AuthorizationRoles.DispatchLimited, UserType.Schedulable); for (int i = 0; i < 20; i++) { GenSeedUser(log, 5, AuthorizationRoles.TechFull | AuthorizationRoles.DispatchLimited, UserType.Schedulable); } //limited techs GenSeedUser(log, 50, AuthorizationRoles.TechLimited | AuthorizationRoles.DispatchLimited, UserType.Schedulable); //20 subcontractors GenSeedUser(log, 20, AuthorizationRoles.SubContractorFull, UserType.Subcontractor); //10 limited subcontractors GenSeedUser(log, 10, AuthorizationRoles.SubContractorLimited, UserType.Subcontractor); //30 sales / generic office people people GenSeedUser(log, 30, AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //5 dispatch manager GenSeedUser(log, 5, AuthorizationRoles.DispatchFull | AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //5 Inventory manager GenSeedUser(log, 5, AuthorizationRoles.InventoryFull | AuthorizationRoles.DispatchLimited, UserType.NonSchedulable); //10 Inventory manager assistants GenSeedUser(log, 5, AuthorizationRoles.InventoryLimited, UserType.NonSchedulable); //5 accountant / bookkeeper GenSeedUser(log, 5, AuthorizationRoles.AccountingFull | AuthorizationRoles.BizAdminLimited, UserType.NonSchedulable); //100 full on client users GenSeedUser(log, 20, AuthorizationRoles.ClientFull, UserType.Client); //100 limited client users GenSeedUser(log, 20, AuthorizationRoles.ClientLimited, UserType.Client); //PERF watch.Stop(); log.LogInformation($"Users seeded in {watch.ElapsedMilliseconds} ms"); //20000 widgets log.LogInformation($"Seeding Widgets...."); watch = new Stopwatch(); watch.Start(); GenSeedWidget(log, 20000); // for (int i = 0; i < 400; i++) // { // GenSeedWidget(log, 50); // } //PERF watch.Stop(); log.LogInformation($"Widgets seeded in {watch.ElapsedMilliseconds} ms"); break; } log.LogInformation("Seeding completed successfully"); } catch (Exception ex) { log.LogError(ex, "Seeder:SeedDatabase error during ops"); throw ex; } finally { log.LogInformation($"Seeder: setting server state back to {wasServerState.ToString()}"); apiServerState.SetState(wasServerState, wasReason); } } public static long RUNNING_COUNT = 0; public static string Uniquify(string s) { //return s + " " + ((DateTimeOffset)DateTime.Now).ToUnixTimeSeconds(); return s + " " + (++RUNNING_COUNT).ToString(); } ////////////////////////////////////////////////////// //Seed test data for integration tests // public static void SeedKnownUsers(ILogger log) { try { //TEST USERS //one of each role type GenSeedUser(log, 1, AuthorizationRoles.BizAdminLimited, UserType.NonSchedulable, "BizAdminLimited", "BizAdminLimited"); GenSeedUser(log, 1, AuthorizationRoles.BizAdminFull, UserType.NonSchedulable, "BizAdminFull", "BizAdminFull"); GenSeedUser(log, 1, AuthorizationRoles.DispatchLimited, UserType.NonSchedulable, "DispatchLimited", "DispatchLimited"); GenSeedUser(log, 1, AuthorizationRoles.DispatchFull, UserType.NonSchedulable, "DispatchFull", "DispatchFull"); GenSeedUser(log, 1, AuthorizationRoles.InventoryLimited, UserType.NonSchedulable, "InventoryLimited", "InventoryLimited"); GenSeedUser(log, 1, AuthorizationRoles.InventoryFull, UserType.NonSchedulable, "InventoryFull", "InventoryFull"); GenSeedUser(log, 1, AuthorizationRoles.AccountingFull, UserType.NonSchedulable, "Accounting", "Accounting"); GenSeedUser(log, 1, AuthorizationRoles.TechLimited, UserType.Schedulable, "TechLimited", "TechLimited"); GenSeedUser(log, 1, AuthorizationRoles.TechFull, UserType.Schedulable, "TechFull", "TechFull"); GenSeedUser(log, 1, AuthorizationRoles.SubContractorLimited, UserType.Subcontractor, "SubContractorLimited", "SubContractorLimited"); GenSeedUser(log, 1, AuthorizationRoles.SubContractorFull, UserType.Subcontractor, "SubContractorFull", "SubContractorFull"); GenSeedUser(log, 1, AuthorizationRoles.ClientLimited, UserType.Client, "ClientLimited", "ClientLimited"); GenSeedUser(log, 1, AuthorizationRoles.ClientFull, UserType.Client, "ClientFull", "ClientFull"); GenSeedUser(log, 1, AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable, "OpsAdminLimited", "OpsAdminLimited"); GenSeedUser(log, 1, AuthorizationRoles.OpsAdminFull, UserType.NonSchedulable, "OpsAdminFull", "OpsAdminFull"); //PRIVACY TEST USER - this is used for a test to see if user info leaks into the logs GenSeedUser(log, 1, AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable, "TEST_PRIVACY_USER_ACCOUNT", "TEST_PRIVACY_USER_ACCOUNT"); //TEST NOT ACTIVE - this is used for a test to see if inactive user can login GenSeedUser(log, 1, AuthorizationRoles.OpsAdminLimited, UserType.NonSchedulable, false, "TEST_INACTIVE", "TEST_INACTIVE"); } catch { throw; } } /// /// Generate seed user with active=true /// (override to save typing) /// /// /// /// /// /// /// public static void GenSeedUser(ILogger log, int count, AuthorizationRoles roles, UserType userType, string login, string password) { try { GenSeedUser(log, count, roles, userType, true, login, password); } catch { throw; } } public static void GenSeedUser(ILogger log, int count, AuthorizationRoles roles, UserType userType, bool active = true, string login = null, string password = null) { // log.LogInformation($"Seeding {count.ToString()} user(s)"); //var watch = new Stopwatch(); //watch.Start(); UserBiz Biz = UserBiz.GetBizInternal(ServiceProviderProvider.DBContext); //allow creation of not entirely ready users (missing client id or subcontractor vendor id etc) Biz.SeedOrImportRelaxedRulesMode = true; for (int x = 0; x < count; x++) { User u = new User(); u.Active = active; u.OwnerId = 1; var p = new Bogus.Person(); u.Name = Uniquify(p.FullName); // u.Salt = Hasher.GenerateSalt(); if (login != null) { u.Login = login; u.Name += " - " + login; } else u.Login = p.FirstName; if (password != null) u.Password = password;//u.Password = Hasher.hash(u.Salt, password); else u.Password = u.Login;//u.Password = Hasher.hash(u.Salt, u.Login); u.Roles = roles; u.LocaleId = ServerBootConfig.AYANOVA_DEFAULT_LANGUAGE_ID; u.UserType = userType; //TODO: After have USER and HEADOFFICE and VENDOR, if usertype is subcontractor or client or headoffice it needs to set a corresponding user's parent org record id to go with it //Children and relations u.UserOptions = new UserOptions(1); u.UserOptions.EmailAddress = p.Email.Replace("gmail.com", "helloayanova.com").Replace("hotmail.com", "helloayanova.com").Replace("yahoo.com", "helloayanova.com"); //Re-instantiating the biz object in the loop speeds it up probably due to db context losing changes to track from before // UserBiz Biz = UserBiz.GetBizInternal(ServiceProviderProvider.DBContext); // //allow creation of not entirely ready users (missing client id or subcontractor vendor id etc) // Biz.SeedOrImportRelaxedRulesMode = true; var NewObject = Biz.Create(ServiceProviderProvider.DBContext, u);//sync version faster than async one if (NewObject == null) { log.LogError($"Seeder::GenSeedUser error creating user {u.Name}\r\n" + Biz.GetErrorsAsString()); throw new System.Exception("Seeder::GenSeedUser error creating user\r\n" + Biz.GetErrorsAsString()); } } // watch.Stop(); // log.LogInformation($"{count.ToString()} user(s) seeded in {watch.ElapsedMilliseconds} ms"); } ////////////////////////////////////////////////////// //Seed widget for testing // public static void GenSeedWidget(ILogger log, int count) { log.LogInformation($"Seeding {count.ToString()} Widget(s)"); var watch = new Stopwatch(); watch.Start(); //get a context just for this op to save memory on changetracking //AyContext ct = ServiceProviderProvider.DBContext; WidgetBiz Biz = WidgetBiz.GetBizInternal(ServiceProviderProvider.DBContext); var f = new Bogus.Faker(); for (int x = 0; x < count; x++) { Widget o = new Widget(); o.Name = Uniquify(f.Commerce.ProductName()); o.Active = true; o.StartDate = f.Date.Between(DateTime.Now, DateTime.Now.AddMinutes(60)); o.EndDate = f.Date.Between(DateTime.Now.AddMinutes(90), DateTime.Now.AddHours(5)); o.DollarAmount = Convert.ToDecimal(f.Commerce.Price()); o.OwnerId = 1; //this is nonsense but just to test an enum o.Roles = AuthorizationRoles.DispatchLimited | AuthorizationRoles.InventoryLimited | AuthorizationRoles.OpsAdminLimited; o.Notes = f.Lorem.Paragraph(); var NewObject = Biz.Create(ServiceProviderProvider.DBContext, o); if (NewObject == null) { log.LogError($"Seeder::GenSeedWidget error creating widget {o.Name}\r\n" + Biz.GetErrorsAsString()); throw new System.Exception("Seeder::GenSeedWidget error creating widget\r\n" + Biz.GetErrorsAsString()); } } watch.Stop(); log.LogInformation($"{count.ToString()} Widget(s) seeded in {watch.ElapsedMilliseconds} ms"); } }//eoc }//eons