This commit is contained in:
6
.vscode/launch.json
vendored
6
.vscode/launch.json
vendored
@@ -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",
|
||||
|
||||
@@ -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");
|
||||
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
250
server/AyaNova/generator/CoreJobCustomerNotify.cs
Normal file
250
server/AyaNova/generator/CoreJobCustomerNotify.cs
Normal file
@@ -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
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// Notification processor
|
||||
/// turn notifyEvent records into inappnotification records for in app viewing and / or deliver smtp notifications seperately
|
||||
///
|
||||
/// </summary>
|
||||
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<JobOperationsLogInfoItem> 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
|
||||
|
||||
@@ -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<JobOperationsLogInfoItem> 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<string> 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<JobOperationsLogInfoItem> 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)
|
||||
|
||||
Reference in New Issue
Block a user