@oneuptime/common 7.0.4019 → 7.0.4026
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/Server/Services/AlertStateTimelineService.ts +66 -0
- package/Server/Services/IncidentStateTimelineService.ts +66 -0
- package/Server/Services/MonitorService.ts +72 -1
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +76 -0
- package/Server/Services/WorkspaceNotificationRuleService.ts +128 -35
- package/Server/Utils/Workspace/Slack/Slack.ts +59 -0
- package/Server/Utils/Workspace/WorkspaceBase.ts +10 -0
- package/Types/Workspace/NotificationRules/CreateChannelNotificationRule.ts +4 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js +50 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +50 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +56 -1
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +52 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +101 -29
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js +46 -0
- package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/WorkspaceBase.js +9 -0
- package/build/dist/Server/Utils/Workspace/WorkspaceBase.js.map +1 -1
- package/package.json +2 -2
|
@@ -21,6 +21,8 @@ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
|
21
21
|
import logger from "../Utils/Logger";
|
|
22
22
|
import AlertFeedService from "./AlertFeedService";
|
|
23
23
|
import { AlertFeedEventType } from "../../Models/DatabaseModels/AlertFeed";
|
|
24
|
+
import WorkspaceNotificationRuleService from "./WorkspaceNotificationRuleService";
|
|
25
|
+
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
24
26
|
|
|
25
27
|
export class Service extends DatabaseService<AlertStateTimeline> {
|
|
26
28
|
public constructor() {
|
|
@@ -336,9 +338,73 @@ ${createdItem.rootCause}`,
|
|
|
336
338
|
logger.error(error);
|
|
337
339
|
});
|
|
338
340
|
|
|
341
|
+
const isLastAlertState: boolean = await this.isLastAlertState({
|
|
342
|
+
projectId: createdItem.projectId!,
|
|
343
|
+
alertStateId: createdItem.alertStateId,
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
if (isLastAlertState) {
|
|
347
|
+
WorkspaceNotificationRuleService.archiveWorkspaceChannels({
|
|
348
|
+
projectId: createdItem.projectId!,
|
|
349
|
+
notificationFor: {
|
|
350
|
+
alertId: createdItem.alertId,
|
|
351
|
+
},
|
|
352
|
+
sendMessageBeforeArchiving: {
|
|
353
|
+
_type: "WorkspacePayloadMarkdown",
|
|
354
|
+
text: `**[Alert ${alertNumber}](${(
|
|
355
|
+
await AlertService.getAlertLinkInDashboard(
|
|
356
|
+
createdItem.projectId!,
|
|
357
|
+
createdItem.alertId!,
|
|
358
|
+
)
|
|
359
|
+
).toString()})** is resolved. Archiving channel.`,
|
|
360
|
+
},
|
|
361
|
+
}).catch((error: Error) => {
|
|
362
|
+
logger.error(`Error while archiving workspace channels:`);
|
|
363
|
+
logger.error(error);
|
|
364
|
+
});
|
|
365
|
+
}
|
|
366
|
+
|
|
339
367
|
return createdItem;
|
|
340
368
|
}
|
|
341
369
|
|
|
370
|
+
private async isLastAlertState(data: {
|
|
371
|
+
projectId: ObjectID;
|
|
372
|
+
alertStateId: ObjectID;
|
|
373
|
+
}): Promise<boolean> {
|
|
374
|
+
// find all the states for this project and sort it by order. Then, check if this is the last state.
|
|
375
|
+
const alertStates: AlertState[] = await AlertStateService.findBy({
|
|
376
|
+
query: {
|
|
377
|
+
projectId: data.projectId,
|
|
378
|
+
},
|
|
379
|
+
limit: LIMIT_PER_PROJECT,
|
|
380
|
+
skip: 0,
|
|
381
|
+
sort: {
|
|
382
|
+
order: SortOrder.Ascending,
|
|
383
|
+
},
|
|
384
|
+
props: {
|
|
385
|
+
isRoot: true,
|
|
386
|
+
},
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
const alertState: AlertState | null =
|
|
390
|
+
alertStates.find((alertState: AlertState) => {
|
|
391
|
+
return alertState.id?.toString() === data.alertStateId.toString();
|
|
392
|
+
}) || null;
|
|
393
|
+
|
|
394
|
+
if (!alertState) {
|
|
395
|
+
throw new BadDataException("Alert state not found.");
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
const lastAlertState: AlertState | undefined =
|
|
399
|
+
alertStates[alertStates.length - 1];
|
|
400
|
+
|
|
401
|
+
if (lastAlertState && lastAlertState.id) {
|
|
402
|
+
return lastAlertState.id.toString() === alertState.id?.toString();
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
|
|
342
408
|
@CaptureSpan()
|
|
343
409
|
protected override async onBeforeDelete(
|
|
344
410
|
deleteBy: DeleteBy<AlertStateTimeline>,
|
|
@@ -22,6 +22,8 @@ import logger from "../Utils/Logger";
|
|
|
22
22
|
import IncidentFeedService from "./IncidentFeedService";
|
|
23
23
|
import { IncidentFeedEventType } from "../../Models/DatabaseModels/IncidentFeed";
|
|
24
24
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
25
|
+
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
26
|
+
import WorkspaceNotificationRuleService from "./WorkspaceNotificationRuleService";
|
|
25
27
|
|
|
26
28
|
export class Service extends DatabaseService<IncidentStateTimeline> {
|
|
27
29
|
public constructor() {
|
|
@@ -359,9 +361,73 @@ ${createdItem.rootCause}`,
|
|
|
359
361
|
logger.error(error);
|
|
360
362
|
});
|
|
361
363
|
|
|
364
|
+
const isLastIncidentState: boolean = await this.isLastIncidentState({
|
|
365
|
+
projectId: createdItem.projectId!,
|
|
366
|
+
incidentStateId: createdItem.incidentStateId,
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
if (isLastIncidentState) {
|
|
370
|
+
WorkspaceNotificationRuleService.archiveWorkspaceChannels({
|
|
371
|
+
projectId: createdItem.projectId!,
|
|
372
|
+
notificationFor: {
|
|
373
|
+
incidentId: createdItem.incidentId,
|
|
374
|
+
},
|
|
375
|
+
sendMessageBeforeArchiving: {
|
|
376
|
+
_type: "WorkspacePayloadMarkdown",
|
|
377
|
+
text: `**[Incident ${incidentNumber}](${(
|
|
378
|
+
await IncidentService.getIncidentLinkInDashboard(
|
|
379
|
+
createdItem.projectId!,
|
|
380
|
+
createdItem.incidentId!,
|
|
381
|
+
)
|
|
382
|
+
).toString()})** is resolved. Archiving channel.`,
|
|
383
|
+
},
|
|
384
|
+
}).catch((error: Error) => {
|
|
385
|
+
logger.error(`Error while archiving workspace channels:`);
|
|
386
|
+
logger.error(error);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
|
|
362
390
|
return createdItem;
|
|
363
391
|
}
|
|
364
392
|
|
|
393
|
+
private async isLastIncidentState(data: {
|
|
394
|
+
projectId: ObjectID;
|
|
395
|
+
incidentStateId: ObjectID;
|
|
396
|
+
}): Promise<boolean> {
|
|
397
|
+
// find all the states for this project and sort it by order. Then, check if this is the last state.
|
|
398
|
+
const incidentStates: IncidentState[] = await IncidentStateService.findBy({
|
|
399
|
+
query: {
|
|
400
|
+
projectId: data.projectId,
|
|
401
|
+
},
|
|
402
|
+
limit: LIMIT_PER_PROJECT,
|
|
403
|
+
skip: 0,
|
|
404
|
+
sort: {
|
|
405
|
+
order: SortOrder.Ascending,
|
|
406
|
+
},
|
|
407
|
+
props: {
|
|
408
|
+
isRoot: true,
|
|
409
|
+
},
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
const incidentState: IncidentState | null =
|
|
413
|
+
incidentStates.find((incidentState: IncidentState) => {
|
|
414
|
+
return incidentState.id?.toString() === data.incidentStateId.toString();
|
|
415
|
+
}) || null;
|
|
416
|
+
|
|
417
|
+
if (!incidentState) {
|
|
418
|
+
throw new BadDataException("Incident state not found.");
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const lastIncidentState: IncidentState | undefined =
|
|
422
|
+
incidentStates[incidentStates.length - 1];
|
|
423
|
+
|
|
424
|
+
if (lastIncidentState && lastIncidentState.id) {
|
|
425
|
+
return lastIncidentState.id.toString() === incidentState.id?.toString();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return false;
|
|
429
|
+
}
|
|
430
|
+
|
|
365
431
|
@CaptureSpan()
|
|
366
432
|
protected override async onBeforeDelete(
|
|
367
433
|
deleteBy: DeleteBy<IncidentStateTimeline>,
|
|
@@ -56,12 +56,17 @@ import Label from "../../Models/DatabaseModels/Label";
|
|
|
56
56
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
57
57
|
import WorkspaceType from "../../Types/Workspace/WorkspaceType";
|
|
58
58
|
import NotificationRuleWorkspaceChannel from "../../Types/Workspace/NotificationRules/NotificationRuleWorkspaceChannel";
|
|
59
|
-
import {
|
|
59
|
+
import WorkspaceNotificationRuleService, {
|
|
60
|
+
MessageBlocksByWorkspaceType,
|
|
61
|
+
} from "./WorkspaceNotificationRuleService";
|
|
60
62
|
import MonitorWorkspaceMessages from "../Utils/Workspace/WorkspaceMessages/Monitor";
|
|
61
63
|
import MonitorFeedService from "./MonitorFeedService";
|
|
62
64
|
import { MonitorFeedEventType } from "../../Models/DatabaseModels/MonitorFeed";
|
|
63
65
|
import { Gray500, Green500 } from "../../Types/BrandColors";
|
|
64
66
|
import LabelService from "./LabelService";
|
|
67
|
+
import QueryOperator from "../../Types/BaseDatabase/QueryOperator";
|
|
68
|
+
import { FindWhere } from "../../Types/BaseDatabase/Query";
|
|
69
|
+
import logger from "../Utils/Logger";
|
|
65
70
|
|
|
66
71
|
export class Service extends DatabaseService<Model> {
|
|
67
72
|
public constructor() {
|
|
@@ -85,6 +90,49 @@ export class Service extends DatabaseService<Model> {
|
|
|
85
90
|
isRoot: true,
|
|
86
91
|
},
|
|
87
92
|
});
|
|
93
|
+
|
|
94
|
+
let projectId: FindWhere<ObjectID> | QueryOperator<ObjectID> | undefined =
|
|
95
|
+
deleteBy.query.projectId || deleteBy.props.tenantId;
|
|
96
|
+
|
|
97
|
+
if (!projectId) {
|
|
98
|
+
// fetch this monitor from the database to get the projectId.
|
|
99
|
+
const monitor: Model | null = await this.findOneById({
|
|
100
|
+
id: new ObjectID(deleteBy.query._id as string) as ObjectID,
|
|
101
|
+
select: {
|
|
102
|
+
projectId: true,
|
|
103
|
+
},
|
|
104
|
+
props: {
|
|
105
|
+
isRoot: true,
|
|
106
|
+
},
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
if (!monitor) {
|
|
110
|
+
throw new BadDataException("Monitor not found.");
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (!monitor.id) {
|
|
114
|
+
throw new BadDataException("Monitor id not found.");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
projectId = monitor.projectId!;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
try {
|
|
121
|
+
await WorkspaceNotificationRuleService.archiveWorkspaceChannels({
|
|
122
|
+
projectId: projectId as ObjectID,
|
|
123
|
+
notificationFor: {
|
|
124
|
+
monitorId: new ObjectID(deleteBy.query._id as string) as ObjectID,
|
|
125
|
+
},
|
|
126
|
+
sendMessageBeforeArchiving: {
|
|
127
|
+
_type: "WorkspacePayloadMarkdown",
|
|
128
|
+
text: `🗑️ This monitor is deleted. The channel is being archived.`,
|
|
129
|
+
},
|
|
130
|
+
});
|
|
131
|
+
} catch (error) {
|
|
132
|
+
logger.error(
|
|
133
|
+
`Error while archiving workspace channels for monitor ${deleteBy.query._id}: ${error}`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
88
136
|
}
|
|
89
137
|
|
|
90
138
|
return { deleteBy, carryForward: null };
|
|
@@ -434,6 +482,29 @@ ${createdItem.description || "No description provided."}
|
|
|
434
482
|
feedInfoInMarkdown += `\n\n`;
|
|
435
483
|
}
|
|
436
484
|
|
|
485
|
+
// send message to workspaces - slack, teams, etc.
|
|
486
|
+
const workspaceResult: {
|
|
487
|
+
channelsCreated: Array<NotificationRuleWorkspaceChannel>;
|
|
488
|
+
} | null =
|
|
489
|
+
await MonitorWorkspaceMessages.createChannelsAndInviteUsersToChannels({
|
|
490
|
+
projectId: createdItem.projectId,
|
|
491
|
+
monitorId: createdItem.id!,
|
|
492
|
+
monitorName: createdItem.name!,
|
|
493
|
+
});
|
|
494
|
+
|
|
495
|
+
if (workspaceResult && workspaceResult.channelsCreated?.length > 0) {
|
|
496
|
+
// update incident with these channels.
|
|
497
|
+
await this.updateOneById({
|
|
498
|
+
id: createdItem.id!,
|
|
499
|
+
data: {
|
|
500
|
+
postUpdatesToWorkspaceChannels: workspaceResult.channelsCreated || [],
|
|
501
|
+
},
|
|
502
|
+
props: {
|
|
503
|
+
isRoot: true,
|
|
504
|
+
},
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
|
|
437
508
|
const monitorCreateMessageBlocks: Array<MessageBlocksByWorkspaceType> =
|
|
438
509
|
await MonitorWorkspaceMessages.getMonitorCreateMessageBlocks({
|
|
439
510
|
monitorId: createdItem.id!,
|
|
@@ -27,6 +27,8 @@ import ScheduledMaintenanceFeedService from "./ScheduledMaintenanceFeedService";
|
|
|
27
27
|
import { ScheduledMaintenanceFeedEventType } from "../../Models/DatabaseModels/ScheduledMaintenanceFeed";
|
|
28
28
|
import logger from "../Utils/Logger";
|
|
29
29
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
30
|
+
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
31
|
+
import WorkspaceNotificationRuleService from "./WorkspaceNotificationRuleService";
|
|
30
32
|
|
|
31
33
|
export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline> {
|
|
32
34
|
public constructor() {
|
|
@@ -36,6 +38,53 @@ export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline>
|
|
|
36
38
|
}
|
|
37
39
|
}
|
|
38
40
|
|
|
41
|
+
private async isLastScheduledMaintenanceState(data: {
|
|
42
|
+
projectId: ObjectID;
|
|
43
|
+
scheduledMaintenanceStateId: ObjectID;
|
|
44
|
+
}): Promise<boolean> {
|
|
45
|
+
// find all the states for this project and sort it by order. Then, check if this is the last state.
|
|
46
|
+
const scheduledMaintenanceStates: ScheduledMaintenanceState[] =
|
|
47
|
+
await ScheduledMaintenanceStateService.findBy({
|
|
48
|
+
query: {
|
|
49
|
+
projectId: data.projectId,
|
|
50
|
+
},
|
|
51
|
+
limit: LIMIT_PER_PROJECT,
|
|
52
|
+
skip: 0,
|
|
53
|
+
sort: {
|
|
54
|
+
order: SortOrder.Ascending,
|
|
55
|
+
},
|
|
56
|
+
props: {
|
|
57
|
+
isRoot: true,
|
|
58
|
+
},
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const scheduledMaintenanceState: ScheduledMaintenanceState | null =
|
|
62
|
+
scheduledMaintenanceStates.find(
|
|
63
|
+
(scheduledMaintenanceState: ScheduledMaintenanceState) => {
|
|
64
|
+
return (
|
|
65
|
+
scheduledMaintenanceState.id?.toString() ===
|
|
66
|
+
data.scheduledMaintenanceStateId.toString()
|
|
67
|
+
);
|
|
68
|
+
},
|
|
69
|
+
) || null;
|
|
70
|
+
|
|
71
|
+
if (!scheduledMaintenanceState) {
|
|
72
|
+
throw new BadDataException("ScheduledMaintenance state not found.");
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const lastScheduledMaintenanceState: ScheduledMaintenanceState | undefined =
|
|
76
|
+
scheduledMaintenanceStates[scheduledMaintenanceStates.length - 1];
|
|
77
|
+
|
|
78
|
+
if (lastScheduledMaintenanceState && lastScheduledMaintenanceState.id) {
|
|
79
|
+
return (
|
|
80
|
+
lastScheduledMaintenanceState.id.toString() ===
|
|
81
|
+
scheduledMaintenanceState.id?.toString()
|
|
82
|
+
);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return false;
|
|
86
|
+
}
|
|
87
|
+
|
|
39
88
|
@CaptureSpan()
|
|
40
89
|
protected override async onBeforeCreate(
|
|
41
90
|
createBy: CreateBy<ScheduledMaintenanceStateTimeline>,
|
|
@@ -361,6 +410,33 @@ export class Service extends DatabaseService<ScheduledMaintenanceStateTimeline>
|
|
|
361
410
|
await this.enableActiveMonitoringForMonitors(scheduledMaintenanceEvent!);
|
|
362
411
|
}
|
|
363
412
|
|
|
413
|
+
const isLastScheduledMaintenanceState: boolean =
|
|
414
|
+
await this.isLastScheduledMaintenanceState({
|
|
415
|
+
projectId: createdItem.projectId!,
|
|
416
|
+
scheduledMaintenanceStateId: createdItem.scheduledMaintenanceStateId,
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
if (isLastScheduledMaintenanceState) {
|
|
420
|
+
WorkspaceNotificationRuleService.archiveWorkspaceChannels({
|
|
421
|
+
projectId: createdItem.projectId!,
|
|
422
|
+
notificationFor: {
|
|
423
|
+
scheduledMaintenanceId: createdItem.scheduledMaintenanceId,
|
|
424
|
+
},
|
|
425
|
+
sendMessageBeforeArchiving: {
|
|
426
|
+
_type: "WorkspacePayloadMarkdown",
|
|
427
|
+
text: `**[Scheduled Event ${scheduledMaintenanceNumber}](${(
|
|
428
|
+
await ScheduledMaintenanceService.getScheduledMaintenanceLinkInDashboard(
|
|
429
|
+
createdItem.projectId!,
|
|
430
|
+
createdItem.scheduledMaintenanceId!,
|
|
431
|
+
)
|
|
432
|
+
).toString()})** is complete. Archiving channel.`,
|
|
433
|
+
},
|
|
434
|
+
}).catch((error: Error) => {
|
|
435
|
+
logger.error(`Error while archiving workspace channels:`);
|
|
436
|
+
logger.error(error);
|
|
437
|
+
});
|
|
438
|
+
}
|
|
439
|
+
|
|
364
440
|
return createdItem;
|
|
365
441
|
}
|
|
366
442
|
|
|
@@ -264,6 +264,87 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
|
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
266
|
|
|
267
|
+
@CaptureSpan()
|
|
268
|
+
public async archiveWorkspaceChannels(data: {
|
|
269
|
+
projectId: ObjectID;
|
|
270
|
+
notificationFor: NotificationFor;
|
|
271
|
+
sendMessageBeforeArchiving: WorkspacePayloadMarkdown;
|
|
272
|
+
}): Promise<void> {
|
|
273
|
+
const workspaceTypes: Array<WorkspaceType> = Service.getAllWorkspaceTypes();
|
|
274
|
+
|
|
275
|
+
for (const workspaceType of workspaceTypes) {
|
|
276
|
+
const notificationRules: Array<WorkspaceNotificationRule> =
|
|
277
|
+
await this.getMatchingNotificationRules({
|
|
278
|
+
projectId: data.projectId,
|
|
279
|
+
notificationFor: data.notificationFor,
|
|
280
|
+
workspaceType: workspaceType,
|
|
281
|
+
notificationRuleEventType: this.getNotificationRuleEventType(
|
|
282
|
+
data.notificationFor,
|
|
283
|
+
),
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
// check if any of these rules have archive channel to true.
|
|
287
|
+
let shouldArchiveChannel: boolean = false;
|
|
288
|
+
|
|
289
|
+
for (const notificationRule of notificationRules) {
|
|
290
|
+
const rule: CreateChannelNotificationRule =
|
|
291
|
+
notificationRule.notificationRule as CreateChannelNotificationRule;
|
|
292
|
+
if (rule && rule.archiveChannelAutomatically) {
|
|
293
|
+
shouldArchiveChannel = true;
|
|
294
|
+
break;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
if (!shouldArchiveChannel) {
|
|
299
|
+
continue; // check next workspace type.
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
const channels: Array<WorkspaceChannel> =
|
|
303
|
+
await this.getWorkspaceChannelsByNotificationFor({
|
|
304
|
+
projectId: data.projectId,
|
|
305
|
+
notificationFor: data.notificationFor,
|
|
306
|
+
workspaceType: workspaceType,
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const channelIds: Array<string> = channels.map(
|
|
310
|
+
(channel: WorkspaceChannel) => {
|
|
311
|
+
return channel.id;
|
|
312
|
+
},
|
|
313
|
+
);
|
|
314
|
+
|
|
315
|
+
// get project auth token.
|
|
316
|
+
const projectAuth: WorkspaceProjectAuthToken | null =
|
|
317
|
+
await WorkspaceProjectAuthTokenService.findOneBy({
|
|
318
|
+
query: {
|
|
319
|
+
projectId: data.projectId,
|
|
320
|
+
workspaceType: workspaceType,
|
|
321
|
+
},
|
|
322
|
+
select: {
|
|
323
|
+
authToken: true,
|
|
324
|
+
miscData: true,
|
|
325
|
+
},
|
|
326
|
+
props: {
|
|
327
|
+
isRoot: true,
|
|
328
|
+
},
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
if (!projectAuth || !projectAuth.authToken) {
|
|
332
|
+
logger.debug("No project auth found for workspace type");
|
|
333
|
+
continue;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
await WorkspaceUtil.getWorkspaceTypeUtil(workspaceType).archiveChannels({
|
|
337
|
+
authToken: projectAuth.authToken!,
|
|
338
|
+
channelIds: channelIds,
|
|
339
|
+
userId: this.getBotUserIdFromprojectAuthToken({
|
|
340
|
+
projectAuthToken: projectAuth,
|
|
341
|
+
workspaceType: workspaceType,
|
|
342
|
+
}),
|
|
343
|
+
sendMessageBeforeArchiving: data.sendMessageBeforeArchiving,
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
267
348
|
@CaptureSpan()
|
|
268
349
|
public async sendWorkspaceMarkdownNotification(data: {
|
|
269
350
|
projectId: ObjectID;
|
|
@@ -316,43 +397,12 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
|
|
|
316
397
|
notificationFor: data.notificationFor,
|
|
317
398
|
});
|
|
318
399
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
monitorId: data.notificationFor.monitorId,
|
|
324
|
-
workspaceType: messageBlocksByWorkspaceType.workspaceType,
|
|
325
|
-
});
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
// incidents
|
|
329
|
-
if (data.notificationFor.incidentId) {
|
|
330
|
-
monitorChannels = await IncidentService.getWorkspaceChannelForIncident({
|
|
331
|
-
incidentId: data.notificationFor.incidentId,
|
|
332
|
-
workspaceType: messageBlocksByWorkspaceType.workspaceType,
|
|
333
|
-
});
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
// alerts
|
|
337
|
-
if (data.notificationFor.alertId) {
|
|
338
|
-
monitorChannels = await AlertService.getWorkspaceChannelForAlert({
|
|
339
|
-
alertId: data.notificationFor.alertId,
|
|
400
|
+
const monitorChannels: Array<WorkspaceChannel> =
|
|
401
|
+
await this.getWorkspaceChannelsByNotificationFor({
|
|
402
|
+
projectId: data.projectId,
|
|
403
|
+
notificationFor: data.notificationFor,
|
|
340
404
|
workspaceType: messageBlocksByWorkspaceType.workspaceType,
|
|
341
405
|
});
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
// scheduled maintenance
|
|
345
|
-
|
|
346
|
-
if (data.notificationFor.scheduledMaintenanceId) {
|
|
347
|
-
monitorChannels =
|
|
348
|
-
await ScheduledMaintenanceService.getWorkspaceChannelForScheduledMaintenance(
|
|
349
|
-
{
|
|
350
|
-
scheduledMaintenanceId:
|
|
351
|
-
data.notificationFor.scheduledMaintenanceId,
|
|
352
|
-
workspaceType: messageBlocksByWorkspaceType.workspaceType,
|
|
353
|
-
},
|
|
354
|
-
);
|
|
355
|
-
}
|
|
356
406
|
|
|
357
407
|
const workspaceMessagePayload: WorkspaceMessagePayload = {
|
|
358
408
|
_type: "WorkspaceMessagePayload",
|
|
@@ -374,6 +424,49 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
|
|
|
374
424
|
});
|
|
375
425
|
}
|
|
376
426
|
|
|
427
|
+
private async getWorkspaceChannelsByNotificationFor(data: {
|
|
428
|
+
projectId: ObjectID;
|
|
429
|
+
notificationFor: NotificationFor;
|
|
430
|
+
workspaceType: WorkspaceType;
|
|
431
|
+
}): Promise<Array<WorkspaceChannel>> {
|
|
432
|
+
let monitorChannels: Array<WorkspaceChannel> = [];
|
|
433
|
+
|
|
434
|
+
if (data.notificationFor.monitorId) {
|
|
435
|
+
monitorChannels = await MonitorService.getWorkspaceChannelForMonitor({
|
|
436
|
+
monitorId: data.notificationFor.monitorId,
|
|
437
|
+
workspaceType: data.workspaceType,
|
|
438
|
+
});
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// incidents
|
|
442
|
+
if (data.notificationFor.incidentId) {
|
|
443
|
+
monitorChannels = await IncidentService.getWorkspaceChannelForIncident({
|
|
444
|
+
incidentId: data.notificationFor.incidentId,
|
|
445
|
+
workspaceType: data.workspaceType,
|
|
446
|
+
});
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
// alerts
|
|
450
|
+
if (data.notificationFor.alertId) {
|
|
451
|
+
monitorChannels = await AlertService.getWorkspaceChannelForAlert({
|
|
452
|
+
alertId: data.notificationFor.alertId,
|
|
453
|
+
workspaceType: data.workspaceType,
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// scheduled maintenance
|
|
458
|
+
if (data.notificationFor.scheduledMaintenanceId) {
|
|
459
|
+
monitorChannels =
|
|
460
|
+
await ScheduledMaintenanceService.getWorkspaceChannelForScheduledMaintenance(
|
|
461
|
+
{
|
|
462
|
+
scheduledMaintenanceId: data.notificationFor.scheduledMaintenanceId,
|
|
463
|
+
workspaceType: data.workspaceType,
|
|
464
|
+
},
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
return monitorChannels;
|
|
468
|
+
}
|
|
469
|
+
|
|
377
470
|
private getNotificationRuleEventType(
|
|
378
471
|
notificationFor: NotificationFor,
|
|
379
472
|
): NotificationRuleEventType {
|
|
@@ -158,6 +158,65 @@ export default class SlackUtil extends WorkspaceBase {
|
|
|
158
158
|
});
|
|
159
159
|
}
|
|
160
160
|
|
|
161
|
+
@CaptureSpan()
|
|
162
|
+
public static override async archiveChannels(data: {
|
|
163
|
+
userId: string;
|
|
164
|
+
channelIds: Array<string>;
|
|
165
|
+
authToken: string;
|
|
166
|
+
sendMessageBeforeArchiving: WorkspacePayloadMarkdown;
|
|
167
|
+
}): Promise<void> {
|
|
168
|
+
if (data.sendMessageBeforeArchiving) {
|
|
169
|
+
await this.sendMessage({
|
|
170
|
+
workspaceMessagePayload: {
|
|
171
|
+
_type: "WorkspaceMessagePayload",
|
|
172
|
+
channelNames: [],
|
|
173
|
+
channelIds: data.channelIds,
|
|
174
|
+
messageBlocks: [data.sendMessageBeforeArchiving],
|
|
175
|
+
workspaceType: WorkspaceType.Slack,
|
|
176
|
+
},
|
|
177
|
+
authToken: data.authToken,
|
|
178
|
+
userId: data.userId,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
logger.debug("Archiving channels with data:");
|
|
183
|
+
logger.debug(data);
|
|
184
|
+
|
|
185
|
+
for (const channelId of data.channelIds) {
|
|
186
|
+
const response: HTTPErrorResponse | HTTPResponse<JSONObject> =
|
|
187
|
+
await API.post(
|
|
188
|
+
URL.fromString("https://slack.com/api/conversations.archive"),
|
|
189
|
+
{
|
|
190
|
+
channel: channelId,
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
Authorization: `Bearer ${data.authToken}`,
|
|
194
|
+
["Content-Type"]: "application/x-www-form-urlencoded",
|
|
195
|
+
},
|
|
196
|
+
);
|
|
197
|
+
|
|
198
|
+
logger.debug("Response from Slack API for archiving channel:");
|
|
199
|
+
logger.debug(response);
|
|
200
|
+
|
|
201
|
+
if (response instanceof HTTPErrorResponse) {
|
|
202
|
+
logger.error("Error response from Slack API:");
|
|
203
|
+
logger.error(response);
|
|
204
|
+
throw response;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if ((response.jsonData as JSONObject)?.["ok"] !== true) {
|
|
208
|
+
logger.error("Invalid response from Slack API:");
|
|
209
|
+
logger.error(response.jsonData);
|
|
210
|
+
const messageFromSlack: string = (response.jsonData as JSONObject)?.[
|
|
211
|
+
"error"
|
|
212
|
+
] as string;
|
|
213
|
+
throw new BadRequestException("Error from Slack " + messageFromSlack);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
logger.debug("Channels archived successfully.");
|
|
218
|
+
}
|
|
219
|
+
|
|
161
220
|
@CaptureSpan()
|
|
162
221
|
public static override async joinChannel(data: {
|
|
163
222
|
authToken: string;
|
|
@@ -57,6 +57,16 @@ export default class WorkspaceBase {
|
|
|
57
57
|
throw new NotImplementedException();
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
@CaptureSpan()
|
|
61
|
+
public static async archiveChannels(_data: {
|
|
62
|
+
channelIds: Array<string>;
|
|
63
|
+
authToken: string;
|
|
64
|
+
userId: string;
|
|
65
|
+
sendMessageBeforeArchiving: WorkspacePayloadMarkdown;
|
|
66
|
+
}): Promise<void> {
|
|
67
|
+
throw new NotImplementedException();
|
|
68
|
+
}
|
|
69
|
+
|
|
60
70
|
@CaptureSpan()
|
|
61
71
|
public static async getUsernameFromUserId(_data: {
|
|
62
72
|
authToken: string;
|
|
@@ -11,4 +11,8 @@ export default interface CreateChannelNotificationRule
|
|
|
11
11
|
inviteUsersToNewChannel: Array<ObjectID>;
|
|
12
12
|
shouldInviteOwnersToNewChannel: boolean;
|
|
13
13
|
newChannelTemplateName: string;
|
|
14
|
+
|
|
15
|
+
// if selected this would archive channel when incident, alert, scheduled maintenance reaches the last state (is resolved)
|
|
16
|
+
// This would only archive the channel which was created.
|
|
17
|
+
archiveChannelAutomatically: boolean;
|
|
14
18
|
}
|