@oneuptime/common 11.3.20 → 11.3.22
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/AnalyticsModels/AnalyticsBaseModel/CommonModel.ts +2 -0
- package/Models/AnalyticsModels/ExceptionInstance.ts +1 -0
- package/Models/AnalyticsModels/Log.ts +1 -0
- package/Models/AnalyticsModels/Metric.ts +1 -0
- package/Models/AnalyticsModels/MonitorLog.ts +1 -0
- package/Models/AnalyticsModels/Span.ts +1 -0
- package/Models/DatabaseModels/Alert.ts +100 -0
- package/Models/DatabaseModels/AlertInternalNote.ts +2 -0
- package/Models/DatabaseModels/AlertReminderRule.ts +597 -0
- package/Models/DatabaseModels/AlertSeverity.ts +2 -0
- package/Models/DatabaseModels/AlertState.ts +2 -0
- package/Models/DatabaseModels/AlertStateTimeline.ts +2 -0
- package/Models/DatabaseModels/EnterpriseLicenseInstance.ts +173 -0
- package/Models/DatabaseModels/GlobalConfig.ts +19 -0
- package/Models/DatabaseModels/Incident.ts +100 -0
- package/Models/DatabaseModels/IncidentInternalNote.ts +2 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +2 -0
- package/Models/DatabaseModels/IncidentReminderRule.ts +597 -0
- package/Models/DatabaseModels/IncidentSeverity.ts +2 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +2 -0
- package/Models/DatabaseModels/Index.ts +8 -0
- package/Models/DatabaseModels/Label.ts +2 -0
- package/Models/DatabaseModels/MonitorStatusTimeline.ts +2 -0
- package/Models/DatabaseModels/ScheduledMaintenance.ts +100 -0
- package/Models/DatabaseModels/ScheduledMaintenanceReminderRule.ts +585 -0
- package/Models/DatabaseModels/ScheduledMaintenanceState.ts +2 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +2 -0
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +2 -0
- package/Server/API/DashboardAPI.ts +221 -0
- package/Server/API/EnterpriseLicenseAPI.ts +279 -12
- package/Server/API/GlobalConfigAPI.ts +81 -16
- package/Server/API/StatusPageAPI.ts +894 -791
- package/Server/EnvironmentConfig.ts +9 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783226868418-AddEnterpriseLicenseInstance.ts +47 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783234451106-AddIncidentAlertReminderRules.ts +211 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783320000000-AddScheduledMaintenanceReminderRules.ts +113 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783330000000-AddRemindWhileScheduledToScheduledMaintenanceReminderRule.ts +20 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1783340000000-AddLabelsToIncidentAndAlertReminderRules.ts +69 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Services/AlertReminderRuleService.ts +253 -0
- package/Server/Services/AlertService.ts +106 -0
- package/Server/Services/EnterpriseLicenseInstanceService.ts +10 -0
- package/Server/Services/IncidentReminderRuleService.ts +256 -0
- package/Server/Services/IncidentService.ts +143 -0
- package/Server/Services/Index.ts +8 -0
- package/Server/Services/IoTDeviceService.ts +99 -4
- package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +236 -0
- package/Server/Services/ScheduledMaintenanceService.ts +127 -0
- package/Server/Services/StatusPageService.ts +104 -45
- package/Server/Services/TelemetryExceptionService.ts +123 -0
- package/Server/Services/UserNotificationSettingService.ts +24 -0
- package/Server/Utils/AnalyticsDatabase/Statement.ts +7 -3
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +35 -0
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +6 -4
- package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +7 -6
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +6 -4
- package/Server/Utils/Telemetry/IoTSnapshotScan.ts +45 -7
- package/Server/Utils/WhatsAppTemplateUtil.ts +10 -0
- package/ServiceRoute.ts +4 -0
- package/Tests/Server/Services/IoTDeviceService.test.ts +280 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +49 -0
- package/Tests/Server/Utils/Monitor/Criteria/TelemetryStepMetricAlias.test.ts +390 -0
- package/Tests/Server/Utils/Telemetry/IoTSnapshotScan.test.ts +202 -0
- package/Tests/Utils/EnterpriseLicenseUsage.test.ts +165 -0
- package/Types/Email/EmailTemplateType.ts +3 -0
- package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +18 -0
- package/Types/Monitor/MonitorStep.ts +32 -0
- package/Types/NotificationSetting/NotificationSettingEventType.ts +3 -0
- package/Types/Permission.ts +144 -0
- package/Types/Reminder/ReminderStopState.ts +6 -0
- package/Types/Reminder/ScheduledMaintenanceReminderStopState.ts +6 -0
- package/Types/WhatsApp/WhatsAppTemplates.ts +14 -0
- package/UI/Components/EditionLabel/EditionLabel.tsx +172 -0
- package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +125 -0
- package/build/dist/Models/AnalyticsModels/AnalyticsBaseModel/CommonModel.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js +1 -0
- package/build/dist/Models/AnalyticsModels/ExceptionInstance.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Log.js +1 -0
- package/build/dist/Models/AnalyticsModels/Log.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Metric.js +1 -0
- package/build/dist/Models/AnalyticsModels/Metric.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/MonitorLog.js +1 -0
- package/build/dist/Models/AnalyticsModels/MonitorLog.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/Span.js +1 -0
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Alert.js +103 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertInternalNote.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertReminderRule.js +605 -0
- package/build/dist/Models/DatabaseModels/AlertReminderRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AlertSeverity.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertSeverity.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertState.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertState.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js +197 -0
- package/build/dist/Models/DatabaseModels/EnterpriseLicenseInstance.js.map +1 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +18 -0
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +103 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentInternalNote.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentInternalNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentReminderRule.js +605 -0
- package/build/dist/Models/DatabaseModels/IncidentReminderRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentSeverity.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentSeverity.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +8 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Label.js +2 -0
- package/build/dist/Models/DatabaseModels/Label.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +103 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceReminderRule.js +596 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceReminderRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceState.js +2 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceState.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +2 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +2 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +166 -16
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/EnterpriseLicenseAPI.js +203 -13
- package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
- package/build/dist/Server/API/GlobalConfigAPI.js +62 -15
- package/build/dist/Server/API/GlobalConfigAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +74 -31
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +6 -0
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783226868418-AddEnterpriseLicenseInstance.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783226868418-AddEnterpriseLicenseInstance.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783234451106-AddIncidentAlertReminderRules.js +78 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783234451106-AddIncidentAlertReminderRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783320000000-AddScheduledMaintenanceReminderRules.js +44 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783320000000-AddScheduledMaintenanceReminderRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783330000000-AddRemindWhileScheduledToScheduledMaintenanceReminderRule.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783330000000-AddRemindWhileScheduledToScheduledMaintenanceReminderRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783340000000-AddLabelsToIncidentAndAlertReminderRules.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1783340000000-AddLabelsToIncidentAndAlertReminderRules.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertReminderRuleService.js +228 -0
- package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -0
- package/build/dist/Server/Services/AlertService.js +87 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js +9 -0
- package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js.map +1 -0
- package/build/dist/Server/Services/IncidentReminderRuleService.js +231 -0
- package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -0
- package/build/dist/Server/Services/IncidentService.js +116 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +8 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTDeviceService.js +66 -4
- package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +214 -0
- package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +102 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +91 -43
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryExceptionService.js +101 -0
- package/build/dist/Server/Services/TelemetryExceptionService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +6 -0
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +4 -2
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +29 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +4 -5
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +6 -5
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -9
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js +33 -2
- package/build/dist/Server/Utils/Telemetry/IoTSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/WhatsAppTemplateUtil.js +6 -0
- package/build/dist/Server/Utils/WhatsAppTemplateUtil.js.map +1 -1
- package/build/dist/ServiceRoute.js +1 -0
- package/build/dist/ServiceRoute.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +3 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.js +2 -0
- package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorStep.js +25 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js +3 -0
- package/build/dist/Types/NotificationSetting/NotificationSettingEventType.js.map +1 -1
- package/build/dist/Types/Permission.js +126 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Reminder/ReminderStopState.js +7 -0
- package/build/dist/Types/Reminder/ReminderStopState.js.map +1 -0
- package/build/dist/Types/Reminder/ScheduledMaintenanceReminderStopState.js +7 -0
- package/build/dist/Types/Reminder/ScheduledMaintenanceReminderStopState.js.map +1 -0
- package/build/dist/Types/WhatsApp/WhatsAppTemplates.js +9 -0
- package/build/dist/Types/WhatsApp/WhatsAppTemplates.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +92 -0
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +83 -0
- package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -0
- package/package.json +1 -1
|
@@ -56,6 +56,8 @@ import logger, { LogAttributes } from "../Utils/Logger";
|
|
|
56
56
|
import Semaphore, { SemaphoreMutex } from "../Infrastructure/Semaphore";
|
|
57
57
|
import IncidentFeedService from "./IncidentFeedService";
|
|
58
58
|
import IncidentSlaService from "./IncidentSlaService";
|
|
59
|
+
import IncidentReminderRuleService from "./IncidentReminderRuleService";
|
|
60
|
+
import IncidentReminderRule from "../../Models/DatabaseModels/IncidentReminderRule";
|
|
59
61
|
import { setIsPublicForMarkdownImages } from "../Utils/InlineImageAccessTokenSync";
|
|
60
62
|
import { IncidentFeedEventType } from "../../Models/DatabaseModels/IncidentFeed";
|
|
61
63
|
import IncidentGroupingEngineService from "./IncidentGroupingEngineService";
|
|
@@ -230,6 +232,64 @@ export class Service extends DatabaseService<Model> {
|
|
|
230
232
|
return false;
|
|
231
233
|
}
|
|
232
234
|
|
|
235
|
+
@CaptureSpan()
|
|
236
|
+
public async refreshReminderSchedule(data: {
|
|
237
|
+
incidentId: ObjectID;
|
|
238
|
+
projectId: ObjectID;
|
|
239
|
+
}): Promise<void> {
|
|
240
|
+
const incident: Model | null = await this.findOneById({
|
|
241
|
+
id: data.incidentId,
|
|
242
|
+
select: {
|
|
243
|
+
enableReminders: true,
|
|
244
|
+
incidentSeverityId: true,
|
|
245
|
+
labels: {
|
|
246
|
+
_id: true,
|
|
247
|
+
},
|
|
248
|
+
},
|
|
249
|
+
props: {
|
|
250
|
+
isRoot: true,
|
|
251
|
+
},
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
if (!incident) {
|
|
255
|
+
return;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
let nextReminderNotificationAt: Date | null = null;
|
|
259
|
+
|
|
260
|
+
if (incident.enableReminders !== false) {
|
|
261
|
+
const matchingRule: IncidentReminderRule | null =
|
|
262
|
+
await IncidentReminderRuleService.findMatchingRule({
|
|
263
|
+
projectId: data.projectId,
|
|
264
|
+
incidentSeverityId: incident.incidentSeverityId,
|
|
265
|
+
labelIds: incident.labels?.map((label: Label) => {
|
|
266
|
+
return label.id!;
|
|
267
|
+
}),
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
if (
|
|
271
|
+
matchingRule &&
|
|
272
|
+
matchingRule.reminderIntervalInMinutes &&
|
|
273
|
+
!(await this.isIncidentResolved({ incidentId: data.incidentId }))
|
|
274
|
+
) {
|
|
275
|
+
nextReminderNotificationAt = OneUptimeDate.addRemoveMinutes(
|
|
276
|
+
OneUptimeDate.getCurrentDate(),
|
|
277
|
+
matchingRule.reminderIntervalInMinutes,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
await this.updateOneById({
|
|
283
|
+
id: data.incidentId,
|
|
284
|
+
data: {
|
|
285
|
+
nextReminderNotificationAt: nextReminderNotificationAt,
|
|
286
|
+
},
|
|
287
|
+
props: {
|
|
288
|
+
isRoot: true,
|
|
289
|
+
},
|
|
290
|
+
});
|
|
291
|
+
}
|
|
292
|
+
|
|
233
293
|
@CaptureSpan()
|
|
234
294
|
public async resolveIncident(
|
|
235
295
|
incidentId: ObjectID,
|
|
@@ -1161,6 +1221,26 @@ export class Service extends DatabaseService<Model> {
|
|
|
1161
1221
|
);
|
|
1162
1222
|
}
|
|
1163
1223
|
})
|
|
1224
|
+
.then(async () => {
|
|
1225
|
+
// Schedule reminder notifications for incident if a matching reminder rule exists
|
|
1226
|
+
try {
|
|
1227
|
+
if (createdItem.projectId && createdItem.id) {
|
|
1228
|
+
await this.refreshReminderSchedule({
|
|
1229
|
+
incidentId: createdItem.id,
|
|
1230
|
+
projectId: createdItem.projectId,
|
|
1231
|
+
});
|
|
1232
|
+
}
|
|
1233
|
+
} catch (error) {
|
|
1234
|
+
logger.error(
|
|
1235
|
+
`Reminder scheduling failed in IncidentService.onCreateSuccess: ${error}`,
|
|
1236
|
+
{
|
|
1237
|
+
projectId: createdItem.projectId?.toString(),
|
|
1238
|
+
incidentId: createdItem.id?.toString(),
|
|
1239
|
+
userId: createdItem.createdByUserId?.toString(),
|
|
1240
|
+
} as LogAttributes,
|
|
1241
|
+
);
|
|
1242
|
+
}
|
|
1243
|
+
})
|
|
1164
1244
|
.catch((error: Error) => {
|
|
1165
1245
|
logger.error(
|
|
1166
1246
|
`Critical error in IncidentService sequential operations: ${error}`,
|
|
@@ -1728,6 +1808,29 @@ ${incident.remediationNotes || "No remediation notes provided."}
|
|
|
1728
1808
|
});
|
|
1729
1809
|
}
|
|
1730
1810
|
|
|
1811
|
+
// Re-evaluate reminder schedule when reminders are enabled or disabled for this incident
|
|
1812
|
+
if (
|
|
1813
|
+
Object.prototype.hasOwnProperty.call(
|
|
1814
|
+
updatedIncidentData,
|
|
1815
|
+
"enableReminders",
|
|
1816
|
+
)
|
|
1817
|
+
) {
|
|
1818
|
+
try {
|
|
1819
|
+
await this.refreshReminderSchedule({
|
|
1820
|
+
incidentId: incidentId,
|
|
1821
|
+
projectId: projectId,
|
|
1822
|
+
});
|
|
1823
|
+
} catch (reminderError) {
|
|
1824
|
+
logger.error(
|
|
1825
|
+
`Reminder rescheduling failed in IncidentService.onUpdateSuccess: ${reminderError}`,
|
|
1826
|
+
{
|
|
1827
|
+
projectId: projectId?.toString(),
|
|
1828
|
+
incidentId: incidentId?.toString(),
|
|
1829
|
+
} as LogAttributes,
|
|
1830
|
+
);
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
|
|
1731
1834
|
// emit postmortem completion time metric when postmortemPostedAt is set
|
|
1732
1835
|
if (
|
|
1733
1836
|
Object.prototype.hasOwnProperty.call(
|
|
@@ -1976,6 +2079,30 @@ ${labels
|
|
|
1976
2079
|
}
|
|
1977
2080
|
}
|
|
1978
2081
|
|
|
2082
|
+
/*
|
|
2083
|
+
* Re-match reminder rule on any labels change (including clearing all
|
|
2084
|
+
* labels), since labels can change which reminder rule matches.
|
|
2085
|
+
*/
|
|
2086
|
+
if (
|
|
2087
|
+
updatedIncidentData.labels &&
|
|
2088
|
+
Array.isArray(updatedIncidentData.labels)
|
|
2089
|
+
) {
|
|
2090
|
+
try {
|
|
2091
|
+
await this.refreshReminderSchedule({
|
|
2092
|
+
incidentId: incidentId,
|
|
2093
|
+
projectId: projectId,
|
|
2094
|
+
});
|
|
2095
|
+
} catch (reminderError) {
|
|
2096
|
+
logger.error(
|
|
2097
|
+
`Reminder rescheduling failed in IncidentService.onUpdateSuccess: ${reminderError}`,
|
|
2098
|
+
{
|
|
2099
|
+
projectId: projectId?.toString(),
|
|
2100
|
+
incidentId: incidentId?.toString(),
|
|
2101
|
+
} as LogAttributes,
|
|
2102
|
+
);
|
|
2103
|
+
}
|
|
2104
|
+
}
|
|
2105
|
+
|
|
1979
2106
|
if (
|
|
1980
2107
|
updatedIncidentData.incidentSeverity &&
|
|
1981
2108
|
(updatedIncidentData.incidentSeverity as any)._id
|
|
@@ -2017,6 +2144,22 @@ ${incidentSeverity.name}
|
|
|
2017
2144
|
);
|
|
2018
2145
|
}
|
|
2019
2146
|
|
|
2147
|
+
// Re-match reminder rule when severity changes
|
|
2148
|
+
try {
|
|
2149
|
+
await this.refreshReminderSchedule({
|
|
2150
|
+
incidentId: incidentId,
|
|
2151
|
+
projectId: projectId,
|
|
2152
|
+
});
|
|
2153
|
+
} catch (reminderError) {
|
|
2154
|
+
logger.error(
|
|
2155
|
+
`Reminder rescheduling failed in IncidentService.onUpdateSuccess: ${reminderError}`,
|
|
2156
|
+
{
|
|
2157
|
+
projectId: projectId?.toString(),
|
|
2158
|
+
incidentId: incidentId?.toString(),
|
|
2159
|
+
} as LogAttributes,
|
|
2160
|
+
);
|
|
2161
|
+
}
|
|
2162
|
+
|
|
2020
2163
|
// emit severity change metric
|
|
2021
2164
|
try {
|
|
2022
2165
|
const severityChangeMetric: MutableMetric = new MutableMetric();
|
package/Server/Services/Index.ts
CHANGED
|
@@ -113,6 +113,7 @@ import GlobalSsoProjectService from "./GlobalSsoProjectService";
|
|
|
113
113
|
import GlobalOidcProjectService from "./GlobalOidcProjectService";
|
|
114
114
|
import PromoCodeService from "./PromoCodeService";
|
|
115
115
|
import EnterpriseLicenseService from "./EnterpriseLicenseService";
|
|
116
|
+
import EnterpriseLicenseInstanceService from "./EnterpriseLicenseInstanceService";
|
|
116
117
|
import OpenSourceDeploymentService from "./OpenSourceDeploymentService";
|
|
117
118
|
import ResellerPlanService from "./ResellerPlanService";
|
|
118
119
|
import ResellerService from "./ResellerService";
|
|
@@ -225,6 +226,9 @@ import IncidentEpisodePublicNoteService from "./IncidentEpisodePublicNoteService
|
|
|
225
226
|
import AlertGroupingRuleService from "./AlertGroupingRuleService";
|
|
226
227
|
import IncidentSlaRuleService from "./IncidentSlaRuleService";
|
|
227
228
|
import IncidentSlaService from "./IncidentSlaService";
|
|
229
|
+
import IncidentReminderRuleService from "./IncidentReminderRuleService";
|
|
230
|
+
import AlertReminderRuleService from "./AlertReminderRuleService";
|
|
231
|
+
import ScheduledMaintenanceReminderRuleService from "./ScheduledMaintenanceReminderRuleService";
|
|
228
232
|
|
|
229
233
|
import TableViewService from "./TableViewService";
|
|
230
234
|
import ScheduledMaintenanceFeedService from "./ScheduledMaintenanceFeedService";
|
|
@@ -251,6 +255,7 @@ const services: Array<BaseService> = [
|
|
|
251
255
|
AcmeCertificateService,
|
|
252
256
|
PromoCodeService,
|
|
253
257
|
EnterpriseLicenseService,
|
|
258
|
+
EnterpriseLicenseInstanceService,
|
|
254
259
|
OpenSourceDeploymentService,
|
|
255
260
|
|
|
256
261
|
ResellerService,
|
|
@@ -479,6 +484,9 @@ const services: Array<BaseService> = [
|
|
|
479
484
|
AlertGroupingRuleService,
|
|
480
485
|
IncidentSlaRuleService,
|
|
481
486
|
IncidentSlaService,
|
|
487
|
+
IncidentReminderRuleService,
|
|
488
|
+
AlertReminderRuleService,
|
|
489
|
+
ScheduledMaintenanceReminderRuleService,
|
|
482
490
|
|
|
483
491
|
TableViewService,
|
|
484
492
|
MonitorTestService,
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import DatabaseService from "./DatabaseService";
|
|
2
2
|
import Model from "../../Models/DatabaseModels/IoTDevice";
|
|
3
3
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
4
|
+
import ColumnLength from "../../Types/Database/ColumnLength";
|
|
4
5
|
import ObjectID from "../../Types/ObjectID";
|
|
5
6
|
import OneUptimeDate from "../../Types/Date";
|
|
7
|
+
import { clampIoTTimestamp } from "../Utils/Telemetry/IoTSnapshotScan";
|
|
6
8
|
import logger from "../Utils/Logger";
|
|
7
9
|
|
|
8
10
|
/*
|
|
@@ -57,6 +59,23 @@ export interface IoTInventorySummary {
|
|
|
57
59
|
const UPSERT_BATCH_SIZE: number = 500;
|
|
58
60
|
const STALE_DELETE_WARN_THRESHOLD: number = 100;
|
|
59
61
|
|
|
62
|
+
/*
|
|
63
|
+
* The identity/text columns are ShortText (100 chars). A single value
|
|
64
|
+
* over the column limit fails the whole multi-row INSERT chunk it rides
|
|
65
|
+
* in — truncate per value instead so one malformed device can never
|
|
66
|
+
* drop the other rows of its chunk.
|
|
67
|
+
*/
|
|
68
|
+
function truncateShortText(value: string): string;
|
|
69
|
+
function truncateShortText(value: string | null): string | null;
|
|
70
|
+
function truncateShortText(value: string | null): string | null {
|
|
71
|
+
if (value === null) {
|
|
72
|
+
return value;
|
|
73
|
+
}
|
|
74
|
+
return value.length > ColumnLength.ShortText
|
|
75
|
+
? value.substring(0, ColumnLength.ShortText)
|
|
76
|
+
: value;
|
|
77
|
+
}
|
|
78
|
+
|
|
60
79
|
/*
|
|
61
80
|
* Column order used by bulkUpsert() and its generated parameter tuples.
|
|
62
81
|
* Keep this and the INSERT column list in perfect sync.
|
|
@@ -103,9 +122,52 @@ export class Service extends DatabaseService<Model> {
|
|
|
103
122
|
return;
|
|
104
123
|
}
|
|
105
124
|
|
|
125
|
+
/*
|
|
126
|
+
* Sanitize before building chunks: ShortText columns truncate to
|
|
127
|
+
* the column limit (one over-long device.id must not fail the
|
|
128
|
+
* whole 500-row chunk), lastSeenAt clamps so a future-skewed
|
|
129
|
+
* device clock can't wedge the `>=` dominance guard, and rows that
|
|
130
|
+
* collide on the conflict target after truncation dedupe to the
|
|
131
|
+
* newest observation (two VALUES rows hitting the same conflict
|
|
132
|
+
* target would abort the statement).
|
|
133
|
+
*/
|
|
134
|
+
const dedupedByIdentity: Map<string, ParsedIoTDevice> = new Map();
|
|
135
|
+
for (const r of data.devices) {
|
|
136
|
+
const externalId: string = truncateShortText(r.externalId);
|
|
137
|
+
if (!externalId) {
|
|
138
|
+
continue;
|
|
139
|
+
}
|
|
140
|
+
if (externalId !== r.externalId) {
|
|
141
|
+
logger.warn(
|
|
142
|
+
`IoTDevice externalId exceeds ${ColumnLength.ShortText} chars; truncated to "${externalId}" (fleet ${data.iotFleetId.toString()}).`,
|
|
143
|
+
);
|
|
144
|
+
}
|
|
145
|
+
const device: ParsedIoTDevice = {
|
|
146
|
+
...r,
|
|
147
|
+
kind: truncateShortText(r.kind),
|
|
148
|
+
externalId,
|
|
149
|
+
name: truncateShortText(r.name),
|
|
150
|
+
deviceType: truncateShortText(r.deviceType),
|
|
151
|
+
firmwareVersion: truncateShortText(r.firmwareVersion),
|
|
152
|
+
lastSeenAt: clampIoTTimestamp(r.lastSeenAt),
|
|
153
|
+
};
|
|
154
|
+
const key: string = `${device.kind}|${device.externalId}`;
|
|
155
|
+
const existing: ParsedIoTDevice | undefined = dedupedByIdentity.get(key);
|
|
156
|
+
if (!existing || device.lastSeenAt >= existing.lastSeenAt) {
|
|
157
|
+
dedupedByIdentity.set(key, device);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const devices: Array<ParsedIoTDevice> = Array.from(
|
|
162
|
+
dedupedByIdentity.values(),
|
|
163
|
+
);
|
|
164
|
+
if (devices.length === 0) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
|
|
106
168
|
// Chunk to keep individual statement parameter counts reasonable.
|
|
107
|
-
for (let i: number = 0; i <
|
|
108
|
-
const chunk: Array<ParsedIoTDevice> =
|
|
169
|
+
for (let i: number = 0; i < devices.length; i += UPSERT_BATCH_SIZE) {
|
|
170
|
+
const chunk: Array<ParsedIoTDevice> = devices.slice(
|
|
109
171
|
i,
|
|
110
172
|
i + UPSERT_BATCH_SIZE,
|
|
111
173
|
);
|
|
@@ -181,8 +243,41 @@ export class Service extends DatabaseService<Model> {
|
|
|
181
243
|
return;
|
|
182
244
|
}
|
|
183
245
|
|
|
184
|
-
|
|
185
|
-
|
|
246
|
+
/*
|
|
247
|
+
* Same sanitation as bulkUpsert — and the identity truncation MUST
|
|
248
|
+
* match it, or the mirror UPDATE would silently miss the row the
|
|
249
|
+
* upsert just wrote under the truncated id. Dedupe keeps the UPDATE
|
|
250
|
+
* deterministic when two rows collapse onto one identity.
|
|
251
|
+
*/
|
|
252
|
+
const dedupedByIdentity: Map<string, IoTDeviceLatestMetric> = new Map();
|
|
253
|
+
for (const m of data.metrics) {
|
|
254
|
+
const externalId: string = truncateShortText(m.externalId);
|
|
255
|
+
if (!externalId) {
|
|
256
|
+
continue;
|
|
257
|
+
}
|
|
258
|
+
const metric: IoTDeviceLatestMetric = {
|
|
259
|
+
...m,
|
|
260
|
+
kind: truncateShortText(m.kind),
|
|
261
|
+
externalId,
|
|
262
|
+
observedAt: clampIoTTimestamp(m.observedAt),
|
|
263
|
+
};
|
|
264
|
+
const key: string = `${metric.kind}|${metric.externalId}`;
|
|
265
|
+
const existing: IoTDeviceLatestMetric | undefined =
|
|
266
|
+
dedupedByIdentity.get(key);
|
|
267
|
+
if (!existing || metric.observedAt >= existing.observedAt) {
|
|
268
|
+
dedupedByIdentity.set(key, metric);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
const metrics: Array<IoTDeviceLatestMetric> = Array.from(
|
|
273
|
+
dedupedByIdentity.values(),
|
|
274
|
+
);
|
|
275
|
+
if (metrics.length === 0) {
|
|
276
|
+
return;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
for (let i: number = 0; i < metrics.length; i += UPSERT_BATCH_SIZE) {
|
|
280
|
+
const chunk: Array<IoTDeviceLatestMetric> = metrics.slice(
|
|
186
281
|
i,
|
|
187
282
|
i + UPSERT_BATCH_SIZE,
|
|
188
283
|
);
|
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import CreateBy from "../Types/Database/CreateBy";
|
|
2
|
+
import { OnCreate, OnDelete, OnUpdate } from "../Types/Database/Hooks";
|
|
3
|
+
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
4
|
+
import DatabaseService from "./DatabaseService";
|
|
5
|
+
import Model from "../../Models/DatabaseModels/ScheduledMaintenanceReminderRule";
|
|
6
|
+
import ScheduledMaintenance from "../../Models/DatabaseModels/ScheduledMaintenance";
|
|
7
|
+
import Label from "../../Models/DatabaseModels/Label";
|
|
8
|
+
import ScheduledMaintenanceState from "../../Models/DatabaseModels/ScheduledMaintenanceState";
|
|
9
|
+
import ScheduledMaintenanceService from "./ScheduledMaintenanceService";
|
|
10
|
+
import ScheduledMaintenanceStateService from "./ScheduledMaintenanceStateService";
|
|
11
|
+
import QueryHelper from "../Types/Database/QueryHelper";
|
|
12
|
+
import LIMIT_MAX, { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
13
|
+
import ObjectID from "../../Types/ObjectID";
|
|
14
|
+
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
15
|
+
import logger, { LogAttributes } from "../Utils/Logger";
|
|
16
|
+
import { IsBillingEnabled } from "../EnvironmentConfig";
|
|
17
|
+
|
|
18
|
+
export class Service extends DatabaseService<Model> {
|
|
19
|
+
public constructor() {
|
|
20
|
+
super(Model);
|
|
21
|
+
if (IsBillingEnabled) {
|
|
22
|
+
this.hardDeleteItemsOlderThanInDays("createdAt", 3 * 365); // 3 years
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
@CaptureSpan()
|
|
27
|
+
protected override async onBeforeCreate(
|
|
28
|
+
createBy: CreateBy<Model>,
|
|
29
|
+
): Promise<OnCreate<Model>> {
|
|
30
|
+
// Auto-assign order on creation
|
|
31
|
+
if (!createBy.data.order) {
|
|
32
|
+
const highestOrderRule: Model | null = await this.findOneBy({
|
|
33
|
+
query: {
|
|
34
|
+
projectId: createBy.data.projectId!,
|
|
35
|
+
},
|
|
36
|
+
select: {
|
|
37
|
+
order: true,
|
|
38
|
+
},
|
|
39
|
+
sort: {
|
|
40
|
+
order: SortOrder.Descending,
|
|
41
|
+
},
|
|
42
|
+
props: {
|
|
43
|
+
isRoot: true,
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
createBy.data.order = (highestOrderRule?.order || 0) + 1;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return {
|
|
51
|
+
createBy,
|
|
52
|
+
carryForward: null,
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@CaptureSpan()
|
|
57
|
+
protected override async onCreateSuccess(
|
|
58
|
+
_onCreate: OnCreate<Model>,
|
|
59
|
+
createdItem: Model,
|
|
60
|
+
): Promise<Model> {
|
|
61
|
+
if (createdItem.projectId) {
|
|
62
|
+
await this.refreshSchedulesForOpenScheduledMaintenances(
|
|
63
|
+
createdItem.projectId,
|
|
64
|
+
);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return createdItem;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
@CaptureSpan()
|
|
71
|
+
protected override async onUpdateSuccess(
|
|
72
|
+
onUpdate: OnUpdate<Model>,
|
|
73
|
+
_updatedItemIds: Array<ObjectID>,
|
|
74
|
+
): Promise<OnUpdate<Model>> {
|
|
75
|
+
const projectId: ObjectID | undefined = onUpdate.updateBy.props.tenantId as
|
|
76
|
+
| ObjectID
|
|
77
|
+
| undefined;
|
|
78
|
+
|
|
79
|
+
if (projectId) {
|
|
80
|
+
await this.refreshSchedulesForOpenScheduledMaintenances(projectId);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
return onUpdate;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@CaptureSpan()
|
|
87
|
+
protected override async onDeleteSuccess(
|
|
88
|
+
onDelete: OnDelete<Model>,
|
|
89
|
+
_itemIdsBeforeDelete: Array<ObjectID>,
|
|
90
|
+
): Promise<OnDelete<Model>> {
|
|
91
|
+
const projectId: ObjectID | undefined = onDelete.deleteBy.props.tenantId as
|
|
92
|
+
| ObjectID
|
|
93
|
+
| undefined;
|
|
94
|
+
|
|
95
|
+
if (projectId) {
|
|
96
|
+
await this.refreshSchedulesForOpenScheduledMaintenances(projectId);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
return onDelete;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
@CaptureSpan()
|
|
103
|
+
public async refreshSchedulesForOpenScheduledMaintenances(
|
|
104
|
+
projectId: ObjectID,
|
|
105
|
+
): Promise<void> {
|
|
106
|
+
try {
|
|
107
|
+
const openStates: Array<ScheduledMaintenanceState> =
|
|
108
|
+
await ScheduledMaintenanceStateService.findBy({
|
|
109
|
+
query: {
|
|
110
|
+
projectId: projectId,
|
|
111
|
+
isResolvedState: false,
|
|
112
|
+
},
|
|
113
|
+
select: {
|
|
114
|
+
_id: true,
|
|
115
|
+
},
|
|
116
|
+
limit: LIMIT_PER_PROJECT,
|
|
117
|
+
skip: 0,
|
|
118
|
+
props: {
|
|
119
|
+
isRoot: true,
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const openStateIds: Array<ObjectID> = openStates
|
|
124
|
+
.map((state: ScheduledMaintenanceState) => {
|
|
125
|
+
return state.id!;
|
|
126
|
+
})
|
|
127
|
+
.filter(Boolean);
|
|
128
|
+
|
|
129
|
+
if (openStateIds.length === 0) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const openScheduledMaintenances: Array<ScheduledMaintenance> =
|
|
134
|
+
await ScheduledMaintenanceService.findBy({
|
|
135
|
+
query: {
|
|
136
|
+
projectId: projectId,
|
|
137
|
+
currentScheduledMaintenanceStateId: QueryHelper.any(openStateIds),
|
|
138
|
+
},
|
|
139
|
+
select: {
|
|
140
|
+
_id: true,
|
|
141
|
+
},
|
|
142
|
+
limit: LIMIT_MAX,
|
|
143
|
+
skip: 0,
|
|
144
|
+
props: {
|
|
145
|
+
isRoot: true,
|
|
146
|
+
},
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
for (const scheduledMaintenance of openScheduledMaintenances) {
|
|
150
|
+
try {
|
|
151
|
+
await ScheduledMaintenanceService.refreshReminderSchedule({
|
|
152
|
+
scheduledMaintenanceId: scheduledMaintenance.id!,
|
|
153
|
+
projectId: projectId,
|
|
154
|
+
});
|
|
155
|
+
} catch (error) {
|
|
156
|
+
logger.error(
|
|
157
|
+
`Failed to refresh reminder schedule for scheduled maintenance ${scheduledMaintenance.id}: ${error}`,
|
|
158
|
+
{ projectId: projectId?.toString() } as LogAttributes,
|
|
159
|
+
);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
} catch (error) {
|
|
163
|
+
logger.error(
|
|
164
|
+
`Failed to refresh reminder schedules for open scheduled maintenances: ${error}`,
|
|
165
|
+
{ projectId: projectId?.toString() } as LogAttributes,
|
|
166
|
+
);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
@CaptureSpan()
|
|
171
|
+
public async findMatchingRule(data: {
|
|
172
|
+
projectId: ObjectID;
|
|
173
|
+
labelIds?: Array<ObjectID> | undefined;
|
|
174
|
+
}): Promise<Model | null> {
|
|
175
|
+
// Get all enabled rules sorted by order. First matching rule wins.
|
|
176
|
+
const rules: Array<Model> = await this.findBy({
|
|
177
|
+
query: {
|
|
178
|
+
projectId: data.projectId,
|
|
179
|
+
isEnabled: true,
|
|
180
|
+
},
|
|
181
|
+
sort: {
|
|
182
|
+
order: SortOrder.Ascending,
|
|
183
|
+
},
|
|
184
|
+
select: {
|
|
185
|
+
_id: true,
|
|
186
|
+
name: true,
|
|
187
|
+
order: true,
|
|
188
|
+
reminderIntervalInMinutes: true,
|
|
189
|
+
stopRemindersOnState: true,
|
|
190
|
+
remindWhileScheduled: true,
|
|
191
|
+
labels: {
|
|
192
|
+
_id: true,
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
limit: 100,
|
|
196
|
+
skip: 0,
|
|
197
|
+
props: {
|
|
198
|
+
isRoot: true,
|
|
199
|
+
},
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
for (const rule of rules) {
|
|
203
|
+
if (rule.labels && rule.labels.length > 0) {
|
|
204
|
+
if (!data.labelIds || data.labelIds.length === 0) {
|
|
205
|
+
continue;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
const ruleLabelIds: Array<string> = rule.labels.map((label: Label) => {
|
|
209
|
+
return label.id?.toString() || "";
|
|
210
|
+
});
|
|
211
|
+
|
|
212
|
+
const hasMatchingLabel: boolean = data.labelIds.some(
|
|
213
|
+
(labelId: ObjectID) => {
|
|
214
|
+
return ruleLabelIds.includes(labelId.toString());
|
|
215
|
+
},
|
|
216
|
+
);
|
|
217
|
+
|
|
218
|
+
if (!hasMatchingLabel) {
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Rule with no labels matches all scheduled maintenances.
|
|
224
|
+
logger.debug(
|
|
225
|
+
`Scheduled maintenance reminder rule ${rule.name || rule.id} matched for project ${data.projectId}`,
|
|
226
|
+
{ projectId: data.projectId?.toString() } as LogAttributes,
|
|
227
|
+
);
|
|
228
|
+
|
|
229
|
+
return rule;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
return null;
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export default new Service();
|