From cd016efbfb866be2897304ead283f00d211e4819 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Fri, 4 Dec 2020 20:49:05 +0000 Subject: [PATCH] --- .vscode/launch.json | 4 +- server/AyaNova/Startup.cs | 3 +- server/AyaNova/biz/BizObject.cs | 4 +- server/AyaNova/biz/CustomerBiz.cs | 10 +-- server/AyaNova/biz/HeadOfficeBiz.cs | 2 + server/AyaNova/biz/NotifyEventProcessor.cs | 5 +- server/AyaNova/biz/TranslationBiz.cs | 5 +- server/AyaNova/biz/UserBiz.cs | 12 ++-- server/AyaNova/biz/VendorBiz.cs | 10 ++- server/AyaNova/biz/WidgetBiz.cs | 3 + server/AyaNova/util/Seeder.cs | 81 ++++++++++++---------- server/AyaNova/util/ServerBootConfig.cs | 8 ++- 12 files changed, 89 insertions(+), 58 deletions(-) diff --git a/.vscode/launch.json b/.vscode/launch.json index 0e2049e9..6d395cd7 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -53,8 +53,8 @@ "AYANOVA_FOLDER_USER_FILES": "c:\\temp\\RavenTestData\\userfiles", "AYANOVA_FOLDER_BACKUP_FILES": "c:\\temp\\RavenTestData\\backupfiles", "AYANOVA_FOLDER_TEMPORARY_SERVER_FILES": "c:\\temp\\RavenTestData\\tempfiles", - "AYANOVA_SERVER_TEST_MODE": "false", - "AYANOVA_SERVER_TEST_MODE_SEEDLEVEL": "small", + "AYANOVA_SERVER_TEST_MODE": "true", + "AYANOVA_SERVER_TEST_MODE_SEEDLEVEL": "huge", "AYANOVA_SERVER_TEST_MODE_TZ_OFFSET": "-7", "AYANOVA_BACKUP_PG_DUMP_PATH": "C:\\data\\code\\postgres_13\\bin\\" }, diff --git a/server/AyaNova/Startup.cs b/server/AyaNova/Startup.cs index 9532c279..3b172c7a 100644 --- a/server/AyaNova/Startup.cs +++ b/server/AyaNova/Startup.cs @@ -36,7 +36,8 @@ namespace AyaNova _newLog = nlogLoggerProvider.CreateLogger("SERVER"); _hostingEnvironment = hostingEnvironment; AyaNova.Util.ApplicationLogging.LoggerProvider = nlogLoggerProvider; - ServerBootConfig.AYANOVA_CONTENT_ROOT_PATH = hostingEnvironment.ContentRootPath; + ServerBootConfig.AYANOVA_CONTENT_ROOT_PATH = hostingEnvironment.ContentRootPath; + ServerBootConfig.SEEDING=false; } private readonly ILogger _newLog; diff --git a/server/AyaNova/biz/BizObject.cs b/server/AyaNova/biz/BizObject.cs index d4d350d4..698e5a08 100644 --- a/server/AyaNova/biz/BizObject.cs +++ b/server/AyaNova/biz/BizObject.cs @@ -13,7 +13,7 @@ namespace AyaNova.Biz public BizObject() { - + } @@ -27,7 +27,7 @@ namespace AyaNova.Biz internal long UserId { get; set; } internal long UserTranslationId { get; set; } internal AuthorizationRoles CurrentUserRoles { get; set; } - + #endregion diff --git a/server/AyaNova/biz/CustomerBiz.cs b/server/AyaNova/biz/CustomerBiz.cs index 212d82bd..9a17ba09 100644 --- a/server/AyaNova/biz/CustomerBiz.cs +++ b/server/AyaNova/biz/CustomerBiz.cs @@ -210,7 +210,9 @@ namespace AyaNova.Biz private async Task ValidateAsync(Customer proposedObj, Customer currentObj) { - + //skip validation if seeding + if (ServerBootConfig.SEEDING) return; + bool isNew = currentObj == null; //Name required @@ -262,18 +264,18 @@ namespace AyaNova.Biz //So, for a customer, the Customer Notes collection is accessed from within the Customer form, even though it's actually external but user doesn't see it that way //so they would be deleted automatically, same goes for Contacts (if they can be deleted and don't have connections elsewhere) - + //Workorders and things that you select a Customer for would trigger an error and NOT be deleted automatically //The Mass delete Extension will be used in those cases to clear out all the workorders etc - + //Referential integrity error if (await ct.User.AnyAsync(z => z.CustomerId == inObj.Id) == true) { //Note: errorbox will ensure it appears in the general errror box and not field specific //the translation key is to indicate what the linked object is that is causing the error - AddError(ApiErrorCode.VALIDATION_REFERENTIAL_INTEGRITY, "errorbox", "LT:Contact"); + AddError(ApiErrorCode.VALIDATION_REFERENTIAL_INTEGRITY, "errorbox", "LT:Contact"); } } diff --git a/server/AyaNova/biz/HeadOfficeBiz.cs b/server/AyaNova/biz/HeadOfficeBiz.cs index 185c50c9..52eeb2d6 100644 --- a/server/AyaNova/biz/HeadOfficeBiz.cs +++ b/server/AyaNova/biz/HeadOfficeBiz.cs @@ -205,6 +205,8 @@ namespace AyaNova.Biz private async Task ValidateAsync(HeadOffice proposedObj, HeadOffice currentObj) { + //skip validation if seeding + if (ServerBootConfig.SEEDING) return; bool isNew = currentObj == null; diff --git a/server/AyaNova/biz/NotifyEventProcessor.cs b/server/AyaNova/biz/NotifyEventProcessor.cs index 8b8d1a7c..d9433913 100644 --- a/server/AyaNova/biz/NotifyEventProcessor.cs +++ b/server/AyaNova/biz/NotifyEventProcessor.cs @@ -136,6 +136,7 @@ namespace AyaNova.Biz //will iterate the subscriptions and see if any apply here internal static async Task HandlePotentialNotificationEvent(AyaEvent ayaEvent, ICoreBizObjectModel newObject, ICoreBizObjectModel originalObject = null) { + if (ServerBootConfig.SEEDING) return; log.LogDebug($"HandlePotentialNotificationEvent processing: [AyaType:{newObject.AyaType}, AyaEvent:{ayaEvent}]"); //set to true if any changes are made to the context (NotifyEvent added) bool SaveContext = false; @@ -144,9 +145,9 @@ namespace AyaNova.Biz using (AyContext ct = AyaNova.Util.ServiceProviderProvider.DBContext) { //short circuit if there are no subscriptions which would be typical of a v8 migrate scenario or fresh import or just not using notification - if(!await ct.NotifySubscription.AnyAsync()) + if (!await ct.NotifySubscription.AnyAsync()) return; - + //switch through AyaEvent then individual Ayatypes as required for event types switch (ayaEvent) { diff --git a/server/AyaNova/biz/TranslationBiz.cs b/server/AyaNova/biz/TranslationBiz.cs index 1b901e42..0959a18e 100644 --- a/server/AyaNova/biz/TranslationBiz.cs +++ b/server/AyaNova/biz/TranslationBiz.cs @@ -12,7 +12,7 @@ namespace AyaNova.Biz internal class TranslationBiz : BizObject { - public bool SeedOrImportRelaxedRulesMode { get; set; } + internal TranslationBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles userRoles) { @@ -20,8 +20,7 @@ namespace AyaNova.Biz UserId = currentUserId; UserTranslationId = userTranslationId; CurrentUserRoles = userRoles; - BizType = AyaType.Translation; - SeedOrImportRelaxedRulesMode = false;//default + BizType = AyaType.Translation; } internal static TranslationBiz GetBiz(AyContext ct, Microsoft.AspNetCore.Http.HttpContext httpContext = null) diff --git a/server/AyaNova/biz/UserBiz.cs b/server/AyaNova/biz/UserBiz.cs index f3eb6fd8..7025c6cd 100644 --- a/server/AyaNova/biz/UserBiz.cs +++ b/server/AyaNova/biz/UserBiz.cs @@ -17,7 +17,7 @@ namespace AyaNova.Biz internal class UserBiz : BizObject, IJobObject, ISearchAbleObject, IReportAbleObject { - public bool SeedOrImportRelaxedRulesMode { get; set; } + internal UserBiz(AyContext dbcontext, long currentUserId, long userTranslationId, AuthorizationRoles userRoles) { @@ -26,7 +26,6 @@ namespace AyaNova.Biz UserTranslationId = userTranslationId; CurrentUserRoles = userRoles; BizType = AyaType.User; - SeedOrImportRelaxedRulesMode = false;//default } //This is where active tech license consumers are accounted for @@ -448,6 +447,9 @@ namespace AyaNova.Biz //Can save or update? private async Task ValidateAsync(User proposedObj, User currentObj) { + //skip validation if seeding + if(ServerBootConfig.SEEDING) return; + //run validation and biz rules bool isNew = currentObj == null; @@ -535,7 +537,7 @@ namespace AyaNova.Biz } //Validate customer type user - if (!SeedOrImportRelaxedRulesMode && proposedObj.UserType == UserType.Customer) + if (proposedObj.UserType == UserType.Customer) { if (proposedObj.CustomerId == null || proposedObj.CustomerId == 0) { @@ -550,7 +552,7 @@ namespace AyaNova.Biz } //Validate headoffice type user - if (!SeedOrImportRelaxedRulesMode && proposedObj.UserType == UserType.HeadOffice) + if (proposedObj.UserType == UserType.HeadOffice) { if (proposedObj.HeadOfficeId == null || proposedObj.HeadOfficeId == 0) { @@ -565,7 +567,7 @@ namespace AyaNova.Biz } //Validate subcontractor type user - if (!SeedOrImportRelaxedRulesMode && proposedObj.UserType == UserType.ServiceContractor) + if (proposedObj.UserType == UserType.ServiceContractor) { if (proposedObj.VendorId == null || proposedObj.VendorId == 0) { diff --git a/server/AyaNova/biz/VendorBiz.cs b/server/AyaNova/biz/VendorBiz.cs index 6c53b4b8..29cf84ee 100644 --- a/server/AyaNova/biz/VendorBiz.cs +++ b/server/AyaNova/biz/VendorBiz.cs @@ -54,6 +54,7 @@ namespace AyaNova.Biz await EventLogProcessor.LogEventToDatabaseAsync(new Event(UserId, newObject.Id, BizType, AyaEvent.Created), ct); await SearchIndexAsync(newObject, true); await TagBiz.ProcessUpdateTagsInRepositoryAsync(ct, newObject.Tags, null); + await NotifyEventProcessor.HandlePotentialNotificationEvent(AyaEvent.Created, newObject); return newObject; } @@ -153,7 +154,7 @@ namespace AyaNova.Biz Vendor dbObject = await ct.Vendor.SingleOrDefaultAsync(z => z.Id == id); ValidateCanDelete(dbObject); if (HasErrors) - return false; + return false; ct.Vendor.Remove(dbObject); await ct.SaveChangesAsync(); await EventLogProcessor.DeleteObjectLogAsync(UserId, BizType, dbObject.Id, dbObject.Name, ct); @@ -202,13 +203,16 @@ namespace AyaNova.Biz private async Task ValidateAsync(Vendor proposedObj, Vendor currentObj) { + //skip validation if seeding + if (ServerBootConfig.SEEDING) return; + bool isNew = currentObj == null; //Name required if (string.IsNullOrWhiteSpace(proposedObj.Name)) AddError(ApiErrorCode.VALIDATION_REQUIRED, "Name"); - + //If name is otherwise OK, check that name is unique if (!PropertyHasErrors("Name")) { @@ -241,7 +245,7 @@ namespace AyaNova.Biz } - + //////////////////////////////////////////////////////////////////////////////////////////////// //REPORTING // diff --git a/server/AyaNova/biz/WidgetBiz.cs b/server/AyaNova/biz/WidgetBiz.cs index 4476703f..54e00fc7 100644 --- a/server/AyaNova/biz/WidgetBiz.cs +++ b/server/AyaNova/biz/WidgetBiz.cs @@ -226,6 +226,9 @@ namespace AyaNova.Biz // private async Task ValidateAsync(Widget proposedObj) { + //skip validation if seeding + if(ServerBootConfig.SEEDING) return; + //NOTE: In DB schema only name and serial are not nullable //run validation and biz rules diff --git a/server/AyaNova/util/Seeder.cs b/server/AyaNova/util/Seeder.cs index 52cef748..1e15e11a 100644 --- a/server/AyaNova/util/Seeder.cs +++ b/server/AyaNova/util/Seeder.cs @@ -95,6 +95,8 @@ namespace AyaNova.Util apiServerState.SetOpsOnly("Seeding database"); + ServerBootConfig.SEEDING = true; + //Erase all the data except for the license, schema and the SuperUser await DbUtil.EmptyBizDataFromDatabaseForSeedingOrImportingAsync(log); //Event log erase and seeding @@ -198,7 +200,7 @@ namespace AyaNova.Util //Seed special test data for integration testing //log.LogInformation("Seeding known users"); - await SeedKnownUsersAsync(log); + await SeedKnownObjectsAsync(log); //log.LogInformation("Seeding all other data"); switch (slevel) @@ -282,7 +284,7 @@ namespace AyaNova.Util //WIDGETS - await SeedWidgetAsync(log, 500); + await SeedWidgetAsync(log, 100); //CUSTOMERS await SeedCustomerAsync(log, 500); @@ -359,7 +361,7 @@ namespace AyaNova.Util //WIDGETS - await SeedWidgetAsync(log, 5000); + await SeedWidgetAsync(log, 100); //CUSTOMERS await SeedCustomerAsync(log, 5000); @@ -436,7 +438,7 @@ namespace AyaNova.Util //WIDGETS - await SeedWidgetAsync(log, 20000); + await SeedWidgetAsync(log, 100); //CUSTOMERS await SeedCustomerAsync(log, 20000); @@ -494,8 +496,8 @@ namespace AyaNova.Util } finally { - //TODO: fully async: watch the job and don't turn the state back until the job is done? - + //No matter what seeding is done + ServerBootConfig.SEEDING = false; log.LogInformation($"Seeder: setting server state back to {wasServerState.ToString()}"); apiServerState.SetState(wasServerState, wasReason); } @@ -517,6 +519,9 @@ namespace AyaNova.Util return s + " " + (++RUNNING_COUNT).ToString(); } + public HashSet HashUserNames = new HashSet(); + public HashSet HashCompanyNames = new HashSet(); + private string[] TagSet = new[] { "red", "orange", "yellow", "green", "blue", "indigo", "violet", "brown", "black", "white", "silver", "gold", "fuchsia", "jade", "mauve", "purple", "quince", "xanthic", "zebra", "zone0", "zone1", "zone2", "zone3", "zone4", "zone5", "zone6", "zone7", "zone8", "zone9" }; @@ -530,7 +535,7 @@ namespace AyaNova.Util ////////////////////////////////////////////////////// //Seed test data for integration tests // - public async Task SeedKnownUsersAsync(ILogger log) + public async Task SeedKnownObjectsAsync(ILogger log) { try { @@ -569,6 +574,9 @@ namespace AyaNova.Util await SeedUserAsync(log, 1, AuthorizationRoles.All, UserType.NotService, true, "es", "es", await TranslationBiz.TranslationNameToIdStaticAsync("es"), KnownUserTags); await SeedUserAsync(log, 1, AuthorizationRoles.All, UserType.NotService, true, "fr", "fr", await TranslationBiz.TranslationNameToIdStaticAsync("fr"), KnownUserTags); + //CUSTOMER / HO Users + + } catch @@ -614,9 +622,12 @@ namespace AyaNova.Util User u = new User(); u.Active = active; - + do + { + u.Name = Fake.Name.FullName(); + } while (!HashUserNames.Add(u.Name)); + - u.Name = Uniquify(Fake.Name.FullName()); var bogusEmail = u.Name.ToLowerInvariant().Replace(" ", "_") + "@example.net"; if (login != null) { @@ -660,14 +671,12 @@ namespace AyaNova.Util //this seems wrong to get a new context inside a loop but in testing is actually faster!? using (AyContext ct = ServiceProviderProvider.DBContext) { - UserBiz Biz = UserBiz.GetBiz(ct); - //allow creation of not entirely ready users (missing client id or subcontractor vendor id etc) - Biz.SeedOrImportRelaxedRulesMode = true; - var NewObject = await Biz.CreateAsync(u); + UserBiz biz = UserBiz.GetBiz(ct); + var NewObject = await biz.CreateAsync(u); if (NewObject == null) { - log.LogError($"Seeder::SeedUser error creating {u.Name}\r\n" + Biz.GetErrorsAsString()); - throw new System.Exception("Seeder::SeedUser error creating user\r\n" + Biz.GetErrorsAsString()); + log.LogError($"Seeder::SeedUser error creating {u.Name}\r\n" + biz.GetErrorsAsString()); + throw new System.Exception("Seeder::SeedUser error creating user\r\n" + biz.GetErrorsAsString()); } } } @@ -675,6 +684,7 @@ namespace AyaNova.Util SeededUserCount += count; } + #region WIDGET ////////////////////////////////////////////////////// //Seed widget for testing // @@ -717,7 +727,7 @@ namespace AyaNova.Util //This seems wrong to do in a loop but is 4 times faster this way ?!? using (AyContext ct = ServiceProviderProvider.DBContext) { - WidgetBiz biz = WidgetBiz.GetBiz(ct); + WidgetBiz biz = WidgetBiz.GetBiz(ct); var NewObject = await biz.CreateAsync(o); if (NewObject == null) { @@ -728,7 +738,7 @@ namespace AyaNova.Util } } - + #endregion @@ -738,12 +748,16 @@ namespace AyaNova.Util public async Task SeedCustomerAsync(ILogger log, int count, long? headOfficeId = null) { - DateTime seedStartWindow = DateTime.Now.AddYears(-1); - DateTime seedEndWindow = DateTime.Now.AddYears(1); for (int x = 0; x < count; x++) { Customer o = new Customer(); - o.Name = Uniquify(Fake.Company.CompanyName()); + + do + { + o.Name = Fake.Name.FullName(); + } while (!HashCompanyNames.Add(o.Name)); + + o.Active = true; o.Notes = Fake.Company.CatchPhrase(); o.Tags = RandomTags(); @@ -784,8 +798,6 @@ namespace AyaNova.Util } //Customer contacts await SeedUserAsync(log, 1, AuthorizationRoles.CustomerFull, UserType.Customer, true, null, null, 0, null, null, NewObject.Id, null); - - } } } @@ -797,12 +809,16 @@ namespace AyaNova.Util public async Task SeedHeadOfficeAsync(ILogger log, int count) { - DateTime seedStartWindow = DateTime.Now.AddYears(-1); - DateTime seedEndWindow = DateTime.Now.AddYears(1); for (int x = 0; x < count; x++) { HeadOffice o = new HeadOffice(); - o.Name = Uniquify(Fake.Company.CompanyName()); + + do + { + o.Name = Fake.Name.FullName(); + } while (!HashCompanyNames.Add(o.Name)); + + o.Active = true; o.Notes = Fake.Company.CatchPhrase(); o.Tags = RandomTags(); @@ -852,18 +868,19 @@ namespace AyaNova.Util public async Task SeedVendorAsync(ILogger log, int count) { - DateTime seedStartWindow = DateTime.Now.AddYears(-1); - DateTime seedEndWindow = DateTime.Now.AddYears(1); for (int x = 0; x < count; x++) { Vendor o = new Vendor(); + do + { + o.Name = Fake.Name.FullName(); + } while (!HashCompanyNames.Add(o.Name)); + - o.Name = Uniquify(Fake.Company.CompanyName()); o.Active = true; o.Notes = Fake.Company.CatchPhrase(); o.Tags = RandomTags(); - o.Contact = Fake.Name.FullName(); o.ContactNotes = Fake.Name.FullName(); o.AccountNumber = Fake.Finance.Account(); @@ -880,8 +897,6 @@ namespace AyaNova.Util o.WebAddress = Fake.Internet.Url(); o.EmailAddress = Fake.Internet.ExampleEmail(); - - //This seems wrong to do in a loop but is 4 times faster this way ?!? using (AyContext ct = ServiceProviderProvider.DBContext) { @@ -894,10 +909,6 @@ namespace AyaNova.Util log.LogError(err); throw new System.Exception(err); } - //HeadOffice contacts - // await SeedUserAsync(log, 1, AuthorizationRoles.CustomerFull, UserType.HeadOffice, true, null, null, 0, null, null, null, NewObject.Id); - //HeadOffice Customer - // await SeedCustomerAsync(log, 2, NewObject.Id); } } diff --git a/server/AyaNova/util/ServerBootConfig.cs b/server/AyaNova/util/ServerBootConfig.cs index b3b8e4fd..417dab32 100644 --- a/server/AyaNova/util/ServerBootConfig.cs +++ b/server/AyaNova/util/ServerBootConfig.cs @@ -15,7 +15,13 @@ namespace AyaNova.Util //STATIC HARD CODED DEFAULTS NOT SET THROUGH CONFIG internal const int FAILED_AUTH_DELAY = 3000;//ms internal const int REPORT_RENDERING_OPERATION_TIMEOUT = 20000;//ms - //############################################################################################################ + //############################################################################################################ + + //############################ + //SEEDING FLAG INTERNAL ONLY + //used to speed up seeding with bypasses to normal validation etc + internal static bool SEEDING { get; set; } + //############################ //Diagnostic static values used during development, may not be related to config at all, this is just a convenient class to put them in #if (DEBUG)