From 553db1e8e4303d02265ce1e92955ed34528b7e28 Mon Sep 17 00:00:00 2001 From: John Cardinal Date: Tue, 8 Mar 2022 00:36:39 +0000 Subject: [PATCH] --- .vscode/launch.json | 6 +- server/AyaNova/biz/JobsBiz.cs | 11 +- server/AyaNova/biz/QuoteBiz.cs | 2 +- .../generator/CoreJobCustomerNotify.cs | 250 +++++++++++++++++ server/AyaNova/generator/CoreJobNotify.cs | 262 +----------------- 5 files changed, 259 insertions(+), 272 deletions(-) create mode 100644 server/AyaNova/generator/CoreJobCustomerNotify.cs diff --git a/.vscode/launch.json b/.vscode/launch.json index d0cceaa8..d1ace0ad 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -40,9 +40,9 @@ "env": { // "ASPNETCORE_ENVIRONMENT": "Development", "AYANOVA_JWT_SECRET": "1111111MyRandom32CharacterSecret", - "AYANOVA_LOG_LEVEL": "Info", - //"AYANOVA_LOG_LEVEL": "Debug", - // // "AYANOVA_LOG_LEVEL": "Trace", + //"AYANOVA_LOG_LEVEL": "Info", + "AYANOVA_LOG_LEVEL": "Debug", + //"AYANOVA_LOG_LEVEL": "Trace", "AYANOVA_DEFAULT_TRANSLATION": "en", "AYANOVA_DB_CONNECTION": "Server=localhost;Username=postgres;Password=raven;Database=AyaNova;CommandTimeout=120;", "AYANOVA_DATA_PATH": "c:\\temp\\ravendata", diff --git a/server/AyaNova/biz/JobsBiz.cs b/server/AyaNova/biz/JobsBiz.cs index f3a763a1..9836a739 100644 --- a/server/AyaNova/biz/JobsBiz.cs +++ b/server/AyaNova/biz/JobsBiz.cs @@ -204,19 +204,14 @@ namespace AyaNova.Biz if (!KeepOnWorking()) return; log.LogTrace("Processing level 2 internal jobs"); - // #if (DEBUG) - // log.LogInformation("Processing semi-critical internal jobs (backup, pm, notification etc)"); - // #endif - //BACKUP await CoreJobBackup.DoWorkAsync(); if (!KeepOnWorking()) return; //NOTIFICATIONS - TaskUtil.Forget(Task.Run(() => CoreJobNotify.DoWorkAsync()));//must fire and forget as it will call a report render job. In fact probably all of these can be fire and forget - // await CoreJobNotify.DoWorkAsync(); - + await CoreJobNotify.DoWorkAsync(); if (!KeepOnWorking()) return; + await CoreNotificationSweeper.DoWorkAsync(); if (!KeepOnWorking()) return; @@ -241,6 +236,8 @@ namespace AyaNova.Biz await CoreJobReportRenderEngineProcessCleanup.DoWork(); if (!KeepOnWorking()) return; + //CUSTOMER NOTIFICATIONS + TaskUtil.Forget(Task.Run(() => CoreJobCustomerNotify.DoWorkAsync()));//must fire and forget as it will call a report render job. In fact probably all of these can be fire and forget log.LogTrace("Processing exclusive dynamic jobs"); diff --git a/server/AyaNova/biz/QuoteBiz.cs b/server/AyaNova/biz/QuoteBiz.cs index f1e74e09..9cd1e955 100644 --- a/server/AyaNova/biz/QuoteBiz.cs +++ b/server/AyaNova/biz/QuoteBiz.cs @@ -1301,7 +1301,7 @@ namespace AyaNova.Biz AyaType = AyaType.Quote, ObjectId = oProposed.QuoteId, CustomerNotifySubscriptionId = sub.Id, - Name = $"{QuoteInfo.Serial.ToString()} - {qos.Name}", + Name = QuoteInfo.Serial.ToString(), Subject="TEST SUBJECT", Message="TEST MESSAGE"//TODO: template processing here }; diff --git a/server/AyaNova/generator/CoreJobCustomerNotify.cs b/server/AyaNova/generator/CoreJobCustomerNotify.cs new file mode 100644 index 00000000..c3ee535f --- /dev/null +++ b/server/AyaNova/generator/CoreJobCustomerNotify.cs @@ -0,0 +1,250 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using AyaNova.Models; +using AyaNova.Util; +using Newtonsoft.Json.Linq; + +namespace AyaNova.Biz +{ + + /// + /// Notification processor + /// turn notifyEvent records into inappnotification records for in app viewing and / or deliver smtp notifications seperately + /// + /// + internal static class CoreJobCustomerNotify + { + private static bool NotifyIsRunning = false; + private static ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger("CoreJobCustomerNotify"); + private static DateTime lastRun = DateTime.MinValue; + +#if (DEBUG) + private static TimeSpan RUN_EVERY_INTERVAL = new TimeSpan(0, 0, 21);//no more frequently than once every 20 seconds +#else + private static TimeSpan RUN_EVERY_INTERVAL = new TimeSpan(0, 1, 1);//no more frequently than once every 1 minute +#endif + + //////////////////////////////////////////////////////////////////////////////////////////////// + // DoSweep + // + public static async Task DoWorkAsync() + { + log.LogTrace("Checking if CustomerNotify should run"); + if (NotifyIsRunning) + { + log.LogTrace("CustomerNotify is running already exiting this cycle"); + return; + } + //This will get triggered roughly every minute, but we don't want to deliver that frequently + if (DateTime.UtcNow - lastRun < RUN_EVERY_INTERVAL) + { + log.LogTrace($"CustomerNotify ran less than {RUN_EVERY_INTERVAL} ago, exiting this cycle"); + return; + } + try + { + NotifyIsRunning = true; + log.LogDebug("CustomerNotify set to RUNNING state and starting now"); + + using (AyContext ct = AyaNova.Util.ServiceProviderProvider.DBContext) + { + var customerevents = await ct.CustomerNotifyEvent.AsNoTracking().ToListAsync(); + log.LogDebug($"Found {customerevents.Count} CustomerNotifyEvents to examine for potential delivery"); + + //iterate and deliver + foreach (var customernotifyevent in customerevents) + { + //no notifications for inactive users, just delete it as if it was delivered + var CustInfo = await ct.Customer.AsNoTracking().Where(x => x.Id == customernotifyevent.CustomerId).Select(x => new { x.Name, x.Active, x.Tags, x.EmailAddress }).FirstOrDefaultAsync(); + + + if (!CustInfo.Active) + { + log.LogDebug($"Inactive Customer {CustInfo.Name}, removing notify rather than delivering it: {customernotifyevent}"); + ct.CustomerNotifyEvent.Remove(customernotifyevent); + await ct.SaveChangesAsync(); + continue; + } + + if (string.IsNullOrWhiteSpace(CustInfo.EmailAddress)) + { + log.LogDebug($"Customer {CustInfo.Name} has no email address, removing notify rather than delivering it: {customernotifyevent}"); + ct.CustomerNotifyEvent.Remove(customernotifyevent); + await ct.SaveChangesAsync(); + continue; + } + + //Get subscription for delivery + var Subscription = await ct.CustomerNotifySubscription.AsNoTracking().FirstOrDefaultAsync(x => x.Id == customernotifyevent.CustomerNotifySubscriptionId); + + //NOTE: There is no need to separate out future delivery and immediate delivery because + // All events have an event date, it's either immediate upon creation or it's future + // but not all events have an age value including ones with future event dates, + // and the default agevalue and advancenotice are both zero regardless so the block below works for either future or immediate deliveries + + var deliverAfter = customernotifyevent.EventDate + Subscription.AgeValue - Subscription.AdvanceNotice; + if (deliverAfter < DateTime.UtcNow) + { + //Do the delivery, it's kosher + await DeliverCustomerNotificationSMTP(customernotifyevent, Subscription, CustInfo.EmailAddress, ct); + } + } + } + + } + catch (Exception ex) + { + log.LogError(ex, $"Error processing customer notification event"); + } + finally + { + log.LogDebug("CustomerNotify is done setting to not running state and tagging lastRun timestamp"); + lastRun = DateTime.UtcNow; + NotifyIsRunning = false; + + } + + } + + + //=== + private static async Task DeliverCustomerNotificationSMTP(CustomerNotifyEvent ne, CustomerNotifySubscription subscription, string deliveryAddress, AyContext ct) + { + + var DeliveryLogItem = new NotifyDeliveryLog() + { + Processed = DateTime.UtcNow, + ObjectId = ne.ObjectId, + NotifySubscriptionId = ne.CustomerNotifySubscriptionId, + Fail = false + }; + + try + { + log.LogDebug($"DeliverCustomerNotificationSMTP delivering notify event: {ne}"); + if (string.IsNullOrWhiteSpace(deliveryAddress)) + { + DeliveryLogItem.Fail = true; + DeliveryLogItem.Error = $"No email address provided for smtp delivery; event: {ne}"; + } + else + { + IMailer m = AyaNova.Util.ServiceProviderProvider.Mailer; + if (!ServerGlobalOpsSettingsCache.Notify.SmtpDeliveryActive) + { + await NotifyEventHelper.AddOpsProblemEvent($"Email notifications are set to OFF at server, unable to send Customer email notification for this event:{ne}"); + log.LogInformation($"** WARNING: SMTP notification is currently set to Active=False; unable to deliver Customer email notification, [CustomerId={ne.CustomerId}, Customer Notify subscription={ne.CustomerNotifySubscriptionId}]. Change this setting or remove all Customer notifications if this is permanent **"); + DeliveryLogItem.Fail = true; + DeliveryLogItem.Error = $"Email notifications are set to OFF at server, unable to send Customer email notification for this event: {ne}"; + } + else + { + //generate report if applicable + + if (subscription.LinkReportId != null) + { + long subTranslationId = (long)subscription.TranslationId; + + ReportBiz biz = new ReportBiz(ct, 1, subTranslationId, AuthorizationRoles.BizAdmin); + //example with workorder report + //{"AType":34,"selectedRowIds":[355],"ReportId":9,"ClientMeta":{"UserName":"AyaNova SuperUser","Authorization":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIxNjQ2NzgyNTc4IiwiaXNzIjoiYXlhbm92YS5jb20iLCJpZCI6IjEifQ.ad7Acq54JCRGitDWKDJFFnqKkidbdaKaFmj-RA_RG5E","DownloadToken":"NdoU8ca3LG4L39Tj2oi3UReeeM7FLevTgbgopTPhGbA","TimeZoneName":"America/Los_Angeles","LanguageName":"en-US","Hour12":true,"CurrencyName":"USD","DefaultLocale":"en","PDFDate":"3/3/22","PDFTime":"3:38 PM"}} + + var reportRequest = new DataListReportRequest(); + + reportRequest.AType = ne.AyaType; + reportRequest.ReportId = (long)subscription.LinkReportId; + reportRequest.SelectedRowIds = new long[] { ne.ObjectId }; + var jwt = Api.Controllers.AuthController.GenRpt(subTranslationId); + + //this could be adjusted by culture if we allow user to set a culture but that's getting a bit into the weeds, likely the server default is fine + var pdfDate = new DateTime().ToShortDateString(); + var pdfTime = new DateTime().ToShortTimeString(); + var h12 = subscription.Hour12 ? "true" : "false"; + reportRequest.ClientMeta = JToken.Parse($"{{'UserName':'-','Authorization':'Bearer {jwt}','TimeZoneName':'{subscription.TimeZoneOverride}','LanguageName':'{subscription.LanguageOverride}','Hour12':{h12},'CurrencyName':'{subscription.CurrencyName}','DefaultLocale':'en','PDFDate':'{pdfDate}','PDFTime':'{pdfTime}'}}"); + //get port number + var match = System.Text.RegularExpressions.Regex.Match(ServerBootConfig.AYANOVA_USE_URLS, "[0-9]+"); + var API_URL = $"http://127.0.0.1:{match.Value}/api/{AyaNovaVersion.CurrentApiVersion}/"; + var jobid = await biz.RequestRenderReport(reportRequest, DateTime.UtcNow.AddMinutes(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT), API_URL, "CUSTOMER NOTIFICATION - NO USER"); + if (jobid == null) + { + throw new ApplicationException($"Report render job id is null failed to start"); + } + else + { + bool done = false; + DateTime bailAfter = DateTime.Now.AddMinutes(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT); + while (!done && DateTime.Now < bailAfter) + { + var status = await JobsBiz.GetJobStatusAsync((Guid)jobid); + switch (status) + { + case JobStatus.Completed: + { + done = true; + //get job logs and parse file name from it + JobOperationsBiz jobopsbiz = new JobOperationsBiz(ct, 1, AuthorizationRoles.BizAdmin); + List log = await jobopsbiz.GetJobLogListAsync((Guid)jobid); + var lastLog = log[log.Count - 1]; + var lastLogJ = JObject.Parse(lastLog.StatusText); + var path = (string)lastLogJ["reportfilename"]; + var FilePath = FileUtil.GetFullPathForTemporaryFile(path); + var FileName = FileUtil.StringToSafeFileName(await TranslationBiz.GetTranslationStaticAsync(ne.AyaType.ToString(), subTranslationId, ct) + $"-{ne.Name}.pdf").ToLowerInvariant(); + await m.SendEmailAsync(deliveryAddress, ne.Subject, ne.Message, ServerGlobalOpsSettingsCache.Notify, FilePath, FileName); + break; + } + case JobStatus.Failed: + case JobStatus.Absent: + throw new ApplicationException($"REPORT RENDER JOB {jobid} started but failed"); + + } + + } + if (!done) + throw new TimeoutException("JOB FAILED DUE TO REPORT RENDER TIMEOUT"); + } + } + else + await m.SendEmailAsync(deliveryAddress, ne.Subject, ne.Message, ServerGlobalOpsSettingsCache.Notify); + + } + + } + } + catch (Exception ex) + { + await NotifyEventHelper.AddOpsProblemEvent("SMTP Customer Notification failed", ex); + DeliveryLogItem.Fail = true; + DeliveryLogItem.Error = $"SMTP Notification failed to deliver for this Customer notify event: {ne}, message: {ex.Message}"; + log.LogDebug(ex, $"DeliverSMTP Failure delivering Customer notify event: {ne}"); + } + finally + { + //remove event no matter what + ct.CustomerNotifyEvent.Remove(ne); + + //add delivery log item + await ct.NotifyDeliveryLog.AddAsync(DeliveryLogItem); + await ct.SaveChangesAsync(); + } + } + //=== + + + + + + + + + + ///////////////////////////////////////////////////////////////////// + + }//eoc + + +}//eons + diff --git a/server/AyaNova/generator/CoreJobNotify.cs b/server/AyaNova/generator/CoreJobNotify.cs index 5a7e1cc2..3daf2d93 100644 --- a/server/AyaNova/generator/CoreJobNotify.cs +++ b/server/AyaNova/generator/CoreJobNotify.cs @@ -6,7 +6,6 @@ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Logging; using AyaNova.Models; using AyaNova.Util; -using Newtonsoft.Json.Linq; namespace AyaNova.Biz { @@ -67,7 +66,7 @@ namespace AyaNova.Biz } } - #region User notification subscription events + using (AyContext ct = AyaNova.Util.ServiceProviderProvider.DBContext) { var events = await ct.NotifyEvent.AsNoTracking().ToListAsync(); @@ -137,55 +136,6 @@ namespace AyaNova.Biz } } } - #endregion user notification events - - - #region Customer 'proxy' notification subscription events - using (AyContext ct = AyaNova.Util.ServiceProviderProvider.DBContext) - { - var customerevents = await ct.CustomerNotifyEvent.AsNoTracking().ToListAsync(); - log.LogDebug($"Found {customerevents.Count} CustomerNotifyEvents to examine for potential delivery"); - - //iterate and deliver - foreach (var customernotifyevent in customerevents) - { - //no notifications for inactive users, just delete it as if it was delivered - var CustInfo = await ct.Customer.AsNoTracking().Where(x => x.Id == customernotifyevent.CustomerId).Select(x => new { x.Name, x.Active, x.Tags, x.EmailAddress }).FirstOrDefaultAsync(); - - - if (!CustInfo.Active) - { - log.LogDebug($"Inactive Customer {CustInfo.Name}, removing notify rather than delivering it: {customernotifyevent}"); - ct.CustomerNotifyEvent.Remove(customernotifyevent); - await ct.SaveChangesAsync(); - continue; - } - - if (string.IsNullOrWhiteSpace(CustInfo.EmailAddress)) - { - log.LogDebug($"Customer {CustInfo.Name} has no email address, removing notify rather than delivering it: {customernotifyevent}"); - ct.CustomerNotifyEvent.Remove(customernotifyevent); - await ct.SaveChangesAsync(); - continue; - } - - //Get subscription for delivery - var Subscription = await ct.CustomerNotifySubscription.AsNoTracking().FirstOrDefaultAsync(x => x.Id == customernotifyevent.CustomerNotifySubscriptionId); - - //NOTE: There is no need to separate out future delivery and immediate delivery because - // All events have an event date, it's either immediate upon creation or it's future - // but not all events have an age value including ones with future event dates, - // and the default agevalue and advancenotice are both zero regardless so the block below works for either future or immediate deliveries - - var deliverAfter = customernotifyevent.EventDate + Subscription.AgeValue - Subscription.AdvanceNotice; - if (deliverAfter < DateTime.UtcNow) - { - //Do the delivery, it's kosher - await DeliverCustomerNotificationSMTP(customernotifyevent, Subscription, CustInfo.EmailAddress, ct); - } - } - } - #endregion customer notification events } catch (Exception ex) @@ -380,208 +330,14 @@ namespace AyaNova.Biz } - - - //=== - private static async Task DeliverCustomerNotificationSMTP(CustomerNotifyEvent ne, CustomerNotifySubscription subscription, string deliveryAddress, AyContext ct) - { - - var DeliveryLogItem = new NotifyDeliveryLog() - { - Processed = DateTime.UtcNow, - ObjectId = ne.ObjectId, - NotifySubscriptionId = ne.CustomerNotifySubscriptionId, - Fail = false - }; - - try - { - log.LogDebug($"DeliverCustomerNotificationSMTP delivering notify event: {ne}"); - if (string.IsNullOrWhiteSpace(deliveryAddress)) - { - DeliveryLogItem.Fail = true; - DeliveryLogItem.Error = $"No email address provided for smtp delivery; event: {ne}"; - } - else - { - IMailer m = AyaNova.Util.ServiceProviderProvider.Mailer; - if (!ServerGlobalOpsSettingsCache.Notify.SmtpDeliveryActive) - { - await NotifyEventHelper.AddOpsProblemEvent($"Email notifications are set to OFF at server, unable to send Customer email notification for this event:{ne}"); - log.LogInformation($"** WARNING: SMTP notification is currently set to Active=False; unable to deliver Customer email notification, [CustomerId={ne.CustomerId}, Customer Notify subscription={ne.CustomerNotifySubscriptionId}]. Change this setting or remove all Customer notifications if this is permanent **"); - DeliveryLogItem.Fail = true; - DeliveryLogItem.Error = $"Email notifications are set to OFF at server, unable to send Customer email notification for this event: {ne}"; - } - else - { - //generate report if applicable - - if (subscription.LinkReportId != null) - { - long subTranslationId = (long)subscription.TranslationId; - - ReportBiz biz = new ReportBiz(ct, 1, subTranslationId, AuthorizationRoles.BizAdmin); - //example with workorder report - //{"AType":34,"selectedRowIds":[355],"ReportId":9,"ClientMeta":{"UserName":"AyaNova SuperUser","Authorization":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIxNjQ2NzgyNTc4IiwiaXNzIjoiYXlhbm92YS5jb20iLCJpZCI6IjEifQ.ad7Acq54JCRGitDWKDJFFnqKkidbdaKaFmj-RA_RG5E","DownloadToken":"NdoU8ca3LG4L39Tj2oi3UReeeM7FLevTgbgopTPhGbA","TimeZoneName":"America/Los_Angeles","LanguageName":"en-US","Hour12":true,"CurrencyName":"USD","DefaultLocale":"en","PDFDate":"3/3/22","PDFTime":"3:38 PM"}} - - var reportRequest = new DataListReportRequest(); - - reportRequest.AType = ne.AyaType; - reportRequest.ReportId = (long)subscription.LinkReportId; - reportRequest.SelectedRowIds = new long[] { ne.ObjectId }; - var jwt = Api.Controllers.AuthController.GenRpt(subTranslationId); - - //this could be adjusted by culture if we allow user to set a culture but that's getting a bit into the weeds, likely the server default is fine - var pdfDate = new DateTime().ToShortDateString(); - var pdfTime = new DateTime().ToShortTimeString(); - var h12 = subscription.Hour12 ? "true" : "false"; - reportRequest.ClientMeta = JToken.Parse($"{{'UserName':'-','Authorization':'Bearer {jwt}','TimeZoneName':'{subscription.TimeZoneOverride}','LanguageName':'{subscription.LanguageOverride}','Hour12':{h12},'CurrencyName':'{subscription.CurrencyName}','DefaultLocale':'en','PDFDate':'{pdfDate}','PDFTime':'{pdfTime}'}}"); - //get port number - var match = System.Text.RegularExpressions.Regex.Match(ServerBootConfig.AYANOVA_USE_URLS, "[0-9]+"); - var API_URL = $"http://127.0.0.1:{match.Value}/api/{AyaNovaVersion.CurrentApiVersion}/"; - var jobid = await biz.RequestRenderReport(reportRequest, DateTime.UtcNow.AddMinutes(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT), API_URL, "CUSTOMER NOTIFICATION - NO USER"); - if (jobid == null) - { - throw new ApplicationException($"Report render job id is null failed to start"); - } - else - { - bool done = false; - DateTime bailAfter = DateTime.Now.AddMinutes(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT); - while (!done && DateTime.Now < bailAfter) - { - var status = await JobsBiz.GetJobStatusAsync((Guid)jobid); - switch (status) - { - case JobStatus.Completed: - { - done = true; - //get job logs and parse file name from it - JobOperationsBiz jobopsbiz = new JobOperationsBiz(ct, 1, AuthorizationRoles.BizAdmin); - List log = await jobopsbiz.GetJobLogListAsync((Guid)jobid); - var lastLog = log[log.Count - 1]; - var lastLogJ = JObject.Parse(lastLog.StatusText); - var path = (string)lastLogJ["reportfilename"]; - var FilePath = FileUtil.GetFullPathForTemporaryFile(path); - await m.SendEmailAsync(deliveryAddress, ne.Subject, ne.Message, ServerGlobalOpsSettingsCache.Notify, FilePath); - break; - } - case JobStatus.Failed: - case JobStatus.Absent: - throw new ApplicationException($"REPORT RENDER JOB {jobid} started but failed"); - - } - - } - if (!done) - throw new TimeoutException("JOB FAILED DUE TO REPORT RENDER TIMEOUT"); - } - } - else - await m.SendEmailAsync(deliveryAddress, ne.Subject, ne.Message, ServerGlobalOpsSettingsCache.Notify); - - } - - } - } - catch (Exception ex) - { - await NotifyEventHelper.AddOpsProblemEvent("SMTP Customer Notification failed", ex); - DeliveryLogItem.Fail = true; - DeliveryLogItem.Error = $"SMTP Notification failed to deliver for this Customer notify event: {ne}, message: {ex.Message}"; - log.LogDebug(ex, $"DeliverSMTP Failure delivering Customer notify event: {ne}"); - } - finally - { - //remove event no matter what - ct.CustomerNotifyEvent.Remove(ne); - - //add delivery log item - await ct.NotifyDeliveryLog.AddAsync(DeliveryLogItem); - await ct.SaveChangesAsync(); - } - } - //=== - - //Called from ops notification settings to test smtp setup by delivering to address of choosing public static async Task TestSMTPDelivery(string toAddress) { - //DO TEST DELIVERY HERE USING EXACT SAME SETTINGS AS FOR DeliverSMTP above - //todo: abstract out email sending to it's own class maybe or whatever method I choose supports the best - //https://jasonwatmore.com/post/2020/07/15/aspnet-core-3-send-emails-via-smtp-with-mailkit - //https://medium.com/@ffimnsr/sending-email-using-mailkit-in-asp-net-core-web-api-71b946380442 - IMailer m = AyaNova.Util.ServiceProviderProvider.Mailer; try { -#if (DEBUG) - //generate a workorder report here get the path and send it with the message - using (AyContext ct = AyaNova.Util.ServiceProviderProvider.DBContext) - { - long overrideLanguageId = 2; - - ReportBiz biz = new ReportBiz(ct, 1, overrideLanguageId, AuthorizationRoles.BizAdmin); - //example with workorder report - //{"AType":34,"selectedRowIds":[355],"ReportId":9,"ClientMeta":{"UserName":"AyaNova SuperUser","Authorization":"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOiIxNjQ2NzgyNTc4IiwiaXNzIjoiYXlhbm92YS5jb20iLCJpZCI6IjEifQ.ad7Acq54JCRGitDWKDJFFnqKkidbdaKaFmj-RA_RG5E","DownloadToken":"NdoU8ca3LG4L39Tj2oi3UReeeM7FLevTgbgopTPhGbA","TimeZoneName":"America/Los_Angeles","LanguageName":"en-US","Hour12":true,"CurrencyName":"USD","DefaultLocale":"en","PDFDate":"3/3/22","PDFTime":"3:38 PM"}} - - var reportRequest = new DataListReportRequest(); - reportRequest.AType = AyaType.WorkOrder; - reportRequest.ReportId = 9; - reportRequest.SelectedRowIds = new long[] { 1 }; - var jwt = Api.Controllers.AuthController.GenRpt(overrideLanguageId); - - //this could be adjusted by culture if we allow user to set a culture but that's getting a bit into the weeds, likely the server default is fine - var pdfDate = new DateTime().ToShortDateString(); - var pdfTime = new DateTime().ToShortTimeString(); - - reportRequest.ClientMeta = JToken.Parse($"{{'UserName':'-','Authorization':'Bearer {jwt}','TimeZoneName':'America/Los_Angeles','LanguageName':'en-US','Hour12':true,'CurrencyName':'USD','DefaultLocale':'en','PDFDate':'{pdfDate}','PDFTime':'{pdfTime}'}}"); - //get port number - var match = System.Text.RegularExpressions.Regex.Match(ServerBootConfig.AYANOVA_USE_URLS, "[0-9]+"); - var API_URL = $"http://127.0.0.1:{match.Value}/api/{AyaNovaVersion.CurrentApiVersion}/"; - var jobid = await biz.RequestRenderReport(reportRequest, DateTime.UtcNow.AddMinutes(ServerBootConfig.AYANOVA_REPORT_RENDERING_TIMEOUT), API_URL, "AUTOMATED NO USER"); - if (jobid == null) - { - return "JOB FAILED"; - } - else - { - bool done = false; - //todo: need a timeout here that sets done as failed due to timeout - while (!done) - { - var status = await JobsBiz.GetJobStatusAsync((Guid)jobid); - switch (status) - { - case JobStatus.Completed: - { - //get job logs and parse file name from it - JobOperationsBiz jobopsbiz = new JobOperationsBiz(ct, 1, AuthorizationRoles.BizAdmin); - List log = await jobopsbiz.GetJobLogListAsync((Guid)jobid); - var lastLog = log[log.Count - 1]; - var lastLogJ = JObject.Parse(lastLog.StatusText); - var path = (string)lastLogJ["reportfilename"]; - var FilePath = FileUtil.GetFullPathForTemporaryFile(path); - await m.SendEmailAsync(toAddress, "DEBUG Test from Notification system", "This is a DEBUG test to confirm notification system is working", ServerGlobalOpsSettingsCache.Notify, FilePath); - return "ok"; - } - case JobStatus.Failed: - return $"JOB {jobid} started but failed"; - } - if (status == JobStatus.Failed) - { - return $"JOB {jobid} started but failed"; - } - } - return "JOB FAILED DUE TO TIMEOUT"; - } - } - -#else await m.SendEmailAsync(toAddress, "Test from Notification system", "This is a test to confirm notification system is working", ServerGlobalOpsSettingsCache.Notify); return "ok"; -#endif - } catch (Exception ex) { @@ -593,7 +349,6 @@ namespace AyaNova.Biz //Open object url - //### NOTE: If this is required anywhere else, move it to a central BizUtils class in Biz folder callable from here and there private static string OpenObjectUrlBuilder(AyaType aType, long id, NotifyEventType net) { var ServerUrl = ServerGlobalOpsSettingsCache.Notify.AyaNovaServerURL; @@ -629,21 +384,6 @@ namespace AyaNova.Biz } - // //Used to directly open a report at client - // private static string OpenReportUrlBuilder(long objectId, long reportId) - // { - // //CLIENT EXPECTS: open report links to have a query string [CLIENTAPPURL]/viewreport/[objectid]/[reportid] - // var ServerUrl = ServerGlobalOpsSettingsCache.Notify.AyaNovaServerURL; - // if (string.IsNullOrWhiteSpace(ServerUrl)) - // { - // NotifyEventHelper.AddOpsProblemEvent("Notification system: The OPS Notification setting is empty for AyaNova Server URL. This prevents Notification system from linking events to openable objects.").Wait(); - // return "OPS ERROR NO SERVER URL CONFIGURED"; - // } - // ServerUrl = ServerUrl.Trim().TrimEnd('/'); - // return $"{ServerUrl}/viewreport/{objectId}/{reportId}"; - // } - - //url to open subscription for editing private static string OpenSubscriptionUrlBuilder(long id)