case 4654 - every direct notification message to a user is delivered
Direct messages are general notifications with no object, so the 12-hour repeat check in CoreJobNotify (case 3917, keyed on subscription and object) dropped every message to a user after the first. For general notifications the check now applies only to the job's own delivery error messages (DELIVERY_ERROR_NAME). Changelog. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -205,7 +205,7 @@ John's "next release" cases for AyaNova (exported 2026-10-24). Each is its own s
|
||||
| 4735 | Roles audit: every documented role right against `BizRoles.cs` (found in 4719, see Deferred) | 00 |
|
||||
| 4692 | SuperUser can be deleted | 00. **Done** (`UserBiz.ValidateCanDelete` refuses user 1 with 2004 before any other check; the integration test `UserWithActivityShouldNotBeDeleteable`, which deleted SuperUser on a database where it had no related records and so was excluded from every run, is now `SuperUserShouldNotBeDeleteable` and runs with the rest: full suite = baseline, SuperUser still logs in afterwards; changelog) |
|
||||
| 4693 | FormFieldReference start-up race | 00. **Done** (both lazily built lists, `FormFieldReferenceList` and `FormFieldKeys`, are built in a local and published when complete, fields volatile; three restarts each followed at once by the full suite: baseline every time, no "is not valid" in the log) |
|
||||
| 4654 | General notification delivers at most once per 12 hours (circuit breaker) | 00 |
|
||||
| 4654 | General notification delivers at most once per 12 hours (circuit breaker) | 00. **Done** (direct messages are general notifications with no object, so the 12-hour repeat check in `CoreJobNotify` (case 3917, keyed on subscription and object) dropped every message to a user after the first; for general notifications the check now applies only to the job's own delivery error messages (`DELIVERY_ERROR_NAME`, "Error"); integration test `DirectMessage_SendAndDelete` un-skipped and now sends two messages: before, only the first arrived; after, both; full suite = baseline; changelog) |
|
||||
| 4694 | Standalone launcher not shutting down PostgreSQL (raven-launcher) | 1 |
|
||||
| 4695 | Shipping the Visual C++ runtime DLLs with the standalone install | 1 |
|
||||
| 4696 | JWT secret missing from the standalone config.json | 1. Resolved by 4711's server change; confirm on a fresh standalone install |
|
||||
|
||||
@@ -32,6 +32,7 @@ IN PROGRESS
|
||||
- Server: while a report renders, its template no longer holds the User's actual access token or download key; the AyaNova server adds the User's access only to the report's requests to the `ayApiUrl` address. Templates that call the AyaNova API through any other address need to be changed. See [API usage in reports](ay-report-edit.md#api-usage).
|
||||
- Server: users with the "Business administration - restricted" role can view [report templates](adm-report-templates.md) but no longer create, edit or import them, as described for that [role](ay-biz-admin-roles.md). Give the full "Business administration" role to users who edit report templates.
|
||||
- Server: the SuperUser account can no longer be deleted.
|
||||
- Server: fixed direct notification messages to a user: only the first one within 12 hours was delivered, now every message is.
|
||||
- Server: the server information page at `/api/v8/` no longer shows license details.
|
||||
- Server: business logo uploads must be PNG, JPEG, GIF, WebP or SVG images, and need full rights to global settings. See [business logos](adm-global-logo.md).
|
||||
- Server: when a user's password is reset or set by an administrator, or their login is disabled, they are signed out and must sign in again.
|
||||
|
||||
@@ -19,6 +19,8 @@ namespace AyaNova.Biz
|
||||
{
|
||||
private static bool NotifyIsRunning = false;
|
||||
private static ILogger log = AyaNova.Util.ApplicationLogging.CreateLogger("CoreJobNotify");
|
||||
//case 4654: name of this job's own delivery error messages to a user, the only general notifications limited to one per 12 hours
|
||||
private const string DELIVERY_ERROR_NAME = "Error";
|
||||
private static DateTime lastRun = DateTime.MinValue;
|
||||
private static DateTime lastNotifyHealthCheckSentLocal = DateTime.MinValue;
|
||||
private static TimeSpan TS_24_HOURS = new TimeSpan(24, 0, 0);//used to ensure daily ops happen no more than that
|
||||
@@ -103,29 +105,31 @@ namespace AyaNova.Biz
|
||||
//Don't check for ones that are regular object based
|
||||
//which can and will properly send out the same notification regularly
|
||||
//(e.g. workorder status change into out of and back into the same staus)
|
||||
switch (notifyevent.EventType)
|
||||
//case 4654: a general notification is either a message a user sent (never repetitive by itself, and with no
|
||||
//object, so the check below took every message to a user after the first within 12 hours as a repeat) or
|
||||
//this job's own delivery error message (which can repeat); only the latter is checked
|
||||
var checkRepeat = notifyevent.EventType switch
|
||||
{
|
||||
case NotifyEventType.BackupStatus:
|
||||
case NotifyEventType.GeneralNotification:
|
||||
case NotifyEventType.ServerOperationsProblem:
|
||||
case NotifyEventType.PMGenerationFailed:
|
||||
{
|
||||
//check if we've just delivered this same thing in the last 12 hours which is the hard limit (case 3917)
|
||||
var twelvehoursago = DateTime.UtcNow - new TimeSpan(12, 0, 0);
|
||||
NotifyEventType.GeneralNotification => notifyevent.Name == DELIVERY_ERROR_NAME,
|
||||
NotifyEventType.BackupStatus or NotifyEventType.ServerOperationsProblem or NotifyEventType.PMGenerationFailed => true,
|
||||
_ => false
|
||||
};
|
||||
if (checkRepeat)
|
||||
{
|
||||
//check if we've just delivered this same thing in the last 12 hours which is the hard limit (case 3917)
|
||||
var twelvehoursago = DateTime.UtcNow - new TimeSpan(12, 0, 0);
|
||||
|
||||
//look for same delivery less than last12hours ago
|
||||
if (await ct.NotifyDeliveryLog.AnyAsync(z => z.Processed > twelvehoursago && z.NotifySubscriptionId == notifyevent.NotifySubscriptionId && z.ObjectId == notifyevent.ObjectId))
|
||||
{
|
||||
log.LogDebug($"Notification event will not be delivered: repetitive (server system event type and delivered at least once in the last 12 hours to this subscriber: {notifyevent})");
|
||||
ct.NotifyEvent.Remove(notifyevent);
|
||||
await ct.SaveChangesAsync();
|
||||
//look for same delivery less than last12hours ago
|
||||
if (await ct.NotifyDeliveryLog.AnyAsync(z => z.Processed > twelvehoursago && z.NotifySubscriptionId == notifyevent.NotifySubscriptionId && z.ObjectId == notifyevent.ObjectId))
|
||||
{
|
||||
log.LogDebug($"Notification event will not be delivered: repetitive (server system event type and delivered at least once in the last 12 hours to this subscriber: {notifyevent})");
|
||||
ct.NotifyEvent.Remove(notifyevent);
|
||||
await ct.SaveChangesAsync();
|
||||
#if (DEBUG)
|
||||
log.LogInformation($"DeliverInApp event will not be delivered: repetitive (server system event type and delivered at least once in the last 12 hours to this subscriber: {notifyevent})");
|
||||
log.LogInformation($"DeliverInApp event will not be delivered: repetitive (server system event type and delivered at least once in the last 12 hours to this subscriber: {notifyevent})");
|
||||
#endif
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
//Do the delivery, it's kosher
|
||||
@@ -201,7 +205,7 @@ namespace AyaNova.Biz
|
||||
log.LogDebug($"DeliverSMTP delivering notify event: {ne}");
|
||||
if (string.IsNullOrWhiteSpace(deliveryAddress))
|
||||
{
|
||||
await NotifyEventHelper.AddGeneralNotifyEvent(NotifyEventType.GeneralNotification, $"No email address is set in subscription to deliver email notification. This event will be removed from the delivery queue as undeliverable: {ne}", "Error", null, ne.UserId);
|
||||
await NotifyEventHelper.AddGeneralNotifyEvent(NotifyEventType.GeneralNotification, $"No email address is set in subscription to deliver email notification. This event will be removed from the delivery queue as undeliverable: {ne}", DELIVERY_ERROR_NAME, null, ne.UserId);
|
||||
DeliveryLogItem.Fail = true;
|
||||
DeliveryLogItem.Error = $"No email address provided for smtp delivery; event: {ne}";
|
||||
}
|
||||
@@ -301,7 +305,7 @@ namespace AyaNova.Biz
|
||||
|
||||
if (!ServerGlobalOpsSettingsCache.Notify.SmtpDeliveryActive)
|
||||
{
|
||||
await NotifyEventHelper.AddGeneralNotifyEvent(NotifyEventType.GeneralNotification, $"Email notifications are set to OFF at server, unable to send email notification for this event:{ne}", "Error", null, ne.UserId);
|
||||
await NotifyEventHelper.AddGeneralNotifyEvent(NotifyEventType.GeneralNotification, $"Email notifications are set to OFF at server, unable to send email notification for this event:{ne}", DELIVERY_ERROR_NAME, null, ne.UserId);
|
||||
log.LogInformation($"** WARNING: SMTP notification is currently set to Active=False; unable to deliver email notification, re-routed to in-app notification instead [UserId={ne.UserId}, Notify subscription={ne.NotifySubscriptionId}]. Change this setting or have users remove email delivery notifications if this is permanent **");
|
||||
DeliveryLogItem.Fail = true;
|
||||
DeliveryLogItem.Error = $"Email notifications are set to OFF at server, unable to send email notification for this event: {ne}";
|
||||
@@ -314,7 +318,7 @@ namespace AyaNova.Biz
|
||||
catch (Exception ex)
|
||||
{
|
||||
await NotifyEventHelper.AddOpsProblemEvent("SMTP Notification failed", ex);
|
||||
await NotifyEventHelper.AddGeneralNotifyEvent(NotifyEventType.GeneralNotification, $"An error prevented delivering the following notification via email. System operator users have been notified:{ne}", "Error", null, ne.UserId);
|
||||
await NotifyEventHelper.AddGeneralNotifyEvent(NotifyEventType.GeneralNotification, $"An error prevented delivering the following notification via email. System operator users have been notified:{ne}", DELIVERY_ERROR_NAME, null, ne.UserId);
|
||||
DeliveryLogItem.Fail = true;
|
||||
DeliveryLogItem.Error = $"SMTP Notification failed to deliver for this event: {ne}, message: {ex.Message}";
|
||||
log.LogDebug(ex, $"DeliverSMTP Failure delivering notify event: {ne}");
|
||||
|
||||
Reference in New Issue
Block a user