@rytass/bpm-core-nestjs-module 0.9.1 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +62 -0
- package/README.md +17 -5
- package/package.json +3 -3
- package/src/lib/attachment/attachment-options.js +7 -1
- package/src/lib/attachment/attachment-options.js.map +1 -1
- package/src/lib/attachment/attachment.service.js +7 -0
- package/src/lib/attachment/attachment.service.js.map +1 -1
- package/src/lib/bpm/bpm-root.module.d.ts +11 -0
- package/src/lib/bpm/bpm-root.module.js.map +1 -1
- package/src/lib/calendar/calendar.module.d.ts +12 -0
- package/src/lib/calendar/calendar.module.js.map +1 -1
- package/src/lib/common/filters/all-exceptions.filter.d.ts +11 -0
- package/src/lib/common/filters/all-exceptions.filter.js +35 -1
- package/src/lib/common/filters/all-exceptions.filter.js.map +1 -1
- package/src/lib/form-data-source/form-data-source-value-resolver.service.d.ts +6 -7
- package/src/lib/form-data-source/form-data-source-value-resolver.service.js +91 -291
- package/src/lib/form-data-source/form-data-source-value-resolver.service.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.queries.d.ts +24 -0
- package/src/lib/form-data-source/form-data-source.queries.js +179 -7
- package/src/lib/form-data-source/form-data-source.queries.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.service.d.ts +36 -21
- package/src/lib/form-data-source/form-data-source.service.js +143 -359
- package/src/lib/form-data-source/form-data-source.service.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.types.d.ts +17 -0
- package/src/lib/form-data-source/form-data-source.types.js.map +1 -1
- package/src/lib/form-data-source/form-data-source.validation.d.ts +78 -0
- package/src/lib/form-data-source/form-data-source.validation.js +409 -0
- package/src/lib/form-data-source/form-data-source.validation.js.map +1 -0
- package/src/lib/migrations/0000000021000-notification-silenced.d.ts +15 -0
- package/src/lib/migrations/0000000021000-notification-silenced.js +31 -0
- package/src/lib/migrations/0000000021000-notification-silenced.js.map +1 -0
- package/src/lib/migrations/index.d.ts +1 -0
- package/src/lib/migrations/index.js +3 -0
- package/src/lib/migrations/index.js.map +1 -1
- package/src/lib/notification/index.d.ts +1 -0
- package/src/lib/notification/index.js +1 -0
- package/src/lib/notification/index.js.map +1 -1
- package/src/lib/notification/notification-delivery.service.d.ts +26 -1
- package/src/lib/notification/notification-delivery.service.js +205 -20
- package/src/lib/notification/notification-delivery.service.js.map +1 -1
- package/src/lib/notification/notification-dispatcher.token.d.ts +13 -0
- package/src/lib/notification/notification-dispatcher.token.js.map +1 -1
- package/src/lib/notification/notification-observer.token.d.ts +6 -0
- package/src/lib/notification/notification-observer.token.js.map +1 -1
- package/src/lib/notification/notification-options.d.ts +43 -0
- package/src/lib/notification/notification-options.js +45 -4
- package/src/lib/notification/notification-options.js.map +1 -1
- package/src/lib/notification/notification-schedule.d.ts +41 -0
- package/src/lib/notification/notification-schedule.js +158 -0
- package/src/lib/notification/notification-schedule.js.map +1 -0
- package/src/lib/notification/notification.entity.d.ts +12 -0
- package/src/lib/notification/notification.entity.js +5 -0
- package/src/lib/notification/notification.entity.js.map +1 -1
- package/src/lib/notification/notification.service.d.ts +10 -0
- package/src/lib/notification/notification.service.js +100 -21
- package/src/lib/notification/notification.service.js.map +1 -1
- package/src/lib/template/index.d.ts +1 -0
- package/src/lib/template/index.js +1 -0
- package/src/lib/template/index.js.map +1 -1
- package/src/lib/template/template-observer.token.d.ts +67 -0
- package/src/lib/template/template-observer.token.js +16 -0
- package/src/lib/template/template-observer.token.js.map +1 -0
- package/src/lib/template/template.mutations.d.ts +1 -1
- package/src/lib/template/template.mutations.js +4 -3
- package/src/lib/template/template.mutations.js.map +1 -1
- package/src/lib/template/template.service.d.ts +18 -2
- package/src/lib/template/template.service.js +117 -17
- package/src/lib/template/template.service.js.map +1 -1
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.DEFAULT_BPM_NOTIFICATION_OPTIONS = exports.BPM_NOTIFICATION_OPTIONS = void 0;
|
|
4
4
|
exports.resolveBPMNotificationOptions = resolveBPMNotificationOptions;
|
|
5
|
+
const notification_schedule_1 = require("./notification-schedule");
|
|
5
6
|
const notification_enums_1 = require("./notification.enums");
|
|
6
7
|
exports.BPM_NOTIFICATION_OPTIONS = Symbol('BPM_NOTIFICATION_OPTIONS');
|
|
7
8
|
exports.DEFAULT_BPM_NOTIFICATION_OPTIONS = {
|
|
@@ -18,10 +19,13 @@ exports.DEFAULT_BPM_NOTIFICATION_OPTIONS = {
|
|
|
18
19
|
defaultEmailPreferenceEnabled: true,
|
|
19
20
|
defaultInAppPreferenceEnabled: true,
|
|
20
21
|
deliveryBatchSize: 25,
|
|
22
|
+
deliveryDispatchTimeoutMs: 30_000,
|
|
21
23
|
deliveryMaxAttempts: 3,
|
|
22
24
|
deliveryRetryBaseDelayMs: 60_000,
|
|
23
25
|
deliveryScanIntervalMs: 30_000,
|
|
24
26
|
deliverySchedulerEnabled: false,
|
|
27
|
+
emailDigestHour: notification_schedule_1.DEFAULT_EMAIL_DIGEST_HOUR,
|
|
28
|
+
quietHoursTimeZone: null,
|
|
25
29
|
slaBusinessCalendarTimeZone: 'UTC',
|
|
26
30
|
slaScanIntervalMs: 60_000,
|
|
27
31
|
slaSchedulerEnabled: false,
|
|
@@ -42,9 +46,11 @@ function resolveBPMNotificationOptions(options = {}) {
|
|
|
42
46
|
const emailFrom = normalizeText(options.notificationEmailFrom);
|
|
43
47
|
const webhookSigningSecret = normalizeText(options.notificationWebhookSigningSecret);
|
|
44
48
|
const webhookEndpointUrl = normalizeText(options.notificationWebhookEndpointUrl);
|
|
49
|
+
const slaBusinessCalendarTimeZone = normalizeTimeZone(options.notificationSlaBusinessCalendarTimeZone, exports.DEFAULT_BPM_NOTIFICATION_OPTIONS.slaBusinessCalendarTimeZone);
|
|
45
50
|
return {
|
|
46
51
|
deliveryScanIntervalMs: normalizeInterval(options.notificationDeliveryScanIntervalMs, exports.DEFAULT_BPM_NOTIFICATION_OPTIONS.deliveryScanIntervalMs),
|
|
47
52
|
deliveryBatchSize: normalizePositiveInteger(options.notificationDeliveryBatchSize, exports.DEFAULT_BPM_NOTIFICATION_OPTIONS.deliveryBatchSize),
|
|
53
|
+
deliveryDispatchTimeoutMs: normalizeTimeout(options.notificationDeliveryDispatchTimeoutMs, exports.DEFAULT_BPM_NOTIFICATION_OPTIONS.deliveryDispatchTimeoutMs),
|
|
48
54
|
deliveryMaxAttempts: normalizePositiveInteger(options.notificationDeliveryMaxAttempts, exports.DEFAULT_BPM_NOTIFICATION_OPTIONS.deliveryMaxAttempts),
|
|
49
55
|
deliveryRetryBaseDelayMs: normalizeInterval(options.notificationDeliveryRetryBaseDelayMs, exports.DEFAULT_BPM_NOTIFICATION_OPTIONS.deliveryRetryBaseDelayMs),
|
|
50
56
|
deliverySchedulerEnabled: options.notificationDeliverySchedulerEnabled ??
|
|
@@ -70,7 +76,13 @@ function resolveBPMNotificationOptions(options = {}) {
|
|
|
70
76
|
exports.DEFAULT_BPM_NOTIFICATION_OPTIONS.defaultEmailPreferenceEnabled,
|
|
71
77
|
defaultInAppPreferenceEnabled: options.notificationDefaultInAppPreferenceEnabled ??
|
|
72
78
|
exports.DEFAULT_BPM_NOTIFICATION_OPTIONS.defaultInAppPreferenceEnabled,
|
|
73
|
-
|
|
79
|
+
emailDigestHour: (0, notification_schedule_1.normalizeDigestHour)(options.notificationEmailDigestHour),
|
|
80
|
+
// Left null when unset so `NotificationService` can read the zone off the
|
|
81
|
+
// registered business calendar. Resolving it here would silently mean UTC
|
|
82
|
+
// for every host that supplies its own calendar — the calendar knows the
|
|
83
|
+
// zone, but it does not exist yet at option-flattening time.
|
|
84
|
+
quietHoursTimeZone: normalizeOptionalTimeZone(options.notificationQuietHoursTimeZone),
|
|
85
|
+
slaBusinessCalendarTimeZone,
|
|
74
86
|
slaScanIntervalMs: normalizeInterval(options.notificationSlaScanIntervalMs, exports.DEFAULT_BPM_NOTIFICATION_OPTIONS.slaScanIntervalMs),
|
|
75
87
|
slaSchedulerEnabled: options.notificationSlaSchedulerEnabled ??
|
|
76
88
|
exports.DEFAULT_BPM_NOTIFICATION_OPTIONS.slaSchedulerEnabled,
|
|
@@ -101,21 +113,39 @@ function normalizePort(value) {
|
|
|
101
113
|
? value
|
|
102
114
|
: null;
|
|
103
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* As {@link normalizeTimeZone}, but reports "not configured" as `null` instead
|
|
118
|
+
* of substituting a fallback. An unreadable zone is treated as absent, so the
|
|
119
|
+
* caller's own fallback applies rather than a silently wrong zone.
|
|
120
|
+
*/
|
|
121
|
+
function normalizeOptionalTimeZone(value) {
|
|
122
|
+
const trimmedValue = value?.trim() ?? '';
|
|
123
|
+
if (!trimmedValue) {
|
|
124
|
+
return null;
|
|
125
|
+
}
|
|
126
|
+
try {
|
|
127
|
+
new Intl.DateTimeFormat('en-CA', { timeZone: trimmedValue });
|
|
128
|
+
return trimmedValue;
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
return null;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
104
134
|
/**
|
|
105
135
|
* Rejects unknown IANA zones at wiring time rather than letting every SLA
|
|
106
136
|
* calculation throw later inside `Intl.DateTimeFormat`.
|
|
107
137
|
*/
|
|
108
|
-
function normalizeTimeZone(value) {
|
|
138
|
+
function normalizeTimeZone(value, fallback) {
|
|
109
139
|
const trimmedValue = value?.trim() ?? '';
|
|
110
140
|
if (!trimmedValue) {
|
|
111
|
-
return
|
|
141
|
+
return fallback;
|
|
112
142
|
}
|
|
113
143
|
try {
|
|
114
144
|
new Intl.DateTimeFormat('en-CA', { timeZone: trimmedValue });
|
|
115
145
|
return trimmedValue;
|
|
116
146
|
}
|
|
117
147
|
catch {
|
|
118
|
-
return
|
|
148
|
+
return fallback;
|
|
119
149
|
}
|
|
120
150
|
}
|
|
121
151
|
function normalizeInterval(value, fallback) {
|
|
@@ -124,6 +154,17 @@ function normalizeInterval(value, fallback) {
|
|
|
124
154
|
}
|
|
125
155
|
return Math.max(value, 1000);
|
|
126
156
|
}
|
|
157
|
+
/**
|
|
158
|
+
* Unlike {@link normalizeInterval} this accepts `0`, which means "wait
|
|
159
|
+
* indefinitely" for a dispatch, and imposes no 1000ms floor: a host may want a
|
|
160
|
+
* tight timeout without that being mistaken for a scan loop.
|
|
161
|
+
*/
|
|
162
|
+
function normalizeTimeout(value, fallback) {
|
|
163
|
+
if (typeof value !== 'number' || !Number.isInteger(value) || value < 0) {
|
|
164
|
+
return fallback;
|
|
165
|
+
}
|
|
166
|
+
return value;
|
|
167
|
+
}
|
|
127
168
|
function normalizePositiveInteger(value, fallback) {
|
|
128
169
|
if (typeof value !== 'number' || !Number.isInteger(value) || value <= 0) {
|
|
129
170
|
return fallback;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification-options.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/notification/notification-options.ts"],"names":[],"mappings":";;;
|
|
1
|
+
{"version":3,"file":"notification-options.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/notification/notification-options.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { NotificationDigestModeEnum } from './notification.enums';
|
|
2
|
+
export interface NotificationQuietHours {
|
|
3
|
+
readonly quietHoursEnd: string | null;
|
|
4
|
+
readonly quietHoursStart: string | null;
|
|
5
|
+
readonly timeZone: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Whether `at` falls inside the recipient's quiet hours.
|
|
9
|
+
*
|
|
10
|
+
* A window whose start equals its end is treated as **no** quiet hours rather
|
|
11
|
+
* than as all day: the destructive reading of an ambiguous setting is the one
|
|
12
|
+
* that silently swallows every notification.
|
|
13
|
+
*/
|
|
14
|
+
export declare function isWithinQuietHours(at: Date, { quietHoursEnd, quietHoursStart, timeZone }: NotificationQuietHours): boolean;
|
|
15
|
+
/**
|
|
16
|
+
* The instant the current quiet period ends, or `null` when `at` is not inside
|
|
17
|
+
* one.
|
|
18
|
+
*/
|
|
19
|
+
export declare function resolveQuietHoursEnd(at: Date, quietHours: NotificationQuietHours): Date | null;
|
|
20
|
+
export interface EmailReleaseOptions extends NotificationQuietHours {
|
|
21
|
+
/** Local hour at which a `DAILY` recipient's held email is flushed. */
|
|
22
|
+
readonly digestHour: number;
|
|
23
|
+
readonly digestMode: NotificationDigestModeEnum;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* When an email notification created at `at` may actually be sent, or `null`
|
|
27
|
+
* when it may go out immediately.
|
|
28
|
+
*
|
|
29
|
+
* The returned instant is written to the row's `nextRetryAt`, which is exactly
|
|
30
|
+
* what the delivery scan already treats as "not before": deferring reuses the
|
|
31
|
+
* retry machinery instead of adding a second scheduler, and means quiet hours
|
|
32
|
+
* *delay* a notification rather than drop it.
|
|
33
|
+
*/
|
|
34
|
+
export declare function resolveEmailReleaseAt(at: Date, options: EmailReleaseOptions): Date | null;
|
|
35
|
+
export declare const DEFAULT_EMAIL_DIGEST_HOUR = 9;
|
|
36
|
+
/**
|
|
37
|
+
* Last-resort zone for quiet hours, used only when neither
|
|
38
|
+
* `notificationQuietHoursTimeZone` nor a registered business calendar answers.
|
|
39
|
+
*/
|
|
40
|
+
export declare const DEFAULT_QUIET_HOURS_TIME_ZONE = "UTC";
|
|
41
|
+
export declare function normalizeDigestHour(value: number | undefined): number;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DEFAULT_QUIET_HOURS_TIME_ZONE = exports.DEFAULT_EMAIL_DIGEST_HOUR = void 0;
|
|
4
|
+
exports.isWithinQuietHours = isWithinQuietHours;
|
|
5
|
+
exports.resolveQuietHoursEnd = resolveQuietHoursEnd;
|
|
6
|
+
exports.resolveEmailReleaseAt = resolveEmailReleaseAt;
|
|
7
|
+
exports.normalizeDigestHour = normalizeDigestHour;
|
|
8
|
+
const notification_enums_1 = require("./notification.enums");
|
|
9
|
+
const MINUTES_PER_DAY = 24 * 60;
|
|
10
|
+
const MS_PER_MINUTE = 60_000;
|
|
11
|
+
const MS_PER_DAY = 24 * 60 * MS_PER_MINUTE;
|
|
12
|
+
/**
|
|
13
|
+
* A `time` column value as stored by `notification_preferences`, expressed as
|
|
14
|
+
* minutes since local midnight. `null` when the preference is unset or holds a
|
|
15
|
+
* value the DTO pattern would have rejected — a stored value BPM cannot read
|
|
16
|
+
* must degrade to "no quiet hours" rather than throw during delivery.
|
|
17
|
+
*/
|
|
18
|
+
function parseTimeOfDay(value) {
|
|
19
|
+
const match = /^([01]\d|2[0-3]):([0-5]\d)(?::[0-5]\d)?$/.exec(value?.trim() ?? '');
|
|
20
|
+
if (!match) {
|
|
21
|
+
return null;
|
|
22
|
+
}
|
|
23
|
+
return Number(match[1]) * 60 + Number(match[2]);
|
|
24
|
+
}
|
|
25
|
+
function readZonedWallClock(at, timeZone) {
|
|
26
|
+
const parts = new Intl.DateTimeFormat('en-CA', {
|
|
27
|
+
day: '2-digit',
|
|
28
|
+
hour: '2-digit',
|
|
29
|
+
hour12: false,
|
|
30
|
+
minute: '2-digit',
|
|
31
|
+
month: '2-digit',
|
|
32
|
+
timeZone,
|
|
33
|
+
year: 'numeric',
|
|
34
|
+
}).formatToParts(at);
|
|
35
|
+
const read = (type) => Number(parts.find((part) => part.type === type)?.value ?? '0');
|
|
36
|
+
// `hourCycle: h23` is what `hour12: false` resolves to in every runtime this
|
|
37
|
+
// package supports, but Node has historically rendered midnight as `24`.
|
|
38
|
+
const hour = read('hour');
|
|
39
|
+
return {
|
|
40
|
+
day: read('day'),
|
|
41
|
+
hour: hour === 24 ? 0 : hour,
|
|
42
|
+
minute: read('minute'),
|
|
43
|
+
month: read('month'),
|
|
44
|
+
year: read('year'),
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
/** How far ahead of UTC the zone's local clock runs at `at`, in milliseconds. */
|
|
48
|
+
function readZoneOffsetMs(at, timeZone) {
|
|
49
|
+
const wallClock = readZonedWallClock(at, timeZone);
|
|
50
|
+
return (Date.UTC(wallClock.year, wallClock.month - 1, wallClock.day, wallClock.hour, wallClock.minute) -
|
|
51
|
+
// The formatter drops seconds and milliseconds, so compare against an
|
|
52
|
+
// instant truncated the same way.
|
|
53
|
+
Math.floor(at.getTime() / MS_PER_MINUTE) * MS_PER_MINUTE);
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* The instant at which the zone's local clock reads the given wall-clock time.
|
|
57
|
+
*
|
|
58
|
+
* Resolved in two passes because the offset to apply is itself a function of
|
|
59
|
+
* the instant: the first pass guesses using the offset in force at the naive
|
|
60
|
+
* UTC reading, the second re-reads the offset at that guess. That settles
|
|
61
|
+
* every case except a wall-clock time inside a spring-forward gap, which does
|
|
62
|
+
* not exist locally and lands on the instant the clock jumps to.
|
|
63
|
+
*/
|
|
64
|
+
function zonedWallClockToInstant(wallClock, timeZone) {
|
|
65
|
+
const naiveUtc = Date.UTC(wallClock.year, wallClock.month - 1, wallClock.day, wallClock.hour, wallClock.minute);
|
|
66
|
+
const firstGuess = new Date(naiveUtc - readZoneOffsetMs(new Date(naiveUtc), timeZone));
|
|
67
|
+
return new Date(naiveUtc - readZoneOffsetMs(firstGuess, timeZone));
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* The first instant at or after `at` whose local clock reads
|
|
71
|
+
* `minuteOfDay`. Returns `at` itself when it already lands exactly there.
|
|
72
|
+
*/
|
|
73
|
+
function nextLocalTimeOfDay(at, timeZone, minuteOfDay) {
|
|
74
|
+
const wallClock = readZonedWallClock(at, timeZone);
|
|
75
|
+
const sameDay = zonedWallClockToInstant({
|
|
76
|
+
...wallClock,
|
|
77
|
+
hour: Math.floor(minuteOfDay / 60),
|
|
78
|
+
minute: minuteOfDay % 60,
|
|
79
|
+
}, timeZone);
|
|
80
|
+
if (sameDay.getTime() >= at.getTime()) {
|
|
81
|
+
return sameDay;
|
|
82
|
+
}
|
|
83
|
+
// Step a day forward through the instant rather than the calendar so a
|
|
84
|
+
// month or year boundary needs no special case, then re-anchor the local
|
|
85
|
+
// time in case the offset changed overnight.
|
|
86
|
+
const nextDay = readZonedWallClock(new Date(sameDay.getTime() + MS_PER_DAY), timeZone);
|
|
87
|
+
return zonedWallClockToInstant({
|
|
88
|
+
...nextDay,
|
|
89
|
+
hour: Math.floor(minuteOfDay / 60),
|
|
90
|
+
minute: minuteOfDay % 60,
|
|
91
|
+
}, timeZone);
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Whether `at` falls inside the recipient's quiet hours.
|
|
95
|
+
*
|
|
96
|
+
* A window whose start equals its end is treated as **no** quiet hours rather
|
|
97
|
+
* than as all day: the destructive reading of an ambiguous setting is the one
|
|
98
|
+
* that silently swallows every notification.
|
|
99
|
+
*/
|
|
100
|
+
function isWithinQuietHours(at, { quietHoursEnd, quietHoursStart, timeZone }) {
|
|
101
|
+
const start = parseTimeOfDay(quietHoursStart);
|
|
102
|
+
const end = parseTimeOfDay(quietHoursEnd);
|
|
103
|
+
if (start === null || end === null || start === end) {
|
|
104
|
+
return false;
|
|
105
|
+
}
|
|
106
|
+
const wallClock = readZonedWallClock(at, timeZone);
|
|
107
|
+
const minuteOfDay = wallClock.hour * 60 + wallClock.minute;
|
|
108
|
+
return start < end
|
|
109
|
+
? minuteOfDay >= start && minuteOfDay < end
|
|
110
|
+
: // A window that wraps past midnight (22:00–08:00) is the common case.
|
|
111
|
+
minuteOfDay >= start || minuteOfDay < end;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The instant the current quiet period ends, or `null` when `at` is not inside
|
|
115
|
+
* one.
|
|
116
|
+
*/
|
|
117
|
+
function resolveQuietHoursEnd(at, quietHours) {
|
|
118
|
+
if (!isWithinQuietHours(at, quietHours)) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const end = parseTimeOfDay(quietHours.quietHoursEnd);
|
|
122
|
+
return end === null
|
|
123
|
+
? null
|
|
124
|
+
: nextLocalTimeOfDay(at, quietHours.timeZone, end % MINUTES_PER_DAY);
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* When an email notification created at `at` may actually be sent, or `null`
|
|
128
|
+
* when it may go out immediately.
|
|
129
|
+
*
|
|
130
|
+
* The returned instant is written to the row's `nextRetryAt`, which is exactly
|
|
131
|
+
* what the delivery scan already treats as "not before": deferring reuses the
|
|
132
|
+
* retry machinery instead of adding a second scheduler, and means quiet hours
|
|
133
|
+
* *delay* a notification rather than drop it.
|
|
134
|
+
*/
|
|
135
|
+
function resolveEmailReleaseAt(at, options) {
|
|
136
|
+
const digestAt = options.digestMode === notification_enums_1.NotificationDigestModeEnum.DAILY
|
|
137
|
+
? nextLocalTimeOfDay(at, options.timeZone, normalizeDigestHour(options.digestHour) * 60)
|
|
138
|
+
: at;
|
|
139
|
+
// Re-checked at the digest boundary, not at `at`: a digest hour that itself
|
|
140
|
+
// falls inside the quiet window must still wait for the window to close.
|
|
141
|
+
const releaseAt = resolveQuietHoursEnd(digestAt, options) ?? digestAt;
|
|
142
|
+
return releaseAt.getTime() > at.getTime() ? releaseAt : null;
|
|
143
|
+
}
|
|
144
|
+
exports.DEFAULT_EMAIL_DIGEST_HOUR = 9;
|
|
145
|
+
/**
|
|
146
|
+
* Last-resort zone for quiet hours, used only when neither
|
|
147
|
+
* `notificationQuietHoursTimeZone` nor a registered business calendar answers.
|
|
148
|
+
*/
|
|
149
|
+
exports.DEFAULT_QUIET_HOURS_TIME_ZONE = 'UTC';
|
|
150
|
+
function normalizeDigestHour(value) {
|
|
151
|
+
return typeof value === 'number' &&
|
|
152
|
+
Number.isInteger(value) &&
|
|
153
|
+
value >= 0 &&
|
|
154
|
+
value <= 23
|
|
155
|
+
? value
|
|
156
|
+
: exports.DEFAULT_EMAIL_DIGEST_HOUR;
|
|
157
|
+
}
|
|
158
|
+
//# sourceMappingURL=notification-schedule.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"notification-schedule.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/notification/notification-schedule.ts"],"names":[],"mappings":"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"}
|
|
@@ -19,6 +19,18 @@ export declare class NotificationEntity {
|
|
|
19
19
|
* default list without deleting the record, so statistics and audits keep it.
|
|
20
20
|
*/
|
|
21
21
|
archivedAt: Date | null;
|
|
22
|
+
/**
|
|
23
|
+
* The row was recorded but deliberately not announced, because the
|
|
24
|
+
* recipient's preferences asked for quiet — `inAppEnabled: false`, or the
|
|
25
|
+
* notification arriving inside their quiet hours.
|
|
26
|
+
*
|
|
27
|
+
* BPM keeps writing the row either way: "do not interrupt me" and "do not
|
|
28
|
+
* keep a record" are different requests, and a notification centre that
|
|
29
|
+
* silently loses an approval task is the worse failure. Hosts driving a
|
|
30
|
+
* realtime channel should skip the toast or push for a silenced row and
|
|
31
|
+
* style it as read-later in the list.
|
|
32
|
+
*/
|
|
33
|
+
silenced: boolean;
|
|
22
34
|
attemptCount: number;
|
|
23
35
|
lastAttemptAt: Date | null;
|
|
24
36
|
nextRetryAt: Date | null;
|
|
@@ -105,6 +105,11 @@ tslib_1.__decorate([
|
|
|
105
105
|
(0, graphql_1.Field)(() => Date, { nullable: true }),
|
|
106
106
|
tslib_1.__metadata("design:type", Object)
|
|
107
107
|
], NotificationEntity.prototype, "archivedAt", void 0);
|
|
108
|
+
tslib_1.__decorate([
|
|
109
|
+
(0, typeorm_1.Column)('boolean', { name: 'silenced', default: false }),
|
|
110
|
+
(0, graphql_1.Field)(),
|
|
111
|
+
tslib_1.__metadata("design:type", Boolean)
|
|
112
|
+
], NotificationEntity.prototype, "silenced", void 0);
|
|
108
113
|
tslib_1.__decorate([
|
|
109
114
|
(0, typeorm_1.Column)('integer', { name: 'attempt_count', default: 0 }),
|
|
110
115
|
(0, graphql_1.Field)(() => Number),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"notification.entity.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/notification/notification.entity.ts"],"names":[],"mappings":";;;;AAAA,6CAAwD;AACxD,qCAMiB;AACjB,6DAK8B;AAE9B,MAAM,6BAA6B,GACjC,IAAI,GAAG,CAAC;IACN,yCAAoB,CAAC,aAAa;IAClC,yCAAoB,CAAC,gBAAgB;CACtC,CAAC,CAAC;AASE,IAAM,kBAAkB,GAAxB,MAAM,kBAAkB;IAAxB;
|
|
1
|
+
{"version":3,"file":"notification.entity.js","sourceRoot":"","sources":["../../../../../../libs/bpm-core/src/lib/notification/notification.entity.ts"],"names":[],"mappings":"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"}
|
|
@@ -138,6 +138,16 @@ export declare class NotificationService {
|
|
|
138
138
|
* approval.
|
|
139
139
|
*/
|
|
140
140
|
private notifyObserver;
|
|
141
|
+
/**
|
|
142
|
+
* The zone the recipients' bare `HH:mm` quiet-hours preferences are read in.
|
|
143
|
+
*
|
|
144
|
+
* Falls back to the registered business calendar rather than to UTC: the
|
|
145
|
+
* calendar already states where the host's people are, and a host that
|
|
146
|
+
* supplied one would otherwise get quiet hours silently offset by its whole
|
|
147
|
+
* UTC offset. Resolved here rather than in the flattened options because the
|
|
148
|
+
* calendar is a DI provider that does not exist at wiring time.
|
|
149
|
+
*/
|
|
150
|
+
private readQuietHoursTimeZone;
|
|
141
151
|
private readHostObserver;
|
|
142
152
|
private recordSlaActivity;
|
|
143
153
|
private runSlaTimeoutHook;
|
|
@@ -8,6 +8,7 @@ const common_1 = require("@nestjs/common");
|
|
|
8
8
|
const core_1 = require("@nestjs/core");
|
|
9
9
|
const typeorm_1 = require("@nestjs/typeorm");
|
|
10
10
|
const typeorm_2 = require("typeorm");
|
|
11
|
+
const business_calendar_token_1 = require("../calendar/business-calendar.token");
|
|
11
12
|
const iso_duration_1 = require("../common/iso-duration");
|
|
12
13
|
const activity_log_entity_1 = require("../workflow-engine/activity-log.entity");
|
|
13
14
|
const approval_instance_entity_1 = require("../workflow-engine/approval-instance.entity");
|
|
@@ -18,6 +19,7 @@ const workflow_engine_tokens_1 = require("../workflow-engine/workflow-engine.tok
|
|
|
18
19
|
const workflow_engine_enums_1 = require("../workflow-engine/workflow-engine.enums");
|
|
19
20
|
const notification_delivery_service_1 = require("./notification-delivery.service");
|
|
20
21
|
const notification_preference_entity_1 = require("./notification-preference.entity");
|
|
22
|
+
const notification_schedule_1 = require("./notification-schedule");
|
|
21
23
|
const notification_entity_1 = require("./notification.entity");
|
|
22
24
|
const notification_enums_1 = require("./notification.enums");
|
|
23
25
|
const ACTIONABLE_NOTIFICATION_TYPES = [
|
|
@@ -507,27 +509,44 @@ let NotificationService = NotificationService_1 = class NotificationService {
|
|
|
507
509
|
}, Promise.resolve(0));
|
|
508
510
|
return createdNotifications > 0;
|
|
509
511
|
}
|
|
510
|
-
async createNotifications(input, manager) {
|
|
512
|
+
async createNotifications(input, manager, now = new Date()) {
|
|
511
513
|
const preference = await this.getPreference(input.recipientMemberId);
|
|
512
514
|
const repository = manager
|
|
513
515
|
? manager.getRepository(notification_entity_1.NotificationEntity)
|
|
514
516
|
: this.notificationRepository;
|
|
515
|
-
const
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
517
|
+
const quietHoursTimeZone = this.readQuietHoursTimeZone();
|
|
518
|
+
const notifications = input.channels.flatMap((channel) => {
|
|
519
|
+
const delivery = resolveChannelDelivery({
|
|
520
|
+
at: now,
|
|
521
|
+
channel,
|
|
522
|
+
options: this.notificationOptions,
|
|
523
|
+
preference,
|
|
524
|
+
quietHoursTimeZone,
|
|
525
|
+
});
|
|
526
|
+
return delivery.record
|
|
527
|
+
? [
|
|
528
|
+
createNotificationEntity({
|
|
529
|
+
channel,
|
|
530
|
+
input,
|
|
531
|
+
now,
|
|
532
|
+
options: this.notificationOptions,
|
|
533
|
+
releaseAt: delivery.releaseAt,
|
|
534
|
+
repository,
|
|
535
|
+
silenced: delivery.silenced,
|
|
536
|
+
}),
|
|
537
|
+
]
|
|
538
|
+
: [];
|
|
539
|
+
});
|
|
525
540
|
if (notifications.length === 0) {
|
|
526
541
|
return [];
|
|
527
542
|
}
|
|
528
543
|
const savedNotifications = await repository.save(notifications);
|
|
529
544
|
await Promise.all(savedNotifications
|
|
530
|
-
.filter((notification) => notification.channel !== notification_enums_1.NotificationChannelEnum.IN_APP
|
|
545
|
+
.filter((notification) => notification.channel !== notification_enums_1.NotificationChannelEnum.IN_APP &&
|
|
546
|
+
// A row held for quiet hours or a daily digest carries its release
|
|
547
|
+
// instant in `nextRetryAt`; the delivery scan is what picks it up
|
|
548
|
+
// once that instant passes.
|
|
549
|
+
notification.nextRetryAt === null)
|
|
531
550
|
.map((notification) => manager
|
|
532
551
|
? Promise.resolve(false)
|
|
533
552
|
: this.deliveryService.deliverNotification(notification, this.notificationOptions)));
|
|
@@ -561,6 +580,28 @@ let NotificationService = NotificationService_1 = class NotificationService {
|
|
|
561
580
|
this.logger.error(`BPM_NOTIFICATION_OBSERVER failed for ${notifications.length} notification(s)`, error instanceof Error ? error.stack : String(error));
|
|
562
581
|
}
|
|
563
582
|
}
|
|
583
|
+
/**
|
|
584
|
+
* The zone the recipients' bare `HH:mm` quiet-hours preferences are read in.
|
|
585
|
+
*
|
|
586
|
+
* Falls back to the registered business calendar rather than to UTC: the
|
|
587
|
+
* calendar already states where the host's people are, and a host that
|
|
588
|
+
* supplied one would otherwise get quiet hours silently offset by its whole
|
|
589
|
+
* UTC offset. Resolved here rather than in the flattened options because the
|
|
590
|
+
* calendar is a DI provider that does not exist at wiring time.
|
|
591
|
+
*/
|
|
592
|
+
readQuietHoursTimeZone() {
|
|
593
|
+
if (this.notificationOptions.quietHoursTimeZone) {
|
|
594
|
+
return this.notificationOptions.quietHoursTimeZone;
|
|
595
|
+
}
|
|
596
|
+
try {
|
|
597
|
+
return this.moduleRef.get(business_calendar_token_1.BPM_BUSINESS_CALENDAR, {
|
|
598
|
+
strict: false,
|
|
599
|
+
}).timeZone;
|
|
600
|
+
}
|
|
601
|
+
catch {
|
|
602
|
+
return notification_schedule_1.DEFAULT_QUIET_HOURS_TIME_ZONE;
|
|
603
|
+
}
|
|
604
|
+
}
|
|
564
605
|
readHostObserver() {
|
|
565
606
|
try {
|
|
566
607
|
return this.moduleRef.get(notification_observer_token_1.BPM_NOTIFICATION_OBSERVER, { strict: false });
|
|
@@ -769,14 +810,49 @@ function isUniqueConstraintViolation(error) {
|
|
|
769
810
|
'code' in error &&
|
|
770
811
|
error.code === '23505');
|
|
771
812
|
}
|
|
772
|
-
|
|
813
|
+
/**
|
|
814
|
+
* Splits a channel's fate into "is it recorded" and "is it announced", which
|
|
815
|
+
* `inAppEnabled` used to conflate.
|
|
816
|
+
*
|
|
817
|
+
* The member preference decides announcement. Recording is the host's call:
|
|
818
|
+
* `options.inAppEnabled` is a wiring-level switch for running BPM without
|
|
819
|
+
* notification-centre data at all, not something a member can trip. Email and
|
|
820
|
+
* webhook have no BPM-side record to lose, so for them the two answers stay
|
|
821
|
+
* the same — except in time, where quiet hours and the daily digest push
|
|
822
|
+
* delivery later rather than dropping it.
|
|
823
|
+
*/
|
|
824
|
+
function resolveChannelDelivery({ at, channel, options, preference, quietHoursTimeZone, }) {
|
|
825
|
+
const quietHours = {
|
|
826
|
+
quietHoursEnd: preference.quietHoursEnd,
|
|
827
|
+
quietHoursStart: preference.quietHoursStart,
|
|
828
|
+
timeZone: quietHoursTimeZone,
|
|
829
|
+
};
|
|
773
830
|
if (channel === notification_enums_1.NotificationChannelEnum.IN_APP) {
|
|
774
|
-
return
|
|
831
|
+
return {
|
|
832
|
+
releaseAt: null,
|
|
833
|
+
record: options.inAppEnabled,
|
|
834
|
+
silenced: !preference.inAppEnabled || (0, notification_schedule_1.isWithinQuietHours)(at, quietHours),
|
|
835
|
+
};
|
|
775
836
|
}
|
|
776
837
|
if (channel === notification_enums_1.NotificationChannelEnum.EMAIL) {
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
838
|
+
const record = options.emailEnabled && preference.emailEnabled;
|
|
839
|
+
return {
|
|
840
|
+
releaseAt: record
|
|
841
|
+
? (0, notification_schedule_1.resolveEmailReleaseAt)(at, {
|
|
842
|
+
...quietHours,
|
|
843
|
+
digestHour: options.emailDigestHour,
|
|
844
|
+
digestMode: preference.emailDigestMode,
|
|
845
|
+
})
|
|
846
|
+
: null,
|
|
847
|
+
record,
|
|
848
|
+
silenced: false,
|
|
849
|
+
};
|
|
850
|
+
}
|
|
851
|
+
return {
|
|
852
|
+
releaseAt: null,
|
|
853
|
+
record: options.webhookEnabled,
|
|
854
|
+
silenced: false,
|
|
855
|
+
};
|
|
780
856
|
}
|
|
781
857
|
function hasSlaEscalationStep(task) {
|
|
782
858
|
return task.delegationChain.some((step) => step['reason'] === notification_enums_1.SLA_ESCALATION_DELEGATION_REASON);
|
|
@@ -793,7 +869,7 @@ function isSlaTimeoutActionEnabled(action, options) {
|
|
|
793
869
|
}
|
|
794
870
|
return options.slaTimeoutRemindEnabled;
|
|
795
871
|
}
|
|
796
|
-
function createNotificationEntity({ channel, input, options, repository, }) {
|
|
872
|
+
function createNotificationEntity({ channel, input, now, options, releaseAt, repository, silenced, }) {
|
|
797
873
|
const renderedTemplate = (0, notification_template_1.renderNotificationTemplate)({
|
|
798
874
|
customTemplate: input.customTemplate,
|
|
799
875
|
engine: options.templateEngine,
|
|
@@ -808,18 +884,21 @@ function createNotificationEntity({ channel, input, options, repository, }) {
|
|
|
808
884
|
attemptCount: 0,
|
|
809
885
|
body: renderedTemplate.body,
|
|
810
886
|
channel,
|
|
811
|
-
|
|
887
|
+
// An in-app row is "delivered" the moment it exists, silenced or not:
|
|
888
|
+
// what silencing withholds is the announcement, not the record.
|
|
889
|
+
deliveredAt: isInApp ? now : null,
|
|
812
890
|
deliveryError: null,
|
|
813
891
|
deliveryTarget: null,
|
|
814
892
|
instanceId: input.instanceId,
|
|
815
893
|
lastAttemptAt: null,
|
|
816
|
-
nextRetryAt:
|
|
894
|
+
nextRetryAt: releaseAt,
|
|
817
895
|
payload: input.payload,
|
|
818
896
|
readAt: null,
|
|
819
897
|
recipientMemberId: input.recipientMemberId,
|
|
820
898
|
resolution: null,
|
|
821
899
|
resolvedAt: null,
|
|
822
|
-
sentAt: isInApp ?
|
|
900
|
+
sentAt: isInApp ? now : null,
|
|
901
|
+
silenced,
|
|
823
902
|
status: isInApp
|
|
824
903
|
? notification_enums_1.NotificationStatusEnum.SENT
|
|
825
904
|
: notification_enums_1.NotificationStatusEnum.PENDING,
|