@oneuptime/common 9.4.8 → 9.4.9
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/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.ts +59 -0
- package/Models/DatabaseModels/UserOnCallLog.ts +48 -0
- package/Models/DatabaseModels/UserOnCallLogTimeline.ts +49 -0
- package/Server/API/UserOnCallLogTimelineAPI.ts +65 -25
- package/Server/Infrastructure/Postgres/SchemaMigrations/1769469813786-MigrationName.ts +71 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1769517677937-RenameNotificationRuleTypes.ts +67 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
- package/Server/Services/AlertEpisodeService.ts +134 -26
- package/Server/Services/OnCallDutyPolicyEscalationRuleService.ts +18 -1
- package/Server/Services/OnCallDutyPolicyExecutionLogService.ts +64 -2
- package/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.ts +27 -1
- package/Server/Services/OnCallDutyPolicyService.ts +11 -1
- package/Server/Services/PushNotificationService.ts +1 -0
- package/Server/Services/UserNotificationRuleService.ts +641 -10
- package/Server/Services/UserOnCallLogService.ts +58 -14
- package/Server/Utils/PushNotificationUtil.ts +75 -16
- package/Types/Email/EmailTemplateType.ts +1 -0
- package/Types/NotificationRule/NotificationRuleType.ts +3 -2
- package/Types/WhatsApp/WhatsAppTemplates.ts +4 -0
- package/UI/Components/Markdown.tsx/MarkdownViewer.tsx +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.js +58 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLogTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js +47 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +48 -0
- package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
- package/build/dist/Server/API/UserOnCallLogTimelineAPI.js +55 -15
- package/build/dist/Server/API/UserOnCallLogTimelineAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769469813786-MigrationName.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769469813786-MigrationName.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769517677937-RenameNotificationRuleTypes.js +67 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1769517677937-RenameNotificationRuleTypes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeService.js +111 -23
- package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js +10 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js +49 -2
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js +21 -1
- package/build/dist/Server/Services/OnCallDutyPolicyExecutionLogTimelineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyService.js +6 -1
- package/build/dist/Server/Services/OnCallDutyPolicyService.js.map +1 -1
- package/build/dist/Server/Services/PushNotificationService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +521 -43
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserOnCallLogService.js +48 -12
- package/build/dist/Server/Services/UserOnCallLogService.js.map +1 -1
- package/build/dist/Server/Utils/PushNotificationUtil.js +51 -16
- package/build/dist/Server/Utils/PushNotificationUtil.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +1 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/NotificationRule/NotificationRuleType.js +3 -2
- package/build/dist/Types/NotificationRule/NotificationRuleType.js.map +1 -1
- package/build/dist/Types/WhatsApp/WhatsAppTemplates.js +3 -0
- package/build/dist/Types/WhatsApp/WhatsAppTemplates.js.map +1 -1
- package/package.json +1 -1
|
@@ -18,7 +18,12 @@ import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
|
18
18
|
import OneUptimeDate from "../../Types/Date";
|
|
19
19
|
import AlertEpisodeFeedService from "./AlertEpisodeFeedService";
|
|
20
20
|
import { AlertEpisodeFeedEventType } from "../../Models/DatabaseModels/AlertEpisodeFeed";
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
Red500,
|
|
23
|
+
Green500,
|
|
24
|
+
Yellow500,
|
|
25
|
+
Purple500,
|
|
26
|
+
} from "../../Types/BrandColors";
|
|
22
27
|
import URL from "../../Types/API/URL";
|
|
23
28
|
import DatabaseConfig from "../DatabaseConfig";
|
|
24
29
|
import AlertSeverityService from "./AlertSeverityService";
|
|
@@ -36,6 +41,9 @@ import WorkspaceType from "../../Types/Workspace/WorkspaceType";
|
|
|
36
41
|
import Typeof from "../../Types/Typeof";
|
|
37
42
|
import AlertService from "./AlertService";
|
|
38
43
|
import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
|
|
44
|
+
import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
|
|
45
|
+
import OnCallDutyPolicy from "../../Models/DatabaseModels/OnCallDutyPolicy";
|
|
46
|
+
import UserNotificationEventType from "../../Types/UserNotification/UserNotificationEventType";
|
|
39
47
|
|
|
40
48
|
export class Service extends DatabaseService<Model> {
|
|
41
49
|
public constructor() {
|
|
@@ -57,7 +65,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
57
65
|
episodeNumber: true,
|
|
58
66
|
},
|
|
59
67
|
sort: {
|
|
60
|
-
|
|
68
|
+
episodeNumber: SortOrder.Descending,
|
|
61
69
|
},
|
|
62
70
|
props: {
|
|
63
71
|
isRoot: true,
|
|
@@ -200,6 +208,16 @@ export class Service extends DatabaseService<Model> {
|
|
|
200
208
|
);
|
|
201
209
|
}
|
|
202
210
|
})
|
|
211
|
+
.then(async () => {
|
|
212
|
+
// Execute on-call duty policies
|
|
213
|
+
try {
|
|
214
|
+
await this.executeEpisodeOnCallDutyPoliciesAsync(createdItem);
|
|
215
|
+
} catch (error) {
|
|
216
|
+
logger.error(
|
|
217
|
+
`On-call duty policy execution failed in AlertEpisodeService.onCreateSuccess: ${error}`,
|
|
218
|
+
);
|
|
219
|
+
}
|
|
220
|
+
})
|
|
203
221
|
.catch((error: Error) => {
|
|
204
222
|
logger.error(
|
|
205
223
|
`Critical error in AlertEpisodeService.onCreateSuccess: ${error}`,
|
|
@@ -239,6 +257,93 @@ export class Service extends DatabaseService<Model> {
|
|
|
239
257
|
});
|
|
240
258
|
}
|
|
241
259
|
|
|
260
|
+
@CaptureSpan()
|
|
261
|
+
private async executeEpisodeOnCallDutyPoliciesAsync(
|
|
262
|
+
createdItem: Model,
|
|
263
|
+
): Promise<void> {
|
|
264
|
+
if (!createdItem.id || !createdItem.projectId) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
try {
|
|
269
|
+
// Fetch the episode with on-call duty policies since they may not be loaded
|
|
270
|
+
const episodeWithPolicies: Model | null = await this.findOneById({
|
|
271
|
+
id: createdItem.id,
|
|
272
|
+
select: {
|
|
273
|
+
onCallDutyPolicies: {
|
|
274
|
+
_id: true,
|
|
275
|
+
name: true,
|
|
276
|
+
},
|
|
277
|
+
},
|
|
278
|
+
props: {
|
|
279
|
+
isRoot: true,
|
|
280
|
+
},
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
if (
|
|
284
|
+
!episodeWithPolicies?.onCallDutyPolicies?.length ||
|
|
285
|
+
episodeWithPolicies.onCallDutyPolicies.length === 0
|
|
286
|
+
) {
|
|
287
|
+
return;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
// Execute all on-call policies in parallel
|
|
291
|
+
const policyPromises: Promise<void>[] =
|
|
292
|
+
episodeWithPolicies.onCallDutyPolicies.map(
|
|
293
|
+
(policy: OnCallDutyPolicy) => {
|
|
294
|
+
return OnCallDutyPolicyService.executePolicy(
|
|
295
|
+
new ObjectID(policy._id as string),
|
|
296
|
+
{
|
|
297
|
+
triggeredByAlertEpisodeId: createdItem.id!,
|
|
298
|
+
userNotificationEventType:
|
|
299
|
+
UserNotificationEventType.AlertEpisodeCreated,
|
|
300
|
+
},
|
|
301
|
+
);
|
|
302
|
+
},
|
|
303
|
+
);
|
|
304
|
+
|
|
305
|
+
await Promise.allSettled(policyPromises);
|
|
306
|
+
|
|
307
|
+
// Update the flag to indicate on-call policy has been executed
|
|
308
|
+
await this.updateOneById({
|
|
309
|
+
id: createdItem.id,
|
|
310
|
+
data: {
|
|
311
|
+
isOnCallPolicyExecuted: true,
|
|
312
|
+
},
|
|
313
|
+
props: {
|
|
314
|
+
isRoot: true,
|
|
315
|
+
},
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
// Create feed entry for on-call policy execution
|
|
319
|
+
const policyNames: string[] = episodeWithPolicies.onCallDutyPolicies
|
|
320
|
+
.map((policy: OnCallDutyPolicy) => {
|
|
321
|
+
return policy.name || "Unnamed Policy";
|
|
322
|
+
})
|
|
323
|
+
.filter((name: string) => {
|
|
324
|
+
return Boolean(name);
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
let feedInfoInMarkdown: string = `#### On-Call Policy Executed\n\n`;
|
|
328
|
+
feedInfoInMarkdown += `The following on-call ${policyNames.length === 1 ? "policy has" : "policies have"} been executed for this episode:\n\n`;
|
|
329
|
+
|
|
330
|
+
for (const policyName of policyNames) {
|
|
331
|
+
feedInfoInMarkdown += `- ${policyName}\n`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
await AlertEpisodeFeedService.createAlertEpisodeFeedItem({
|
|
335
|
+
alertEpisodeId: createdItem.id,
|
|
336
|
+
projectId: createdItem.projectId,
|
|
337
|
+
alertEpisodeFeedEventType: AlertEpisodeFeedEventType.OnCallPolicy,
|
|
338
|
+
displayColor: Purple500,
|
|
339
|
+
feedInfoInMarkdown: feedInfoInMarkdown,
|
|
340
|
+
});
|
|
341
|
+
} catch (error) {
|
|
342
|
+
logger.error(`Error in executeEpisodeOnCallDutyPoliciesAsync: ${error}`);
|
|
343
|
+
throw error;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
|
|
242
347
|
@CaptureSpan()
|
|
243
348
|
public async changeEpisodeState(data: {
|
|
244
349
|
projectId: ObjectID;
|
|
@@ -304,28 +409,10 @@ export class Service extends DatabaseService<Model> {
|
|
|
304
409
|
props: props || {},
|
|
305
410
|
});
|
|
306
411
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
isResolvedState: true,
|
|
312
|
-
},
|
|
313
|
-
props: {
|
|
314
|
-
isRoot: true,
|
|
315
|
-
},
|
|
316
|
-
});
|
|
317
|
-
|
|
318
|
-
if (alertState?.isResolvedState) {
|
|
319
|
-
await this.updateOneById({
|
|
320
|
-
id: episodeId,
|
|
321
|
-
data: {
|
|
322
|
-
resolvedAt: OneUptimeDate.getCurrentDate(),
|
|
323
|
-
},
|
|
324
|
-
props: {
|
|
325
|
-
isRoot: true,
|
|
326
|
-
},
|
|
327
|
-
});
|
|
328
|
-
}
|
|
412
|
+
/*
|
|
413
|
+
* Note: resolvedAt is updated by AlertEpisodeStateTimelineService.onCreateSuccess()
|
|
414
|
+
* to avoid duplicate updates.
|
|
415
|
+
*/
|
|
329
416
|
|
|
330
417
|
// Cascade state change to all member alerts if requested
|
|
331
418
|
if (cascadeToAlerts) {
|
|
@@ -395,7 +482,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
395
482
|
@CaptureSpan()
|
|
396
483
|
public async acknowledgeEpisode(
|
|
397
484
|
episodeId: ObjectID,
|
|
398
|
-
acknowledgedByUserId
|
|
485
|
+
acknowledgedByUserId?: ObjectID,
|
|
399
486
|
cascadeToAlerts: boolean = true,
|
|
400
487
|
): Promise<void> {
|
|
401
488
|
const episode: Model | null = await this.findOneById({
|
|
@@ -463,7 +550,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
463
550
|
@CaptureSpan()
|
|
464
551
|
public async resolveEpisode(
|
|
465
552
|
episodeId: ObjectID,
|
|
466
|
-
resolvedByUserId
|
|
553
|
+
resolvedByUserId?: ObjectID,
|
|
467
554
|
cascadeToAlerts: boolean = true,
|
|
468
555
|
): Promise<void> {
|
|
469
556
|
const episode: Model | null = await this.findOneById({
|
|
@@ -809,6 +896,27 @@ export class Service extends DatabaseService<Model> {
|
|
|
809
896
|
);
|
|
810
897
|
}
|
|
811
898
|
|
|
899
|
+
@CaptureSpan()
|
|
900
|
+
public async getEpisodeNumber(data: {
|
|
901
|
+
episodeId: ObjectID;
|
|
902
|
+
}): Promise<number | null> {
|
|
903
|
+
const episode: Model | null = await this.findOneById({
|
|
904
|
+
id: data.episodeId,
|
|
905
|
+
select: {
|
|
906
|
+
episodeNumber: true,
|
|
907
|
+
},
|
|
908
|
+
props: {
|
|
909
|
+
isRoot: true,
|
|
910
|
+
},
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
if (!episode) {
|
|
914
|
+
throw new BadDataException("Episode not found.");
|
|
915
|
+
}
|
|
916
|
+
|
|
917
|
+
return episode.episodeNumber ? Number(episode.episodeNumber) : null;
|
|
918
|
+
}
|
|
919
|
+
|
|
812
920
|
@CaptureSpan()
|
|
813
921
|
protected override async onUpdateSuccess(
|
|
814
922
|
onUpdate: OnUpdate<Model>,
|
|
@@ -110,6 +110,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
110
110
|
projectId: ObjectID;
|
|
111
111
|
triggeredByIncidentId?: ObjectID | undefined;
|
|
112
112
|
triggeredByAlertId?: ObjectID | undefined;
|
|
113
|
+
triggeredByAlertEpisodeId?: ObjectID | undefined;
|
|
113
114
|
userNotificationEventType: UserNotificationEventType;
|
|
114
115
|
onCallPolicyExecutionLogId: ObjectID;
|
|
115
116
|
onCallPolicyId: ObjectID;
|
|
@@ -173,6 +174,10 @@ export class Service extends DatabaseService<Model> {
|
|
|
173
174
|
log.triggeredByAlertId = options.triggeredByAlertId;
|
|
174
175
|
}
|
|
175
176
|
|
|
177
|
+
if (options.triggeredByAlertEpisodeId) {
|
|
178
|
+
log.triggeredByAlertEpisodeId = options.triggeredByAlertEpisodeId;
|
|
179
|
+
}
|
|
180
|
+
|
|
176
181
|
return log;
|
|
177
182
|
};
|
|
178
183
|
|
|
@@ -192,7 +197,17 @@ export class Service extends DatabaseService<Model> {
|
|
|
192
197
|
!options.triggeredByAlertId
|
|
193
198
|
) {
|
|
194
199
|
throw new BadDataException(
|
|
195
|
-
"triggeredByAlertId is required when userNotificationEventType is
|
|
200
|
+
"triggeredByAlertId is required when userNotificationEventType is AlertCreated",
|
|
201
|
+
);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
if (
|
|
205
|
+
UserNotificationEventType.AlertEpisodeCreated ===
|
|
206
|
+
options.userNotificationEventType &&
|
|
207
|
+
!options.triggeredByAlertEpisodeId
|
|
208
|
+
) {
|
|
209
|
+
throw new BadDataException(
|
|
210
|
+
"triggeredByAlertEpisodeId is required when userNotificationEventType is AlertEpisodeCreated",
|
|
196
211
|
);
|
|
197
212
|
}
|
|
198
213
|
|
|
@@ -307,6 +322,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
307
322
|
userNotificationEventType: options.userNotificationEventType!,
|
|
308
323
|
triggeredByIncidentId: options.triggeredByIncidentId || undefined,
|
|
309
324
|
triggeredByAlertId: options.triggeredByAlertId || undefined,
|
|
325
|
+
triggeredByAlertEpisodeId:
|
|
326
|
+
options.triggeredByAlertEpisodeId || undefined,
|
|
310
327
|
onCallPolicyExecutionLogId: options.onCallPolicyExecutionLogId,
|
|
311
328
|
onCallPolicyId: options.onCallPolicyId,
|
|
312
329
|
onCallPolicyEscalationRuleId: ruleId,
|
|
@@ -17,9 +17,12 @@ import ObjectID from "../../Types/ObjectID";
|
|
|
17
17
|
import Color from "../../Types/Color";
|
|
18
18
|
import AlertFeedService from "./AlertFeedService";
|
|
19
19
|
import { AlertFeedEventType } from "../../Models/DatabaseModels/AlertFeed";
|
|
20
|
+
import AlertEpisodeFeedService from "./AlertEpisodeFeedService";
|
|
21
|
+
import { AlertEpisodeFeedEventType } from "../../Models/DatabaseModels/AlertEpisodeFeed";
|
|
20
22
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
21
23
|
import IncidentService from "./IncidentService";
|
|
22
24
|
import AlertService from "./AlertService";
|
|
25
|
+
import AlertEpisodeService from "./AlertEpisodeService";
|
|
23
26
|
|
|
24
27
|
export class Service extends DatabaseService<Model> {
|
|
25
28
|
public constructor() {
|
|
@@ -55,7 +58,11 @@ export class Service extends DatabaseService<Model> {
|
|
|
55
58
|
_onCreate: OnCreate<Model>,
|
|
56
59
|
createdItem: Model,
|
|
57
60
|
): Promise<Model> {
|
|
58
|
-
if (
|
|
61
|
+
if (
|
|
62
|
+
createdItem.triggeredByIncidentId ||
|
|
63
|
+
createdItem.triggeredByAlertId ||
|
|
64
|
+
createdItem.triggeredByAlertEpisodeId
|
|
65
|
+
) {
|
|
59
66
|
const onCallPolicy: OnCallDutyPolicy | null =
|
|
60
67
|
await OnCallDutyPolicyService.findOneById({
|
|
61
68
|
id: createdItem.onCallDutyPolicyId!,
|
|
@@ -90,6 +97,14 @@ export class Service extends DatabaseService<Model> {
|
|
|
90
97
|
incidentOrAlertLink = `[Alert ${alertNumber}](${(await AlertService.getAlertLinkInDashboard(createdItem.projectId!, createdItem.triggeredByAlertId)).toString()})`;
|
|
91
98
|
}
|
|
92
99
|
|
|
100
|
+
if (createdItem.triggeredByAlertEpisodeId) {
|
|
101
|
+
const episodeNumber: number | null =
|
|
102
|
+
await AlertEpisodeService.getEpisodeNumber({
|
|
103
|
+
episodeId: createdItem.triggeredByAlertEpisodeId,
|
|
104
|
+
});
|
|
105
|
+
incidentOrAlertLink = `[Alert Episode ${episodeNumber}](${(await AlertEpisodeService.getEpisodeLinkInDashboard(createdItem.projectId!, createdItem.triggeredByAlertEpisodeId)).toString()})`;
|
|
106
|
+
}
|
|
107
|
+
|
|
93
108
|
const feedInfoInMarkdown: string = `**📞 On Call Policy Started Executing:** On Call Policy **${onCallPolicy.name}** started executing for ${incidentOrAlertLink}. Users on call on this policy will now be notified.`;
|
|
94
109
|
|
|
95
110
|
if (
|
|
@@ -118,6 +133,20 @@ export class Service extends DatabaseService<Model> {
|
|
|
118
133
|
feedInfoInMarkdown: feedInfoInMarkdown,
|
|
119
134
|
});
|
|
120
135
|
}
|
|
136
|
+
|
|
137
|
+
if (
|
|
138
|
+
onCallPolicy &&
|
|
139
|
+
onCallPolicy.id &&
|
|
140
|
+
createdItem.triggeredByAlertEpisodeId
|
|
141
|
+
) {
|
|
142
|
+
await AlertEpisodeFeedService.createAlertEpisodeFeedItem({
|
|
143
|
+
alertEpisodeId: createdItem.triggeredByAlertEpisodeId,
|
|
144
|
+
projectId: createdItem.projectId!,
|
|
145
|
+
alertEpisodeFeedEventType: AlertEpisodeFeedEventType.OnCallPolicy,
|
|
146
|
+
displayColor: Yellow500,
|
|
147
|
+
feedInfoInMarkdown: feedInfoInMarkdown,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
121
150
|
}
|
|
122
151
|
}
|
|
123
152
|
|
|
@@ -159,6 +188,11 @@ export class Service extends DatabaseService<Model> {
|
|
|
159
188
|
userNotificationEventType = UserNotificationEventType.AlertCreated;
|
|
160
189
|
}
|
|
161
190
|
|
|
191
|
+
if (createdItem.triggeredByAlertEpisodeId) {
|
|
192
|
+
userNotificationEventType =
|
|
193
|
+
UserNotificationEventType.AlertEpisodeCreated;
|
|
194
|
+
}
|
|
195
|
+
|
|
162
196
|
if (!userNotificationEventType) {
|
|
163
197
|
throw new BadDataException("Invalid userNotificationEventType");
|
|
164
198
|
}
|
|
@@ -169,6 +203,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
169
203
|
projectId: createdItem.projectId!,
|
|
170
204
|
triggeredByIncidentId: createdItem.triggeredByIncidentId,
|
|
171
205
|
triggeredByAlertId: createdItem.triggeredByAlertId,
|
|
206
|
+
triggeredByAlertEpisodeId: createdItem.triggeredByAlertEpisodeId,
|
|
172
207
|
userNotificationEventType: userNotificationEventType,
|
|
173
208
|
onCallPolicyExecutionLogId: createdItem.id!,
|
|
174
209
|
onCallPolicyId: createdItem.onCallDutyPolicyId!,
|
|
@@ -256,6 +291,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
256
291
|
statusMessage: true,
|
|
257
292
|
triggeredByIncidentId: true,
|
|
258
293
|
triggeredByAlertId: true,
|
|
294
|
+
triggeredByAlertEpisodeId: true,
|
|
259
295
|
},
|
|
260
296
|
props: {
|
|
261
297
|
isRoot: true,
|
|
@@ -266,7 +302,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
266
302
|
if (
|
|
267
303
|
onCalldutyPolicyExecutionLog &&
|
|
268
304
|
(onCalldutyPolicyExecutionLog.triggeredByIncidentId ||
|
|
269
|
-
onCalldutyPolicyExecutionLog.triggeredByAlertId
|
|
305
|
+
onCalldutyPolicyExecutionLog.triggeredByAlertId ||
|
|
306
|
+
onCalldutyPolicyExecutionLog.triggeredByAlertEpisodeId)
|
|
270
307
|
) {
|
|
271
308
|
const onCallPolicy: OnCallDutyPolicy | null =
|
|
272
309
|
await OnCallDutyPolicyService.findOneById({
|
|
@@ -308,6 +345,15 @@ export class Service extends DatabaseService<Model> {
|
|
|
308
345
|
incidentOrAlertLink = `[Alert ${alertNumber}](${(await AlertService.getAlertLinkInDashboard(onCalldutyPolicyExecutionLog.projectId!, onCalldutyPolicyExecutionLog.triggeredByAlertId)).toString()})`;
|
|
309
346
|
}
|
|
310
347
|
|
|
348
|
+
if (onCalldutyPolicyExecutionLog.triggeredByAlertEpisodeId) {
|
|
349
|
+
const episodeNumber: number | null =
|
|
350
|
+
await AlertEpisodeService.getEpisodeNumber({
|
|
351
|
+
episodeId:
|
|
352
|
+
onCalldutyPolicyExecutionLog.triggeredByAlertEpisodeId,
|
|
353
|
+
});
|
|
354
|
+
incidentOrAlertLink = `[Alert Episode ${episodeNumber}](${(await AlertEpisodeService.getEpisodeLinkInDashboard(onCalldutyPolicyExecutionLog.projectId!, onCalldutyPolicyExecutionLog.triggeredByAlertEpisodeId)).toString()})`;
|
|
355
|
+
}
|
|
356
|
+
|
|
311
357
|
const feedInfoInMarkdown: string = `**${this.getEmojiByStatus(onCalldutyPolicyExecutionLog.status)} On Call Policy Status Updated for ${incidentOrAlertLink}:**
|
|
312
358
|
|
|
313
359
|
On-call policy **[${onCallPolicy.name?.toString()}](${(await OnCallDutyPolicyService.getOnCallDutyPolicyLinkInDashboard(onCallPolicy.projectId!, onCallPolicy.id!)).toString()})** status updated to **${onCalldutyPolicyExecutionLog.status}**`;
|
|
@@ -344,6 +390,22 @@ export class Service extends DatabaseService<Model> {
|
|
|
344
390
|
feedInfoInMarkdown: feedInfoInMarkdown,
|
|
345
391
|
});
|
|
346
392
|
}
|
|
393
|
+
|
|
394
|
+
if (onCalldutyPolicyExecutionLog.triggeredByAlertEpisodeId) {
|
|
395
|
+
await AlertEpisodeFeedService.createAlertEpisodeFeedItem({
|
|
396
|
+
alertEpisodeId:
|
|
397
|
+
onCalldutyPolicyExecutionLog.triggeredByAlertEpisodeId,
|
|
398
|
+
projectId: onCalldutyPolicyExecutionLog.projectId!,
|
|
399
|
+
alertEpisodeFeedEventType: AlertEpisodeFeedEventType.OnCallPolicy,
|
|
400
|
+
displayColor: onCalldutyPolicyExecutionLog.status
|
|
401
|
+
? this.getDisplayColorByStatus(
|
|
402
|
+
onCalldutyPolicyExecutionLog.status,
|
|
403
|
+
)
|
|
404
|
+
: Blue500,
|
|
405
|
+
moreInformationInMarkdown: moreInformationInMarkdown,
|
|
406
|
+
feedInfoInMarkdown: feedInfoInMarkdown,
|
|
407
|
+
});
|
|
408
|
+
}
|
|
347
409
|
}
|
|
348
410
|
}
|
|
349
411
|
}
|
|
@@ -11,8 +11,11 @@ import logger from "../Utils/Logger";
|
|
|
11
11
|
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
12
12
|
import AlertFeedService from "./AlertFeedService";
|
|
13
13
|
import { AlertFeedEventType } from "../../Models/DatabaseModels/AlertFeed";
|
|
14
|
+
import AlertEpisodeFeedService from "./AlertEpisodeFeedService";
|
|
15
|
+
import { AlertEpisodeFeedEventType } from "../../Models/DatabaseModels/AlertEpisodeFeed";
|
|
14
16
|
import OnCallDutyPolicyService from "./OnCallDutyPolicyService";
|
|
15
17
|
import AlertService from "./AlertService";
|
|
18
|
+
import AlertEpisodeService from "./AlertEpisodeService";
|
|
16
19
|
import IncidentService from "./IncidentService";
|
|
17
20
|
import UserService from "./UserService";
|
|
18
21
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
@@ -80,6 +83,7 @@ export class Service extends DatabaseService<Model> {
|
|
|
80
83
|
onCallDutyPolicyId: true,
|
|
81
84
|
triggeredByIncidentId: true,
|
|
82
85
|
triggeredByAlertId: true,
|
|
86
|
+
triggeredByAlertEpisodeId: true,
|
|
83
87
|
projectId: true,
|
|
84
88
|
status: true,
|
|
85
89
|
statusMessage: true,
|
|
@@ -125,7 +129,8 @@ export class Service extends DatabaseService<Model> {
|
|
|
125
129
|
|
|
126
130
|
if (
|
|
127
131
|
!onCallDutyPolicyExecutionLogTimeline.triggeredByIncidentId &&
|
|
128
|
-
!onCallDutyPolicyExecutionLogTimeline.triggeredByAlertId
|
|
132
|
+
!onCallDutyPolicyExecutionLogTimeline.triggeredByAlertId &&
|
|
133
|
+
!onCallDutyPolicyExecutionLogTimeline.triggeredByAlertEpisodeId
|
|
129
134
|
) {
|
|
130
135
|
return;
|
|
131
136
|
}
|
|
@@ -172,6 +177,15 @@ export class Service extends DatabaseService<Model> {
|
|
|
172
177
|
incidentOrAlertLink = `[Alert ${alertNumber}](${(await AlertService.getAlertLinkInDashboard(onCallDutyPolicyExecutionLogTimeline.projectId!, onCallDutyPolicyExecutionLogTimeline.triggeredByAlertId)).toString()})`;
|
|
173
178
|
}
|
|
174
179
|
|
|
180
|
+
if (onCallDutyPolicyExecutionLogTimeline.triggeredByAlertEpisodeId) {
|
|
181
|
+
const episodeNumber: number | null =
|
|
182
|
+
await AlertEpisodeService.getEpisodeNumber({
|
|
183
|
+
episodeId:
|
|
184
|
+
onCallDutyPolicyExecutionLogTimeline.triggeredByAlertEpisodeId,
|
|
185
|
+
});
|
|
186
|
+
incidentOrAlertLink = `[Alert Episode ${episodeNumber}](${(await AlertEpisodeService.getEpisodeLinkInDashboard(onCallDutyPolicyExecutionLogTimeline.projectId!, onCallDutyPolicyExecutionLogTimeline.triggeredByAlertEpisodeId)).toString()})`;
|
|
187
|
+
}
|
|
188
|
+
|
|
175
189
|
let feedInfoInMarkdown: string = `**${this.getEmojiBasedOnStatus(status)} ${incidentOrAlertLink} On-Call Alert ${status} to ${await UserService.getUserMarkdownString(
|
|
176
190
|
{
|
|
177
191
|
userId: onCallDutyPolicyExecutionLogTimeline.alertSentToUserId!,
|
|
@@ -224,6 +238,18 @@ The on-call policy **[${onCallDutyPolicyExecutionLogTimeline.onCallDutyPolicy.na
|
|
|
224
238
|
});
|
|
225
239
|
}
|
|
226
240
|
|
|
241
|
+
if (onCallDutyPolicyExecutionLogTimeline.triggeredByAlertEpisodeId) {
|
|
242
|
+
await AlertEpisodeFeedService.createAlertEpisodeFeedItem({
|
|
243
|
+
alertEpisodeId:
|
|
244
|
+
onCallDutyPolicyExecutionLogTimeline.triggeredByAlertEpisodeId,
|
|
245
|
+
projectId: onCallDutyPolicyExecutionLogTimeline.projectId!,
|
|
246
|
+
alertEpisodeFeedEventType:
|
|
247
|
+
AlertEpisodeFeedEventType.OnCallNotification,
|
|
248
|
+
displayColor: displayColor,
|
|
249
|
+
feedInfoInMarkdown: feedInfoInMarkdown,
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
|
|
227
253
|
logger.debug("Incident Feed created");
|
|
228
254
|
}
|
|
229
255
|
}
|
|
@@ -285,7 +285,17 @@ ${onCallPolicy.description || "No description provided."}
|
|
|
285
285
|
!options.triggeredByAlertId
|
|
286
286
|
) {
|
|
287
287
|
throw new BadDataException(
|
|
288
|
-
"triggeredByAlertId is required when userNotificationEventType is
|
|
288
|
+
"triggeredByAlertId is required when userNotificationEventType is AlertCreated",
|
|
289
|
+
);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
if (
|
|
293
|
+
UserNotificationEventType.AlertEpisodeCreated ===
|
|
294
|
+
options.userNotificationEventType &&
|
|
295
|
+
!options.triggeredByAlertEpisodeId
|
|
296
|
+
) {
|
|
297
|
+
throw new BadDataException(
|
|
298
|
+
"triggeredByAlertEpisodeId is required when userNotificationEventType is AlertEpisodeCreated",
|
|
289
299
|
);
|
|
290
300
|
}
|
|
291
301
|
|
|
@@ -25,6 +25,7 @@ export interface PushNotificationOptions {
|
|
|
25
25
|
// Optional relations for richer logging
|
|
26
26
|
incidentId?: ObjectID | undefined;
|
|
27
27
|
alertId?: ObjectID | undefined;
|
|
28
|
+
alertEpisodeId?: ObjectID | undefined;
|
|
28
29
|
scheduledMaintenanceId?: ObjectID | undefined;
|
|
29
30
|
statusPageId?: ObjectID | undefined;
|
|
30
31
|
statusPageAnnouncementId?: ObjectID | undefined;
|