@oneuptime/common 10.0.35 → 10.0.37

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.
Files changed (113) hide show
  1. package/Models/DatabaseModels/Index.ts +2 -0
  2. package/Models/DatabaseModels/WorkspaceNotificationSummary.ts +819 -0
  3. package/Server/API/StatusPageAPI.ts +7 -0
  4. package/Server/API/WorkspaceNotificationSummaryAPI.ts +67 -0
  5. package/Server/Infrastructure/Postgres/SchemaMigrations/{1773761409952-MigrationName.ts → 1774000000001-MigrationName.ts} +2 -2
  6. package/Server/Infrastructure/Postgres/SchemaMigrations/1774355321449-MigrationName.ts +51 -0
  7. package/Server/Infrastructure/Postgres/SchemaMigrations/1774357353502-MigrationName.ts +29 -0
  8. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -2
  9. package/Server/Middleware/MasterAdminAuthorization.ts +55 -0
  10. package/Server/Services/Index.ts +2 -0
  11. package/Server/Services/WorkspaceNotificationSummaryService.ts +1450 -0
  12. package/Server/Types/Markdown.ts +11 -3
  13. package/Server/Utils/Greenlock/Greenlock.ts +1 -0
  14. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +4 -1
  15. package/Types/Code/CodeType.ts +1 -1
  16. package/Types/Metrics/MetricQueryConfigData.ts +1 -0
  17. package/Types/Monitor/CriteriaFilter.ts +19 -0
  18. package/Types/Monitor/KubernetesAlertTemplates.ts +703 -0
  19. package/Types/Monitor/KubernetesMetricCatalog.ts +347 -0
  20. package/Types/Monitor/MonitorCriteriaInstance.ts +86 -0
  21. package/Types/Monitor/MonitorStep.ts +36 -1
  22. package/Types/Monitor/MonitorStepKubernetesMonitor.ts +50 -0
  23. package/Types/Monitor/MonitorType.ts +14 -10
  24. package/Types/Permission.ts +42 -0
  25. package/Types/Workspace/NotificationSummary/WorkspaceNotificationSummaryItem.ts +13 -0
  26. package/Types/Workspace/NotificationSummary/WorkspaceNotificationSummaryType.ts +8 -0
  27. package/UI/Components/AlertBanner/AlertBanner.tsx +69 -0
  28. package/UI/Components/ConditionsTable/ConditionsTable.tsx +149 -0
  29. package/UI/Components/Dictionary/DictionaryOfStingsViewer.tsx +35 -15
  30. package/UI/Components/ExpandableText/ExpandableText.tsx +42 -0
  31. package/UI/Components/FilterButtons/FilterButtons.tsx +60 -0
  32. package/UI/Components/GanttChart/Bar/Index.tsx +23 -5
  33. package/UI/Components/Markdown.tsx/MarkdownEditor.tsx +4 -1
  34. package/UI/Components/ResourceUsageBar/ResourceUsageBar.tsx +58 -0
  35. package/UI/Components/StackedProgressBar/StackedProgressBar.tsx +81 -0
  36. package/UI/Components/StatusBadge/StatusBadge.tsx +44 -0
  37. package/UI/Components/Tabs/Tabs.tsx +36 -8
  38. package/UI/Utils/Dropdown.ts +2 -1
  39. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  40. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  41. package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js +857 -0
  42. package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js.map +1 -0
  43. package/build/dist/Server/API/StatusPageAPI.js +2 -0
  44. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  45. package/build/dist/Server/API/WorkspaceNotificationSummaryAPI.js +40 -0
  46. package/build/dist/Server/API/WorkspaceNotificationSummaryAPI.js.map +1 -0
  47. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/{1773761409952-MigrationName.js → 1774000000001-MigrationName.js} +3 -3
  48. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/{1773761409952-MigrationName.js.map → 1774000000001-MigrationName.js.map} +1 -1
  49. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774355321449-MigrationName.js +24 -0
  50. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774355321449-MigrationName.js.map +1 -0
  51. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774357353502-MigrationName.js +16 -0
  52. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1774357353502-MigrationName.js.map +1 -0
  53. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -2
  54. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  55. package/build/dist/Server/Middleware/MasterAdminAuthorization.js +25 -0
  56. package/build/dist/Server/Middleware/MasterAdminAuthorization.js.map +1 -0
  57. package/build/dist/Server/Services/Index.js +2 -0
  58. package/build/dist/Server/Services/Index.js.map +1 -1
  59. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js +1122 -0
  60. package/build/dist/Server/Services/WorkspaceNotificationSummaryService.js.map +1 -0
  61. package/build/dist/Server/Types/Markdown.js +10 -2
  62. package/build/dist/Server/Types/Markdown.js.map +1 -1
  63. package/build/dist/Server/Utils/Greenlock/Greenlock.js +1 -0
  64. package/build/dist/Server/Utils/Greenlock/Greenlock.js.map +1 -1
  65. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +2 -1
  66. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  67. package/build/dist/Types/Code/CodeType.js +1 -1
  68. package/build/dist/Types/Code/CodeType.js.map +1 -1
  69. package/build/dist/Types/Monitor/CriteriaFilter.js +18 -0
  70. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  71. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +594 -0
  72. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -0
  73. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +311 -0
  74. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -0
  75. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +78 -0
  76. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  77. package/build/dist/Types/Monitor/MonitorStep.js +24 -1
  78. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  79. package/build/dist/Types/Monitor/MonitorStepKubernetesMonitor.js +30 -0
  80. package/build/dist/Types/Monitor/MonitorStepKubernetesMonitor.js.map +1 -0
  81. package/build/dist/Types/Monitor/MonitorType.js +13 -10
  82. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  83. package/build/dist/Types/Permission.js +36 -0
  84. package/build/dist/Types/Permission.js.map +1 -1
  85. package/build/dist/Types/Workspace/NotificationSummary/WorkspaceNotificationSummaryItem.js +14 -0
  86. package/build/dist/Types/Workspace/NotificationSummary/WorkspaceNotificationSummaryItem.js.map +1 -0
  87. package/build/dist/Types/Workspace/NotificationSummary/WorkspaceNotificationSummaryType.js +9 -0
  88. package/build/dist/Types/Workspace/NotificationSummary/WorkspaceNotificationSummaryType.js.map +1 -0
  89. package/build/dist/UI/Components/AlertBanner/AlertBanner.js +42 -0
  90. package/build/dist/UI/Components/AlertBanner/AlertBanner.js.map +1 -0
  91. package/build/dist/UI/Components/ConditionsTable/ConditionsTable.js +83 -0
  92. package/build/dist/UI/Components/ConditionsTable/ConditionsTable.js.map +1 -0
  93. package/build/dist/UI/Components/Dictionary/DictionaryOfStingsViewer.js +14 -8
  94. package/build/dist/UI/Components/Dictionary/DictionaryOfStingsViewer.js.map +1 -1
  95. package/build/dist/UI/Components/ExpandableText/ExpandableText.js +19 -0
  96. package/build/dist/UI/Components/ExpandableText/ExpandableText.js.map +1 -0
  97. package/build/dist/UI/Components/FilterButtons/FilterButtons.js +17 -0
  98. package/build/dist/UI/Components/FilterButtons/FilterButtons.js.map +1 -0
  99. package/build/dist/UI/Components/GanttChart/Bar/Index.js +15 -3
  100. package/build/dist/UI/Components/GanttChart/Bar/Index.js.map +1 -1
  101. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js +3 -1
  102. package/build/dist/UI/Components/Markdown.tsx/MarkdownEditor.js.map +1 -1
  103. package/build/dist/UI/Components/ResourceUsageBar/ResourceUsageBar.js +23 -0
  104. package/build/dist/UI/Components/ResourceUsageBar/ResourceUsageBar.js.map +1 -0
  105. package/build/dist/UI/Components/StackedProgressBar/StackedProgressBar.js +34 -0
  106. package/build/dist/UI/Components/StackedProgressBar/StackedProgressBar.js.map +1 -0
  107. package/build/dist/UI/Components/StatusBadge/StatusBadge.js +22 -0
  108. package/build/dist/UI/Components/StatusBadge/StatusBadge.js.map +1 -0
  109. package/build/dist/UI/Components/Tabs/Tabs.js +32 -9
  110. package/build/dist/UI/Components/Tabs/Tabs.js.map +1 -1
  111. package/build/dist/UI/Utils/Dropdown.js +2 -1
  112. package/build/dist/UI/Utils/Dropdown.js.map +1 -1
  113. package/package.json +1 -1
@@ -0,0 +1,1122 @@
1
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6
+ };
7
+ var __metadata = (this && this.__metadata) || function (k, v) {
8
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
9
+ };
10
+ import ObjectID from "../../Types/ObjectID";
11
+ import DatabaseService from "./DatabaseService";
12
+ import WorkspaceNotificationSummary from "../../Models/DatabaseModels/WorkspaceNotificationSummary";
13
+ import WorkspaceNotificationSummaryType from "../../Types/Workspace/NotificationSummary/WorkspaceNotificationSummaryType";
14
+ import WorkspaceNotificationSummaryItem from "../../Types/Workspace/NotificationSummary/WorkspaceNotificationSummaryItem";
15
+ import BadDataException from "../../Types/Exception/BadDataException";
16
+ import IncidentService from "./IncidentService";
17
+ import AlertService from "./AlertService";
18
+ import IncidentEpisodeService from "./IncidentEpisodeService";
19
+ import AlertEpisodeService from "./AlertEpisodeService";
20
+ import IncidentStateTimelineService from "./IncidentStateTimelineService";
21
+ import AlertStateTimelineService from "./AlertStateTimelineService";
22
+ import WorkspaceNotificationLogService from "./WorkspaceNotificationLogService";
23
+ import WorkspaceNotificationStatus from "../../Types/Workspace/WorkspaceNotificationStatus";
24
+ import WorkspaceNotificationActionType from "../../Types/Workspace/WorkspaceNotificationActionType";
25
+ import logger from "../Utils/Logger";
26
+ import OneUptimeDate from "../../Types/Date";
27
+ import QueryHelper from "../Types/Database/QueryHelper";
28
+ import WorkspaceUtil from "../Utils/Workspace/Workspace";
29
+ import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
30
+ import URL from "../../Types/API/URL";
31
+ import DatabaseConfig from "../DatabaseConfig";
32
+ import { NotificationRuleConditionCheckOn, } from "../../Types/Workspace/NotificationRules/NotificationRuleCondition";
33
+ import FilterCondition from "../../Types/Filter/FilterCondition";
34
+ import { WorkspaceNotificationRuleUtil } from "../../Types/Workspace/NotificationRules/NotificationRuleUtil";
35
+ export class Service extends DatabaseService {
36
+ constructor() {
37
+ super(WorkspaceNotificationSummary);
38
+ }
39
+ async testSummary(data) {
40
+ await this.sendSummary({ summaryId: data.summaryId, isTest: true });
41
+ }
42
+ async sendSummary(data) {
43
+ const summary = await this.findOneById({
44
+ id: data.summaryId,
45
+ select: {
46
+ projectId: true,
47
+ name: true,
48
+ workspaceType: true,
49
+ summaryType: true,
50
+ recurringInterval: true,
51
+ numberOfDaysOfData: true,
52
+ channelNames: true,
53
+ teamName: true,
54
+ summaryItems: true,
55
+ filters: true,
56
+ filterCondition: true,
57
+ },
58
+ props: {
59
+ isRoot: true,
60
+ },
61
+ });
62
+ if (!summary) {
63
+ throw new BadDataException("Summary not found");
64
+ }
65
+ if (!summary.projectId) {
66
+ throw new BadDataException("Summary project ID not found");
67
+ }
68
+ if (!summary.channelNames || summary.channelNames.length === 0) {
69
+ throw new BadDataException("No channel names configured for summary");
70
+ }
71
+ if (!summary.summaryItems || summary.summaryItems.length === 0) {
72
+ throw new BadDataException("No summary items selected");
73
+ }
74
+ const messageBlocks = await this.buildSummaryMessageBlocks({ summary });
75
+ const messagePayload = {
76
+ _type: "WorkspaceMessagePayload",
77
+ channelNames: summary.channelNames,
78
+ channelIds: [],
79
+ messageBlocks: messageBlocks,
80
+ workspaceType: summary.workspaceType,
81
+ teamId: summary.teamName || undefined,
82
+ };
83
+ try {
84
+ await WorkspaceUtil.postMessageToAllWorkspaceChannelsAsBot({
85
+ projectId: summary.projectId,
86
+ messagePayloadsByWorkspace: [messagePayload],
87
+ });
88
+ // Log successful send
89
+ for (const channelName of summary.channelNames) {
90
+ await WorkspaceNotificationLogService.createWorkspaceLog({
91
+ projectId: summary.projectId,
92
+ workspaceType: summary.workspaceType,
93
+ channelName: channelName,
94
+ actionType: WorkspaceNotificationActionType.SendMessage,
95
+ status: WorkspaceNotificationStatus.Success,
96
+ statusMessage: data.isTest
97
+ ? "Test summary sent successfully"
98
+ : "Summary sent successfully",
99
+ message: `${summary.summaryType || ""} summary "${summary.name || "Untitled"}" sent to channel "${channelName}"`,
100
+ }, { isRoot: true });
101
+ }
102
+ }
103
+ catch (err) {
104
+ // Log failed send
105
+ for (const channelName of summary.channelNames) {
106
+ await WorkspaceNotificationLogService.createWorkspaceLog({
107
+ projectId: summary.projectId,
108
+ workspaceType: summary.workspaceType,
109
+ channelName: channelName,
110
+ actionType: WorkspaceNotificationActionType.SendMessage,
111
+ status: WorkspaceNotificationStatus.Error,
112
+ statusMessage: err instanceof Error ? err.message : "Unknown error",
113
+ message: `Failed to send ${summary.summaryType || ""} summary "${summary.name || "Untitled"}" to channel "${channelName}"`,
114
+ }, { isRoot: true }).catch((logErr) => {
115
+ logger.error("Failed to create workspace notification log for summary send failure");
116
+ logger.error(logErr);
117
+ });
118
+ }
119
+ throw err; // Re-throw so caller knows it failed
120
+ }
121
+ if (!data.isTest) {
122
+ await this.updateOneById({
123
+ id: data.summaryId,
124
+ data: {
125
+ lastSentAt: OneUptimeDate.getCurrentDate(),
126
+ },
127
+ props: {
128
+ isRoot: true,
129
+ },
130
+ });
131
+ }
132
+ }
133
+ // ───────────────────────── helpers ─────────────────────────
134
+ static divider() {
135
+ return { _type: "WorkspacePayloadDivider" };
136
+ }
137
+ static header(text) {
138
+ return { _type: "WorkspacePayloadHeader", text };
139
+ }
140
+ static md(text) {
141
+ return { _type: "WorkspacePayloadMarkdown", text };
142
+ }
143
+ static bold(text) {
144
+ return `**${text}**`;
145
+ }
146
+ static link(url, text) {
147
+ return `[${text}](${url})`;
148
+ }
149
+ static formatDuration(totalMinutes) {
150
+ if (totalMinutes < 1) {
151
+ return "< 1m";
152
+ }
153
+ const days = Math.floor(totalMinutes / 1440);
154
+ const hours = Math.floor((totalMinutes % 1440) / 60);
155
+ const mins = Math.round(totalMinutes % 60);
156
+ const parts = [];
157
+ if (days > 0) {
158
+ parts.push(`${days}d`);
159
+ }
160
+ if (hours > 0) {
161
+ parts.push(`${hours}h`);
162
+ }
163
+ if (mins > 0 || parts.length === 0) {
164
+ parts.push(`${mins}m`);
165
+ }
166
+ return parts.join(" ");
167
+ }
168
+ static formatDate(date) {
169
+ return OneUptimeDate.getDateAsLocalFormattedString(date, true);
170
+ }
171
+ static has(items, item) {
172
+ return items.includes(item);
173
+ }
174
+ // Check if an item matches the summary's filter conditions
175
+ static matchesFilters(data) {
176
+ if (!data.filters || data.filters.length === 0) {
177
+ return true; // no filters = include everything
178
+ }
179
+ const rule = {
180
+ filters: data.filters,
181
+ filterCondition: data.filterCondition || FilterCondition.Any,
182
+ };
183
+ return WorkspaceNotificationRuleUtil.isRuleMatching({
184
+ notificationRule: rule,
185
+ values: data.values,
186
+ });
187
+ }
188
+ // Build values map for an incident
189
+ static buildIncidentValues(incident) {
190
+ var _a, _b, _c, _d, _e, _f;
191
+ return {
192
+ [NotificationRuleConditionCheckOn.IncidentTitle]: incident.title || "",
193
+ [NotificationRuleConditionCheckOn.IncidentDescription]: incident.description || "",
194
+ [NotificationRuleConditionCheckOn.IncidentSeverity]: ((_b = (_a = incident.incidentSeverity) === null || _a === void 0 ? void 0 : _a._id) === null || _b === void 0 ? void 0 : _b.toString()) || "",
195
+ [NotificationRuleConditionCheckOn.IncidentState]: ((_d = (_c = incident.currentIncidentState) === null || _c === void 0 ? void 0 : _c._id) === null || _d === void 0 ? void 0 : _d.toString()) || "",
196
+ [NotificationRuleConditionCheckOn.IncidentLabels]: ((_e = incident.labels) === null || _e === void 0 ? void 0 : _e.map((l) => {
197
+ var _a;
198
+ return ((_a = l._id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
199
+ })) || [],
200
+ [NotificationRuleConditionCheckOn.Monitors]: ((_f = incident.monitors) === null || _f === void 0 ? void 0 : _f.map((m) => {
201
+ var _a;
202
+ return ((_a = m._id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
203
+ })) || [],
204
+ // unused for incidents
205
+ [NotificationRuleConditionCheckOn.MonitorName]: undefined,
206
+ [NotificationRuleConditionCheckOn.MonitorType]: undefined,
207
+ [NotificationRuleConditionCheckOn.MonitorStatus]: undefined,
208
+ [NotificationRuleConditionCheckOn.AlertTitle]: undefined,
209
+ [NotificationRuleConditionCheckOn.AlertDescription]: undefined,
210
+ [NotificationRuleConditionCheckOn.AlertSeverity]: undefined,
211
+ [NotificationRuleConditionCheckOn.AlertState]: undefined,
212
+ [NotificationRuleConditionCheckOn.AlertLabels]: undefined,
213
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceTitle]: undefined,
214
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceDescription]: undefined,
215
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceState]: undefined,
216
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceLabels]: undefined,
217
+ [NotificationRuleConditionCheckOn.MonitorLabels]: undefined,
218
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyName]: undefined,
219
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyDescription]: undefined,
220
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyLabels]: undefined,
221
+ [NotificationRuleConditionCheckOn.AlertEpisodeTitle]: undefined,
222
+ [NotificationRuleConditionCheckOn.AlertEpisodeDescription]: undefined,
223
+ [NotificationRuleConditionCheckOn.AlertEpisodeSeverity]: undefined,
224
+ [NotificationRuleConditionCheckOn.AlertEpisodeState]: undefined,
225
+ [NotificationRuleConditionCheckOn.AlertEpisodeLabels]: undefined,
226
+ [NotificationRuleConditionCheckOn.IncidentEpisodeTitle]: undefined,
227
+ [NotificationRuleConditionCheckOn.IncidentEpisodeDescription]: undefined,
228
+ [NotificationRuleConditionCheckOn.IncidentEpisodeSeverity]: undefined,
229
+ [NotificationRuleConditionCheckOn.IncidentEpisodeState]: undefined,
230
+ [NotificationRuleConditionCheckOn.IncidentEpisodeLabels]: undefined,
231
+ };
232
+ }
233
+ // Build values map for an alert
234
+ static buildAlertValues(alert) {
235
+ var _a, _b, _c, _d, _e, _f;
236
+ return {
237
+ [NotificationRuleConditionCheckOn.AlertTitle]: alert.title || "",
238
+ [NotificationRuleConditionCheckOn.AlertDescription]: alert.description || "",
239
+ [NotificationRuleConditionCheckOn.AlertSeverity]: ((_b = (_a = alert.alertSeverity) === null || _a === void 0 ? void 0 : _a._id) === null || _b === void 0 ? void 0 : _b.toString()) || "",
240
+ [NotificationRuleConditionCheckOn.AlertState]: ((_d = (_c = alert.currentAlertState) === null || _c === void 0 ? void 0 : _c._id) === null || _d === void 0 ? void 0 : _d.toString()) || "",
241
+ [NotificationRuleConditionCheckOn.AlertLabels]: ((_e = alert.labels) === null || _e === void 0 ? void 0 : _e.map((l) => {
242
+ var _a;
243
+ return ((_a = l._id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
244
+ })) || [],
245
+ [NotificationRuleConditionCheckOn.Monitors]: ((_f = alert.monitor) === null || _f === void 0 ? void 0 : _f._id)
246
+ ? [alert.monitor._id.toString()]
247
+ : [],
248
+ // unused for alerts
249
+ [NotificationRuleConditionCheckOn.MonitorName]: undefined,
250
+ [NotificationRuleConditionCheckOn.MonitorType]: undefined,
251
+ [NotificationRuleConditionCheckOn.MonitorStatus]: undefined,
252
+ [NotificationRuleConditionCheckOn.IncidentTitle]: undefined,
253
+ [NotificationRuleConditionCheckOn.IncidentDescription]: undefined,
254
+ [NotificationRuleConditionCheckOn.IncidentSeverity]: undefined,
255
+ [NotificationRuleConditionCheckOn.IncidentState]: undefined,
256
+ [NotificationRuleConditionCheckOn.IncidentLabels]: undefined,
257
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceTitle]: undefined,
258
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceDescription]: undefined,
259
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceState]: undefined,
260
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceLabels]: undefined,
261
+ [NotificationRuleConditionCheckOn.MonitorLabels]: undefined,
262
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyName]: undefined,
263
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyDescription]: undefined,
264
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyLabels]: undefined,
265
+ [NotificationRuleConditionCheckOn.AlertEpisodeTitle]: undefined,
266
+ [NotificationRuleConditionCheckOn.AlertEpisodeDescription]: undefined,
267
+ [NotificationRuleConditionCheckOn.AlertEpisodeSeverity]: undefined,
268
+ [NotificationRuleConditionCheckOn.AlertEpisodeState]: undefined,
269
+ [NotificationRuleConditionCheckOn.AlertEpisodeLabels]: undefined,
270
+ [NotificationRuleConditionCheckOn.IncidentEpisodeTitle]: undefined,
271
+ [NotificationRuleConditionCheckOn.IncidentEpisodeDescription]: undefined,
272
+ [NotificationRuleConditionCheckOn.IncidentEpisodeSeverity]: undefined,
273
+ [NotificationRuleConditionCheckOn.IncidentEpisodeState]: undefined,
274
+ [NotificationRuleConditionCheckOn.IncidentEpisodeLabels]: undefined,
275
+ };
276
+ }
277
+ // Build values map for an incident episode
278
+ static buildIncidentEpisodeValues(episode) {
279
+ var _a, _b, _c, _d, _e;
280
+ return {
281
+ [NotificationRuleConditionCheckOn.IncidentEpisodeTitle]: episode.title || "",
282
+ [NotificationRuleConditionCheckOn.IncidentEpisodeDescription]: episode.description || "",
283
+ [NotificationRuleConditionCheckOn.IncidentEpisodeSeverity]: ((_b = (_a = episode.incidentSeverity) === null || _a === void 0 ? void 0 : _a._id) === null || _b === void 0 ? void 0 : _b.toString()) || "",
284
+ [NotificationRuleConditionCheckOn.IncidentEpisodeState]: ((_d = (_c = episode.currentIncidentState) === null || _c === void 0 ? void 0 : _c._id) === null || _d === void 0 ? void 0 : _d.toString()) || "",
285
+ [NotificationRuleConditionCheckOn.IncidentEpisodeLabels]: ((_e = episode.labels) === null || _e === void 0 ? void 0 : _e.map((l) => {
286
+ var _a;
287
+ return ((_a = l._id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
288
+ })) || [],
289
+ // unused for incident episodes
290
+ [NotificationRuleConditionCheckOn.IncidentTitle]: undefined,
291
+ [NotificationRuleConditionCheckOn.IncidentDescription]: undefined,
292
+ [NotificationRuleConditionCheckOn.IncidentSeverity]: undefined,
293
+ [NotificationRuleConditionCheckOn.IncidentState]: undefined,
294
+ [NotificationRuleConditionCheckOn.IncidentLabels]: undefined,
295
+ [NotificationRuleConditionCheckOn.AlertTitle]: undefined,
296
+ [NotificationRuleConditionCheckOn.AlertDescription]: undefined,
297
+ [NotificationRuleConditionCheckOn.AlertSeverity]: undefined,
298
+ [NotificationRuleConditionCheckOn.AlertState]: undefined,
299
+ [NotificationRuleConditionCheckOn.AlertLabels]: undefined,
300
+ [NotificationRuleConditionCheckOn.AlertEpisodeTitle]: undefined,
301
+ [NotificationRuleConditionCheckOn.AlertEpisodeDescription]: undefined,
302
+ [NotificationRuleConditionCheckOn.AlertEpisodeSeverity]: undefined,
303
+ [NotificationRuleConditionCheckOn.AlertEpisodeState]: undefined,
304
+ [NotificationRuleConditionCheckOn.AlertEpisodeLabels]: undefined,
305
+ [NotificationRuleConditionCheckOn.Monitors]: undefined,
306
+ [NotificationRuleConditionCheckOn.MonitorName]: undefined,
307
+ [NotificationRuleConditionCheckOn.MonitorType]: undefined,
308
+ [NotificationRuleConditionCheckOn.MonitorStatus]: undefined,
309
+ [NotificationRuleConditionCheckOn.MonitorLabels]: undefined,
310
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceTitle]: undefined,
311
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceDescription]: undefined,
312
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceState]: undefined,
313
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceLabels]: undefined,
314
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyName]: undefined,
315
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyDescription]: undefined,
316
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyLabels]: undefined,
317
+ };
318
+ }
319
+ // Build values map for an alert episode
320
+ static buildAlertEpisodeValues(episode) {
321
+ var _a, _b, _c, _d, _e;
322
+ return {
323
+ [NotificationRuleConditionCheckOn.AlertEpisodeTitle]: episode.title || "",
324
+ [NotificationRuleConditionCheckOn.AlertEpisodeDescription]: episode.description || "",
325
+ [NotificationRuleConditionCheckOn.AlertEpisodeSeverity]: ((_b = (_a = episode.alertSeverity) === null || _a === void 0 ? void 0 : _a._id) === null || _b === void 0 ? void 0 : _b.toString()) || "",
326
+ [NotificationRuleConditionCheckOn.AlertEpisodeState]: ((_d = (_c = episode.currentAlertState) === null || _c === void 0 ? void 0 : _c._id) === null || _d === void 0 ? void 0 : _d.toString()) || "",
327
+ [NotificationRuleConditionCheckOn.AlertEpisodeLabels]: ((_e = episode.labels) === null || _e === void 0 ? void 0 : _e.map((l) => {
328
+ var _a;
329
+ return ((_a = l._id) === null || _a === void 0 ? void 0 : _a.toString()) || "";
330
+ })) || [],
331
+ // unused for alert episodes
332
+ [NotificationRuleConditionCheckOn.IncidentTitle]: undefined,
333
+ [NotificationRuleConditionCheckOn.IncidentDescription]: undefined,
334
+ [NotificationRuleConditionCheckOn.IncidentSeverity]: undefined,
335
+ [NotificationRuleConditionCheckOn.IncidentState]: undefined,
336
+ [NotificationRuleConditionCheckOn.IncidentLabels]: undefined,
337
+ [NotificationRuleConditionCheckOn.IncidentEpisodeTitle]: undefined,
338
+ [NotificationRuleConditionCheckOn.IncidentEpisodeDescription]: undefined,
339
+ [NotificationRuleConditionCheckOn.IncidentEpisodeSeverity]: undefined,
340
+ [NotificationRuleConditionCheckOn.IncidentEpisodeState]: undefined,
341
+ [NotificationRuleConditionCheckOn.IncidentEpisodeLabels]: undefined,
342
+ [NotificationRuleConditionCheckOn.AlertTitle]: undefined,
343
+ [NotificationRuleConditionCheckOn.AlertDescription]: undefined,
344
+ [NotificationRuleConditionCheckOn.AlertSeverity]: undefined,
345
+ [NotificationRuleConditionCheckOn.AlertState]: undefined,
346
+ [NotificationRuleConditionCheckOn.AlertLabels]: undefined,
347
+ [NotificationRuleConditionCheckOn.Monitors]: undefined,
348
+ [NotificationRuleConditionCheckOn.MonitorName]: undefined,
349
+ [NotificationRuleConditionCheckOn.MonitorType]: undefined,
350
+ [NotificationRuleConditionCheckOn.MonitorStatus]: undefined,
351
+ [NotificationRuleConditionCheckOn.MonitorLabels]: undefined,
352
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceTitle]: undefined,
353
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceDescription]: undefined,
354
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceState]: undefined,
355
+ [NotificationRuleConditionCheckOn.ScheduledMaintenanceLabels]: undefined,
356
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyName]: undefined,
357
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyDescription]: undefined,
358
+ [NotificationRuleConditionCheckOn.OnCallDutyPolicyLabels]: undefined,
359
+ };
360
+ }
361
+ // ───────────────────────── main builder ─────────────────────────
362
+ async buildSummaryMessageBlocks(data) {
363
+ const { summary } = data;
364
+ const blocks = [];
365
+ const items = summary.summaryItems;
366
+ const days = summary.numberOfDaysOfData || 7;
367
+ const type = summary.summaryType;
368
+ const fromDate = OneUptimeDate.addRemoveDays(OneUptimeDate.getCurrentDate(), -days);
369
+ const fromDateStr = Service.formatDate(fromDate);
370
+ const toDateStr = Service.formatDate(OneUptimeDate.getCurrentDate());
371
+ // Title
372
+ blocks.push(Service.header(`${type} Summary — ${fromDateStr} to ${toDateStr}`));
373
+ blocks.push(Service.md(`_Reporting period: ${Service.bold(String(days))} day${days !== 1 ? "s" : ""}_`));
374
+ blocks.push(Service.divider());
375
+ // Build type-specific content
376
+ if (type === WorkspaceNotificationSummaryType.Incident ||
377
+ type === WorkspaceNotificationSummaryType.IncidentEpisode) {
378
+ await this.buildIncidentBlocks({
379
+ blocks,
380
+ items,
381
+ type,
382
+ fromDate,
383
+ projectId: summary.projectId,
384
+ filters: summary.filters || undefined,
385
+ filterCondition: summary.filterCondition || undefined,
386
+ });
387
+ }
388
+ else {
389
+ await this.buildAlertBlocks({
390
+ blocks,
391
+ items,
392
+ type,
393
+ fromDate,
394
+ projectId: summary.projectId,
395
+ filters: summary.filters || undefined,
396
+ filterCondition: summary.filterCondition || undefined,
397
+ });
398
+ }
399
+ // Footer
400
+ blocks.push(Service.divider());
401
+ blocks.push(Service.md(`_Sent by OneUptime • ${summary.name || "Untitled"}_`));
402
+ return blocks;
403
+ }
404
+ // ───────────────────────── Incidents ─────────────────────────
405
+ async buildIncidentBlocks(data) {
406
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p;
407
+ if (data.type === WorkspaceNotificationSummaryType.IncidentEpisode) {
408
+ await this.buildIncidentEpisodeBlocks(data);
409
+ return;
410
+ }
411
+ const { blocks, items, fromDate, projectId } = data;
412
+ let incidents = await IncidentService.findAllBy({
413
+ query: {
414
+ projectId,
415
+ createdAt: QueryHelper.greaterThanEqualTo(fromDate),
416
+ },
417
+ select: {
418
+ _id: true,
419
+ title: true,
420
+ description: true,
421
+ incidentNumber: true,
422
+ incidentNumberWithPrefix: true,
423
+ incidentSeverity: { name: true, _id: true },
424
+ currentIncidentState: {
425
+ name: true,
426
+ _id: true,
427
+ isResolvedState: true,
428
+ isAcknowledgedState: true,
429
+ },
430
+ labels: { _id: true, name: true },
431
+ monitors: { name: true, _id: true },
432
+ createdAt: true,
433
+ declaredAt: true,
434
+ },
435
+ props: { isRoot: true },
436
+ });
437
+ // Apply filters
438
+ if (data.filters && data.filters.length > 0) {
439
+ incidents = incidents.filter((inc) => {
440
+ return Service.matchesFilters({
441
+ filters: data.filters,
442
+ filterCondition: data.filterCondition,
443
+ values: Service.buildIncidentValues(inc),
444
+ });
445
+ });
446
+ }
447
+ const dashboardUrl = await DatabaseConfig.getDashboardUrl();
448
+ // Overview stats
449
+ if (Service.has(items, WorkspaceNotificationSummaryItem.TotalCount)) {
450
+ const resolved = incidents.filter((i) => {
451
+ var _a;
452
+ return (_a = i.currentIncidentState) === null || _a === void 0 ? void 0 : _a.isResolvedState;
453
+ }).length;
454
+ const open = incidents.length - resolved;
455
+ blocks.push(Service.md(`${Service.bold("Total:")} ${incidents.length} incident${incidents.length !== 1 ? "s" : ""} · ` +
456
+ `${Service.bold("Open:")} ${open} · ${Service.bold("Resolved:")} ${resolved}`));
457
+ }
458
+ // Severity breakdown
459
+ if (Service.has(items, WorkspaceNotificationSummaryItem.SeverityBreakdown)) {
460
+ const map = new Map();
461
+ for (const i of incidents) {
462
+ const s = ((_a = i.incidentSeverity) === null || _a === void 0 ? void 0 : _a.name) || "Unknown";
463
+ map.set(s, (map.get(s) || 0) + 1);
464
+ }
465
+ if (map.size > 0) {
466
+ const parts = [];
467
+ for (const [sev, count] of map) {
468
+ parts.push(`${sev}: ${Service.bold(String(count))}`);
469
+ }
470
+ blocks.push(Service.md(`${Service.bold("By Severity:")} ${parts.join(" · ")}`));
471
+ }
472
+ }
473
+ // State breakdown
474
+ if (Service.has(items, WorkspaceNotificationSummaryItem.StateBreakdown)) {
475
+ const map = new Map();
476
+ for (const i of incidents) {
477
+ const s = ((_b = i.currentIncidentState) === null || _b === void 0 ? void 0 : _b.name) || "Unknown";
478
+ map.set(s, (map.get(s) || 0) + 1);
479
+ }
480
+ if (map.size > 0) {
481
+ const parts = [];
482
+ for (const [state, count] of map) {
483
+ parts.push(`${state}: ${Service.bold(String(count))}`);
484
+ }
485
+ blocks.push(Service.md(`${Service.bold("By State:")} ${parts.join(" · ")}`));
486
+ }
487
+ }
488
+ // Timeline data for MTTA/MTTR/who
489
+ const needTimeline = Service.has(items, WorkspaceNotificationSummaryItem.WhoAcknowledged) ||
490
+ Service.has(items, WorkspaceNotificationSummaryItem.WhoResolved) ||
491
+ Service.has(items, WorkspaceNotificationSummaryItem.TimeToAcknowledge) ||
492
+ Service.has(items, WorkspaceNotificationSummaryItem.TimeToResolve);
493
+ const tlMap = new Map();
494
+ if (needTimeline && incidents.length > 0) {
495
+ const ids = incidents
496
+ .filter((i) => {
497
+ return i._id;
498
+ })
499
+ .map((i) => {
500
+ return new ObjectID(i._id.toString());
501
+ });
502
+ const timelines = await IncidentStateTimelineService.findAllBy({
503
+ query: {
504
+ projectId,
505
+ incidentId: QueryHelper.any(ids),
506
+ },
507
+ select: {
508
+ incidentId: true,
509
+ incidentState: {
510
+ isAcknowledgedState: true,
511
+ isResolvedState: true,
512
+ },
513
+ createdByUser: { name: true, email: true },
514
+ createdAt: true,
515
+ },
516
+ props: { isRoot: true },
517
+ });
518
+ for (const tl of timelines) {
519
+ const id = ((_c = tl.incidentId) === null || _c === void 0 ? void 0 : _c.toString()) || "";
520
+ if (!tlMap.has(id)) {
521
+ tlMap.set(id, {});
522
+ }
523
+ const td = tlMap.get(id);
524
+ const userName = ((_e = (_d = tl.createdByUser) === null || _d === void 0 ? void 0 : _d.name) === null || _e === void 0 ? void 0 : _e.toString()) ||
525
+ ((_g = (_f = tl.createdByUser) === null || _f === void 0 ? void 0 : _f.email) === null || _g === void 0 ? void 0 : _g.toString()) ||
526
+ "System";
527
+ if (((_h = tl.incidentState) === null || _h === void 0 ? void 0 : _h.isAcknowledgedState) && !td.ackAt) {
528
+ td.ackBy = userName;
529
+ td.ackAt = tl.createdAt;
530
+ }
531
+ if (((_j = tl.incidentState) === null || _j === void 0 ? void 0 : _j.isResolvedState) && !td.resolvedAt) {
532
+ td.resolvedBy = userName;
533
+ td.resolvedAt = tl.createdAt;
534
+ }
535
+ }
536
+ for (const inc of incidents) {
537
+ const id = ((_k = inc._id) === null || _k === void 0 ? void 0 : _k.toString()) || "";
538
+ if (!tlMap.has(id)) {
539
+ tlMap.set(id, {});
540
+ }
541
+ tlMap.get(id).declaredAt = inc.declaredAt || inc.createdAt;
542
+ }
543
+ }
544
+ // MTTA
545
+ if (Service.has(items, WorkspaceNotificationSummaryItem.TimeToAcknowledge)) {
546
+ const { avg, count } = this.computeAvg(tlMap, "ack");
547
+ blocks.push(Service.md(count > 0
548
+ ? `${Service.bold("MTTA (Mean Time to Acknowledge):")} ${Service.bold(Service.formatDuration(avg))} _(${count} acknowledged)_`
549
+ : `${Service.bold("MTTA (Mean Time to Acknowledge):")} _No incidents acknowledged_`));
550
+ }
551
+ // MTTR
552
+ if (Service.has(items, WorkspaceNotificationSummaryItem.TimeToResolve)) {
553
+ const { avg, count } = this.computeAvg(tlMap, "resolve");
554
+ blocks.push(Service.md(count > 0
555
+ ? `${Service.bold("MTTR (Mean Time to Resolve):")} ${Service.bold(Service.formatDuration(avg))} _(${count} resolved)_`
556
+ : `${Service.bold("MTTR (Mean Time to Resolve):")} _No incidents resolved_`));
557
+ }
558
+ // Resources affected
559
+ if (Service.has(items, WorkspaceNotificationSummaryItem.ResourcesAffected)) {
560
+ const names = new Set();
561
+ for (const inc of incidents) {
562
+ if (inc.monitors) {
563
+ for (const m of inc.monitors) {
564
+ if (m.name) {
565
+ names.add(m.name);
566
+ }
567
+ }
568
+ }
569
+ }
570
+ if (names.size > 0) {
571
+ blocks.push(Service.md(`${Service.bold(`Resources Affected (${names.size}):`)} ${Array.from(names).join(", ")}`));
572
+ }
573
+ }
574
+ // Detailed list
575
+ if (Service.has(items, WorkspaceNotificationSummaryItem.ListWithLinks)) {
576
+ blocks.push(Service.divider());
577
+ if (incidents.length === 0) {
578
+ blocks.push(Service.md(`_No incidents reported in this period._`));
579
+ return;
580
+ }
581
+ for (const inc of incidents) {
582
+ const id = ((_l = inc._id) === null || _l === void 0 ? void 0 : _l.toString()) || "";
583
+ const display = inc.incidentNumberWithPrefix || `#${inc.incidentNumber || ""}`;
584
+ const linkUrl = URL.fromString(dashboardUrl.toString())
585
+ .addRoute(`/${projectId.toString()}/incidents/${id}`)
586
+ .toString();
587
+ const td = tlMap.get(id);
588
+ // Title line with link
589
+ let text = `${Service.bold(Service.link(linkUrl, `${display} — ${inc.title || "Untitled"}`))}`;
590
+ // Meta line
591
+ const meta = [];
592
+ if ((_m = inc.incidentSeverity) === null || _m === void 0 ? void 0 : _m.name) {
593
+ meta.push(`Severity: ${Service.bold(inc.incidentSeverity.name)}`);
594
+ }
595
+ if ((_o = inc.currentIncidentState) === null || _o === void 0 ? void 0 : _o.name) {
596
+ meta.push(`State: ${Service.bold(inc.currentIncidentState.name)}`);
597
+ }
598
+ if (inc.declaredAt) {
599
+ meta.push(`Declared: ${Service.formatDate(inc.declaredAt)}`);
600
+ }
601
+ if (meta.length > 0) {
602
+ text += `\n${meta.join(" · ")}`;
603
+ }
604
+ // Ack & resolve line
605
+ const ackResolve = [];
606
+ if (Service.has(items, WorkspaceNotificationSummaryItem.WhoAcknowledged)) {
607
+ if ((td === null || td === void 0 ? void 0 : td.ackBy) && (td === null || td === void 0 ? void 0 : td.ackAt)) {
608
+ ackResolve.push(`Ack: ${Service.bold(td.ackBy)} in ${Service.formatDuration(OneUptimeDate.getMinutesBetweenTwoDates(td.declaredAt || inc.createdAt, td.ackAt))}`);
609
+ }
610
+ else {
611
+ ackResolve.push(`_Not yet acknowledged_`);
612
+ }
613
+ }
614
+ if (Service.has(items, WorkspaceNotificationSummaryItem.WhoResolved)) {
615
+ if ((td === null || td === void 0 ? void 0 : td.resolvedBy) && (td === null || td === void 0 ? void 0 : td.resolvedAt)) {
616
+ ackResolve.push(`Resolved: ${Service.bold(td.resolvedBy)} in ${Service.formatDuration(OneUptimeDate.getMinutesBetweenTwoDates(td.declaredAt || inc.createdAt, td.resolvedAt))}`);
617
+ }
618
+ else if (!((_p = inc.currentIncidentState) === null || _p === void 0 ? void 0 : _p.isResolvedState)) {
619
+ ackResolve.push(`_Not yet resolved_`);
620
+ }
621
+ }
622
+ if (ackResolve.length > 0) {
623
+ text += `\n${ackResolve.join(" · ")}`;
624
+ }
625
+ blocks.push(Service.md(text));
626
+ }
627
+ }
628
+ }
629
+ // ───────────────────────── Incident Episodes ─────────────────────────
630
+ async buildIncidentEpisodeBlocks(data) {
631
+ var _a, _b, _c, _d, _e;
632
+ const { blocks, items, fromDate, projectId } = data;
633
+ let episodes = await IncidentEpisodeService.findAllBy({
634
+ query: {
635
+ projectId,
636
+ createdAt: QueryHelper.greaterThanEqualTo(fromDate),
637
+ },
638
+ select: {
639
+ _id: true,
640
+ title: true,
641
+ description: true,
642
+ incidentSeverity: { name: true, _id: true },
643
+ currentIncidentState: {
644
+ name: true,
645
+ _id: true,
646
+ isResolvedState: true,
647
+ },
648
+ labels: { _id: true, name: true },
649
+ createdAt: true,
650
+ resolvedAt: true,
651
+ },
652
+ props: { isRoot: true },
653
+ });
654
+ // Apply filters
655
+ if (data.filters && data.filters.length > 0) {
656
+ episodes = episodes.filter((ep) => {
657
+ return Service.matchesFilters({
658
+ filters: data.filters,
659
+ filterCondition: data.filterCondition,
660
+ values: Service.buildIncidentEpisodeValues(ep),
661
+ });
662
+ });
663
+ }
664
+ const dashboardUrl = await DatabaseConfig.getDashboardUrl();
665
+ if (Service.has(items, WorkspaceNotificationSummaryItem.TotalCount)) {
666
+ const resolved = episodes.filter((e) => {
667
+ var _a;
668
+ return (_a = e.currentIncidentState) === null || _a === void 0 ? void 0 : _a.isResolvedState;
669
+ }).length;
670
+ blocks.push(Service.md(`${Service.bold("Total:")} ${episodes.length} episode${episodes.length !== 1 ? "s" : ""} · ` +
671
+ `${Service.bold("Open:")} ${episodes.length - resolved} · ${Service.bold("Resolved:")} ${resolved}`));
672
+ }
673
+ if (Service.has(items, WorkspaceNotificationSummaryItem.SeverityBreakdown)) {
674
+ const map = new Map();
675
+ for (const e of episodes) {
676
+ const s = ((_a = e.incidentSeverity) === null || _a === void 0 ? void 0 : _a.name) || "Unknown";
677
+ map.set(s, (map.get(s) || 0) + 1);
678
+ }
679
+ if (map.size > 0) {
680
+ const parts = [];
681
+ for (const [sev, c] of map) {
682
+ parts.push(`${sev}: ${Service.bold(String(c))}`);
683
+ }
684
+ blocks.push(Service.md(`${Service.bold("By Severity:")} ${parts.join(" · ")}`));
685
+ }
686
+ }
687
+ if (Service.has(items, WorkspaceNotificationSummaryItem.StateBreakdown)) {
688
+ const map = new Map();
689
+ for (const e of episodes) {
690
+ const s = ((_b = e.currentIncidentState) === null || _b === void 0 ? void 0 : _b.name) || "Unknown";
691
+ map.set(s, (map.get(s) || 0) + 1);
692
+ }
693
+ if (map.size > 0) {
694
+ const parts = [];
695
+ for (const [state, c] of map) {
696
+ parts.push(`${state}: ${Service.bold(String(c))}`);
697
+ }
698
+ blocks.push(Service.md(`${Service.bold("By State:")} ${parts.join(" · ")}`));
699
+ }
700
+ }
701
+ if (Service.has(items, WorkspaceNotificationSummaryItem.TimeToResolve)) {
702
+ let total = 0;
703
+ let count = 0;
704
+ for (const e of episodes) {
705
+ if (e.resolvedAt && e.createdAt) {
706
+ total += OneUptimeDate.getMinutesBetweenTwoDates(e.createdAt, e.resolvedAt);
707
+ count++;
708
+ }
709
+ }
710
+ blocks.push(Service.md(count > 0
711
+ ? `${Service.bold("MTTR (Mean Time to Resolve):")} ${Service.bold(Service.formatDuration(Math.round(total / count)))} _(${count} resolved)_`
712
+ : `${Service.bold("MTTR (Mean Time to Resolve):")} _No episodes resolved_`));
713
+ }
714
+ if (Service.has(items, WorkspaceNotificationSummaryItem.ListWithLinks)) {
715
+ blocks.push(Service.divider());
716
+ if (episodes.length === 0) {
717
+ blocks.push(Service.md(`_No incident episodes in this period._`));
718
+ return;
719
+ }
720
+ for (const ep of episodes) {
721
+ const id = ((_c = ep._id) === null || _c === void 0 ? void 0 : _c.toString()) || "";
722
+ const linkUrl = URL.fromString(dashboardUrl.toString())
723
+ .addRoute(`/${projectId.toString()}/incidents/episodes/${id}`)
724
+ .toString();
725
+ let text = `${Service.bold(Service.link(linkUrl, ep.title || "Untitled Episode"))}`;
726
+ const meta = [];
727
+ if ((_d = ep.incidentSeverity) === null || _d === void 0 ? void 0 : _d.name) {
728
+ meta.push(`Severity: ${Service.bold(ep.incidentSeverity.name)}`);
729
+ }
730
+ if ((_e = ep.currentIncidentState) === null || _e === void 0 ? void 0 : _e.name) {
731
+ meta.push(`State: ${Service.bold(ep.currentIncidentState.name)}`);
732
+ }
733
+ if (ep.createdAt) {
734
+ meta.push(`Created: ${Service.formatDate(ep.createdAt)}`);
735
+ }
736
+ if (ep.resolvedAt && ep.createdAt) {
737
+ meta.push(`Resolved in ${Service.bold(Service.formatDuration(OneUptimeDate.getMinutesBetweenTwoDates(ep.createdAt, ep.resolvedAt)))}`);
738
+ }
739
+ if (meta.length > 0) {
740
+ text += `\n${meta.join(" · ")}`;
741
+ }
742
+ blocks.push(Service.md(text));
743
+ }
744
+ }
745
+ }
746
+ // ───────────────────────── Alerts ─────────────────────────
747
+ async buildAlertBlocks(data) {
748
+ var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q;
749
+ if (data.type === WorkspaceNotificationSummaryType.AlertEpisode) {
750
+ await this.buildAlertEpisodeBlocks(data);
751
+ return;
752
+ }
753
+ const { blocks, items, fromDate, projectId } = data;
754
+ let alerts = await AlertService.findAllBy({
755
+ query: {
756
+ projectId,
757
+ createdAt: QueryHelper.greaterThanEqualTo(fromDate),
758
+ },
759
+ select: {
760
+ _id: true,
761
+ title: true,
762
+ description: true,
763
+ alertNumber: true,
764
+ alertNumberWithPrefix: true,
765
+ alertSeverity: { name: true, _id: true },
766
+ currentAlertState: {
767
+ name: true,
768
+ _id: true,
769
+ isResolvedState: true,
770
+ isAcknowledgedState: true,
771
+ },
772
+ labels: { _id: true, name: true },
773
+ monitor: { name: true, _id: true },
774
+ createdAt: true,
775
+ },
776
+ props: { isRoot: true },
777
+ });
778
+ // Apply filters
779
+ if (data.filters && data.filters.length > 0) {
780
+ alerts = alerts.filter((alert) => {
781
+ return Service.matchesFilters({
782
+ filters: data.filters,
783
+ filterCondition: data.filterCondition,
784
+ values: Service.buildAlertValues(alert),
785
+ });
786
+ });
787
+ }
788
+ const dashboardUrl = await DatabaseConfig.getDashboardUrl();
789
+ if (Service.has(items, WorkspaceNotificationSummaryItem.TotalCount)) {
790
+ const resolved = alerts.filter((a) => {
791
+ var _a;
792
+ return (_a = a.currentAlertState) === null || _a === void 0 ? void 0 : _a.isResolvedState;
793
+ }).length;
794
+ blocks.push(Service.md(`${Service.bold("Total:")} ${alerts.length} alert${alerts.length !== 1 ? "s" : ""} · ` +
795
+ `${Service.bold("Open:")} ${alerts.length - resolved} · ${Service.bold("Resolved:")} ${resolved}`));
796
+ }
797
+ if (Service.has(items, WorkspaceNotificationSummaryItem.SeverityBreakdown)) {
798
+ const map = new Map();
799
+ for (const a of alerts) {
800
+ const s = ((_a = a.alertSeverity) === null || _a === void 0 ? void 0 : _a.name) || "Unknown";
801
+ map.set(s, (map.get(s) || 0) + 1);
802
+ }
803
+ if (map.size > 0) {
804
+ const parts = [];
805
+ for (const [sev, c] of map) {
806
+ parts.push(`${sev}: ${Service.bold(String(c))}`);
807
+ }
808
+ blocks.push(Service.md(`${Service.bold("By Severity:")} ${parts.join(" · ")}`));
809
+ }
810
+ }
811
+ if (Service.has(items, WorkspaceNotificationSummaryItem.StateBreakdown)) {
812
+ const map = new Map();
813
+ for (const a of alerts) {
814
+ const s = ((_b = a.currentAlertState) === null || _b === void 0 ? void 0 : _b.name) || "Unknown";
815
+ map.set(s, (map.get(s) || 0) + 1);
816
+ }
817
+ if (map.size > 0) {
818
+ const parts = [];
819
+ for (const [state, c] of map) {
820
+ parts.push(`${state}: ${Service.bold(String(c))}`);
821
+ }
822
+ blocks.push(Service.md(`${Service.bold("By State:")} ${parts.join(" · ")}`));
823
+ }
824
+ }
825
+ // Timeline data
826
+ const needTimeline = Service.has(items, WorkspaceNotificationSummaryItem.WhoAcknowledged) ||
827
+ Service.has(items, WorkspaceNotificationSummaryItem.WhoResolved) ||
828
+ Service.has(items, WorkspaceNotificationSummaryItem.TimeToAcknowledge) ||
829
+ Service.has(items, WorkspaceNotificationSummaryItem.TimeToResolve);
830
+ const tlMap = new Map();
831
+ if (needTimeline && alerts.length > 0) {
832
+ const ids = alerts
833
+ .filter((a) => {
834
+ return a._id;
835
+ })
836
+ .map((a) => {
837
+ return new ObjectID(a._id.toString());
838
+ });
839
+ const timelines = await AlertStateTimelineService.findAllBy({
840
+ query: { projectId, alertId: QueryHelper.any(ids) },
841
+ select: {
842
+ alertId: true,
843
+ alertState: {
844
+ isAcknowledgedState: true,
845
+ isResolvedState: true,
846
+ },
847
+ createdByUser: { name: true, email: true },
848
+ createdAt: true,
849
+ },
850
+ props: { isRoot: true },
851
+ });
852
+ for (const tl of timelines) {
853
+ const id = ((_c = tl.alertId) === null || _c === void 0 ? void 0 : _c.toString()) || "";
854
+ if (!tlMap.has(id)) {
855
+ tlMap.set(id, {});
856
+ }
857
+ const td = tlMap.get(id);
858
+ const userName = ((_e = (_d = tl.createdByUser) === null || _d === void 0 ? void 0 : _d.name) === null || _e === void 0 ? void 0 : _e.toString()) ||
859
+ ((_g = (_f = tl.createdByUser) === null || _f === void 0 ? void 0 : _f.email) === null || _g === void 0 ? void 0 : _g.toString()) ||
860
+ "System";
861
+ if (((_h = tl.alertState) === null || _h === void 0 ? void 0 : _h.isAcknowledgedState) && !td.ackAt) {
862
+ td.ackBy = userName;
863
+ td.ackAt = tl.createdAt;
864
+ }
865
+ if (((_j = tl.alertState) === null || _j === void 0 ? void 0 : _j.isResolvedState) && !td.resolvedAt) {
866
+ td.resolvedBy = userName;
867
+ td.resolvedAt = tl.createdAt;
868
+ }
869
+ }
870
+ for (const a of alerts) {
871
+ const id = ((_k = a._id) === null || _k === void 0 ? void 0 : _k.toString()) || "";
872
+ if (!tlMap.has(id)) {
873
+ tlMap.set(id, {});
874
+ }
875
+ tlMap.get(id).declaredAt = a.createdAt;
876
+ }
877
+ }
878
+ if (Service.has(items, WorkspaceNotificationSummaryItem.TimeToAcknowledge)) {
879
+ const { avg, count } = this.computeAvg(tlMap, "ack");
880
+ blocks.push(Service.md(count > 0
881
+ ? `${Service.bold("MTTA (Mean Time to Acknowledge):")} ${Service.bold(Service.formatDuration(avg))} _(${count} acknowledged)_`
882
+ : `${Service.bold("MTTA (Mean Time to Acknowledge):")} _No alerts acknowledged_`));
883
+ }
884
+ if (Service.has(items, WorkspaceNotificationSummaryItem.TimeToResolve)) {
885
+ const { avg, count } = this.computeAvg(tlMap, "resolve");
886
+ blocks.push(Service.md(count > 0
887
+ ? `${Service.bold("MTTR (Mean Time to Resolve):")} ${Service.bold(Service.formatDuration(avg))} _(${count} resolved)_`
888
+ : `${Service.bold("MTTR (Mean Time to Resolve):")} _No alerts resolved_`));
889
+ }
890
+ if (Service.has(items, WorkspaceNotificationSummaryItem.ResourcesAffected)) {
891
+ const names = new Set();
892
+ for (const a of alerts) {
893
+ if ((_l = a.monitor) === null || _l === void 0 ? void 0 : _l.name) {
894
+ names.add(a.monitor.name);
895
+ }
896
+ }
897
+ if (names.size > 0) {
898
+ blocks.push(Service.md(`${Service.bold(`Resources Affected (${names.size}):`)} ${Array.from(names).join(", ")}`));
899
+ }
900
+ }
901
+ if (Service.has(items, WorkspaceNotificationSummaryItem.ListWithLinks)) {
902
+ blocks.push(Service.divider());
903
+ if (alerts.length === 0) {
904
+ blocks.push(Service.md(`_No alerts reported in this period._`));
905
+ return;
906
+ }
907
+ for (const a of alerts) {
908
+ const id = ((_m = a._id) === null || _m === void 0 ? void 0 : _m.toString()) || "";
909
+ const display = a.alertNumberWithPrefix || `#${a.alertNumber || ""}`;
910
+ const linkUrl = URL.fromString(dashboardUrl.toString())
911
+ .addRoute(`/${projectId.toString()}/alerts/${id}`)
912
+ .toString();
913
+ const td = tlMap.get(id);
914
+ let text = `${Service.bold(Service.link(linkUrl, `${display} — ${a.title || "Untitled"}`))}`;
915
+ const meta = [];
916
+ if ((_o = a.alertSeverity) === null || _o === void 0 ? void 0 : _o.name) {
917
+ meta.push(`Severity: ${Service.bold(a.alertSeverity.name)}`);
918
+ }
919
+ if ((_p = a.currentAlertState) === null || _p === void 0 ? void 0 : _p.name) {
920
+ meta.push(`State: ${Service.bold(a.currentAlertState.name)}`);
921
+ }
922
+ if (a.createdAt) {
923
+ meta.push(`Created: ${Service.formatDate(a.createdAt)}`);
924
+ }
925
+ if (meta.length > 0) {
926
+ text += `\n${meta.join(" · ")}`;
927
+ }
928
+ const ackResolve = [];
929
+ if (Service.has(items, WorkspaceNotificationSummaryItem.WhoAcknowledged)) {
930
+ if ((td === null || td === void 0 ? void 0 : td.ackBy) && (td === null || td === void 0 ? void 0 : td.ackAt)) {
931
+ ackResolve.push(`Ack: ${Service.bold(td.ackBy)} in ${Service.formatDuration(OneUptimeDate.getMinutesBetweenTwoDates(td.declaredAt || a.createdAt, td.ackAt))}`);
932
+ }
933
+ else {
934
+ ackResolve.push(`_Not yet acknowledged_`);
935
+ }
936
+ }
937
+ if (Service.has(items, WorkspaceNotificationSummaryItem.WhoResolved)) {
938
+ if ((td === null || td === void 0 ? void 0 : td.resolvedBy) && (td === null || td === void 0 ? void 0 : td.resolvedAt)) {
939
+ ackResolve.push(`Resolved: ${Service.bold(td.resolvedBy)} in ${Service.formatDuration(OneUptimeDate.getMinutesBetweenTwoDates(td.declaredAt || a.createdAt, td.resolvedAt))}`);
940
+ }
941
+ else if (!((_q = a.currentAlertState) === null || _q === void 0 ? void 0 : _q.isResolvedState)) {
942
+ ackResolve.push(`_Not yet resolved_`);
943
+ }
944
+ }
945
+ if (ackResolve.length > 0) {
946
+ text += `\n${ackResolve.join(" · ")}`;
947
+ }
948
+ blocks.push(Service.md(text));
949
+ }
950
+ }
951
+ }
952
+ // ───────────────────────── Alert Episodes ─────────────────────────
953
+ async buildAlertEpisodeBlocks(data) {
954
+ var _a, _b, _c, _d, _e;
955
+ const { blocks, items, fromDate, projectId } = data;
956
+ let episodes = await AlertEpisodeService.findAllBy({
957
+ query: {
958
+ projectId,
959
+ createdAt: QueryHelper.greaterThanEqualTo(fromDate),
960
+ },
961
+ select: {
962
+ _id: true,
963
+ title: true,
964
+ description: true,
965
+ alertSeverity: { name: true, _id: true },
966
+ currentAlertState: { name: true, _id: true, isResolvedState: true },
967
+ labels: { _id: true, name: true },
968
+ createdAt: true,
969
+ resolvedAt: true,
970
+ },
971
+ props: { isRoot: true },
972
+ });
973
+ // Apply filters
974
+ if (data.filters && data.filters.length > 0) {
975
+ episodes = episodes.filter((ep) => {
976
+ return Service.matchesFilters({
977
+ filters: data.filters,
978
+ filterCondition: data.filterCondition,
979
+ values: Service.buildAlertEpisodeValues(ep),
980
+ });
981
+ });
982
+ }
983
+ const dashboardUrl = await DatabaseConfig.getDashboardUrl();
984
+ if (Service.has(items, WorkspaceNotificationSummaryItem.TotalCount)) {
985
+ const resolved = episodes.filter((e) => {
986
+ var _a;
987
+ return (_a = e.currentAlertState) === null || _a === void 0 ? void 0 : _a.isResolvedState;
988
+ }).length;
989
+ blocks.push(Service.md(`${Service.bold("Total:")} ${episodes.length} episode${episodes.length !== 1 ? "s" : ""} · ` +
990
+ `${Service.bold("Open:")} ${episodes.length - resolved} · ${Service.bold("Resolved:")} ${resolved}`));
991
+ }
992
+ if (Service.has(items, WorkspaceNotificationSummaryItem.SeverityBreakdown)) {
993
+ const map = new Map();
994
+ for (const e of episodes) {
995
+ const s = ((_a = e.alertSeverity) === null || _a === void 0 ? void 0 : _a.name) || "Unknown";
996
+ map.set(s, (map.get(s) || 0) + 1);
997
+ }
998
+ if (map.size > 0) {
999
+ const parts = [];
1000
+ for (const [sev, c] of map) {
1001
+ parts.push(`${sev}: ${Service.bold(String(c))}`);
1002
+ }
1003
+ blocks.push(Service.md(`${Service.bold("By Severity:")} ${parts.join(" · ")}`));
1004
+ }
1005
+ }
1006
+ if (Service.has(items, WorkspaceNotificationSummaryItem.StateBreakdown)) {
1007
+ const map = new Map();
1008
+ for (const e of episodes) {
1009
+ const s = ((_b = e.currentAlertState) === null || _b === void 0 ? void 0 : _b.name) || "Unknown";
1010
+ map.set(s, (map.get(s) || 0) + 1);
1011
+ }
1012
+ if (map.size > 0) {
1013
+ const parts = [];
1014
+ for (const [state, c] of map) {
1015
+ parts.push(`${state}: ${Service.bold(String(c))}`);
1016
+ }
1017
+ blocks.push(Service.md(`${Service.bold("By State:")} ${parts.join(" · ")}`));
1018
+ }
1019
+ }
1020
+ if (Service.has(items, WorkspaceNotificationSummaryItem.TimeToResolve)) {
1021
+ let total = 0;
1022
+ let count = 0;
1023
+ for (const e of episodes) {
1024
+ if (e.resolvedAt && e.createdAt) {
1025
+ total += OneUptimeDate.getMinutesBetweenTwoDates(e.createdAt, e.resolvedAt);
1026
+ count++;
1027
+ }
1028
+ }
1029
+ blocks.push(Service.md(count > 0
1030
+ ? `${Service.bold("MTTR (Mean Time to Resolve):")} ${Service.bold(Service.formatDuration(Math.round(total / count)))} _(${count} resolved)_`
1031
+ : `${Service.bold("MTTR (Mean Time to Resolve):")} _No episodes resolved_`));
1032
+ }
1033
+ if (Service.has(items, WorkspaceNotificationSummaryItem.ListWithLinks)) {
1034
+ blocks.push(Service.divider());
1035
+ if (episodes.length === 0) {
1036
+ blocks.push(Service.md(`_No alert episodes in this period._`));
1037
+ return;
1038
+ }
1039
+ for (const ep of episodes) {
1040
+ const id = ((_c = ep._id) === null || _c === void 0 ? void 0 : _c.toString()) || "";
1041
+ const linkUrl = URL.fromString(dashboardUrl.toString())
1042
+ .addRoute(`/${projectId.toString()}/alerts/episodes/${id}`)
1043
+ .toString();
1044
+ let text = `${Service.bold(Service.link(linkUrl, ep.title || "Untitled Episode"))}`;
1045
+ const meta = [];
1046
+ if ((_d = ep.alertSeverity) === null || _d === void 0 ? void 0 : _d.name) {
1047
+ meta.push(`Severity: ${Service.bold(ep.alertSeverity.name)}`);
1048
+ }
1049
+ if ((_e = ep.currentAlertState) === null || _e === void 0 ? void 0 : _e.name) {
1050
+ meta.push(`State: ${Service.bold(ep.currentAlertState.name)}`);
1051
+ }
1052
+ if (ep.createdAt) {
1053
+ meta.push(`Created: ${Service.formatDate(ep.createdAt)}`);
1054
+ }
1055
+ if (ep.resolvedAt && ep.createdAt) {
1056
+ meta.push(`Resolved in ${Service.bold(Service.formatDuration(OneUptimeDate.getMinutesBetweenTwoDates(ep.createdAt, ep.resolvedAt)))}`);
1057
+ }
1058
+ if (meta.length > 0) {
1059
+ text += `\n${meta.join(" · ")}`;
1060
+ }
1061
+ blocks.push(Service.md(text));
1062
+ }
1063
+ }
1064
+ }
1065
+ // ───────────────────────── Utilities ─────────────────────────
1066
+ computeAvg(tlMap, kind) {
1067
+ let total = 0;
1068
+ let count = 0;
1069
+ for (const [, td] of tlMap) {
1070
+ const eventTime = kind === "ack" ? td.ackAt : td.resolvedAt;
1071
+ if (eventTime && td.declaredAt) {
1072
+ total += OneUptimeDate.getMinutesBetweenTwoDates(td.declaredAt, eventTime);
1073
+ count++;
1074
+ }
1075
+ }
1076
+ return { avg: count > 0 ? Math.round(total / count) : 0, count };
1077
+ }
1078
+ }
1079
+ __decorate([
1080
+ CaptureSpan(),
1081
+ __metadata("design:type", Function),
1082
+ __metadata("design:paramtypes", [Object]),
1083
+ __metadata("design:returntype", Promise)
1084
+ ], Service.prototype, "testSummary", null);
1085
+ __decorate([
1086
+ CaptureSpan(),
1087
+ __metadata("design:type", Function),
1088
+ __metadata("design:paramtypes", [Object]),
1089
+ __metadata("design:returntype", Promise)
1090
+ ], Service.prototype, "sendSummary", null);
1091
+ __decorate([
1092
+ CaptureSpan(),
1093
+ __metadata("design:type", Function),
1094
+ __metadata("design:paramtypes", [Object]),
1095
+ __metadata("design:returntype", Promise)
1096
+ ], Service.prototype, "buildSummaryMessageBlocks", null);
1097
+ __decorate([
1098
+ CaptureSpan(),
1099
+ __metadata("design:type", Function),
1100
+ __metadata("design:paramtypes", [Object]),
1101
+ __metadata("design:returntype", Promise)
1102
+ ], Service.prototype, "buildIncidentBlocks", null);
1103
+ __decorate([
1104
+ CaptureSpan(),
1105
+ __metadata("design:type", Function),
1106
+ __metadata("design:paramtypes", [Object]),
1107
+ __metadata("design:returntype", Promise)
1108
+ ], Service.prototype, "buildIncidentEpisodeBlocks", null);
1109
+ __decorate([
1110
+ CaptureSpan(),
1111
+ __metadata("design:type", Function),
1112
+ __metadata("design:paramtypes", [Object]),
1113
+ __metadata("design:returntype", Promise)
1114
+ ], Service.prototype, "buildAlertBlocks", null);
1115
+ __decorate([
1116
+ CaptureSpan(),
1117
+ __metadata("design:type", Function),
1118
+ __metadata("design:paramtypes", [Object]),
1119
+ __metadata("design:returntype", Promise)
1120
+ ], Service.prototype, "buildAlertEpisodeBlocks", null);
1121
+ export default new Service();
1122
+ //# sourceMappingURL=WorkspaceNotificationSummaryService.js.map