@oneuptime/common 11.7.0 → 11.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/DashboardDomain.ts +1 -0
- package/Models/DatabaseModels/Incident.ts +2 -0
- package/Models/DatabaseModels/IncidentEpisode.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +1 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
- package/Models/DatabaseModels/Monitor.ts +1 -0
- package/Models/DatabaseModels/MonitorStatusTimeline.ts +1 -0
- package/Models/DatabaseModels/NetworkSite.ts +1 -0
- package/Models/DatabaseModels/NetworkSiteStatusTimeline.ts +1 -0
- package/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.ts +1 -0
- package/Models/DatabaseModels/OnCallDutyPolicySchedule.ts +2 -0
- package/Models/DatabaseModels/Probe.ts +13 -13
- package/Models/DatabaseModels/ScheduledMaintenance.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
- package/Models/DatabaseModels/ScheduledMaintenanceTemplate.ts +1 -0
- package/Models/DatabaseModels/StatusPage.ts +1 -0
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +1 -0
- package/Models/DatabaseModels/StatusPageDomain.ts +1 -0
- package/Models/DatabaseModels/WorkspaceNotificationSummary.ts +1 -0
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +11 -0
- package/Server/API/BaseAPI.ts +17 -1
- package/Server/API/CommonAPI.ts +32 -0
- package/Server/API/MicrosoftTeamsAPI.ts +99 -2
- package/Server/API/ProbeAPI.ts +2 -0
- package/Server/API/SlackAPI.ts +62 -0
- package/Server/API/StatusPageAPI.ts +157 -192
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.ts +89 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.ts +61 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.ts +280 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Services/DatabaseService.ts +11 -3
- package/Server/Services/MonitorGroupService.ts +145 -0
- package/Server/Services/MonitorService.ts +231 -30
- package/Server/Services/ProbeService.ts +319 -16
- package/Server/Services/StatusPageService.ts +72 -42
- package/Server/Services/WorkspaceNotificationRuleService.ts +232 -2
- package/Server/Utils/Database/PostgresErrorTranslator.ts +131 -0
- package/Server/Utils/Monitor/MonitorResource.ts +88 -35
- package/Server/Utils/Monitor/MonitorStatusTimeline.ts +21 -1
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +158 -0
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +706 -6
- package/Tests/Models/DatabaseModels/ProbeColumnAccessControl.test.ts +103 -0
- package/Tests/Server/API/BaseAPIUpdateNoOp.test.ts +135 -0
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +317 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +228 -0
- package/Tests/Server/API/ProbeAPI.test.ts +6 -0
- package/Tests/Server/API/ZZVerifyChatsAuth.test.ts +167 -0
- package/Tests/Server/Services/AddHotQueryIndexesMigration.test.ts +255 -0
- package/Tests/Server/Services/AddHotQueryIndexesSecondPassMigration.test.ts +258 -0
- package/Tests/Server/Services/DatabaseServiceSanitizeUpdateData.test.ts +1 -1
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +121 -0
- package/Tests/Server/Services/HeartbeatWriteFastPathSafety.test.ts +200 -0
- package/Tests/Server/Services/HookFreeWriteSafetySecondPass.test.ts +181 -0
- package/Tests/Server/Services/MonitorGroupServiceBatchedStatuses.test.ts +513 -0
- package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +492 -0
- package/Tests/Server/Services/MonitorServiceProbeSelection.test.ts +401 -0
- package/Tests/Server/Services/MonitorStatusTimelineFastPath.test.ts +453 -0
- package/Tests/Server/Services/ProbeAuthKeyCacheAndHeartbeat.test.ts +679 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +3 -3
- package/Tests/Server/Services/StatusPageDomainResolutionCache.test.ts +245 -0
- package/Tests/Server/Services/StatusPageOverviewTimelineWindow.test.ts +283 -0
- package/Tests/Server/Services/StatusPageServiceMcp.test.ts +17 -1
- package/Tests/Server/Services/StatusPageServiceMonitorGroupBatching.test.ts +331 -0
- package/Tests/Server/Services/UserOnCallLogClaimNotificationRule.test.ts +12 -12
- package/Tests/Server/Services/WorkspaceNotificationRuleChats.test.ts +1390 -0
- package/Tests/Server/Utils/AI/Insights/InsightFixRouting.test.ts +4 -4
- package/Tests/Server/Utils/AI/Insights/InsightScanner.test.ts +2 -2
- package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +1 -3
- package/Tests/Server/Utils/AI/Insights/InsightStoreHardening.test.ts +1 -3
- package/Tests/Server/Utils/AI/Insights/InsightTriage.test.ts +1 -1
- package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +5 -5
- package/Tests/Server/Utils/Alert/AlertPrivacyFilter.test.ts +187 -0
- package/Tests/Server/Utils/AlertEpisode/AlertEpisodePrivacyFilter.test.ts +170 -0
- package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +125 -0
- package/Tests/Server/Utils/Incident/IncidentPrivacyFilter.test.ts +264 -0
- package/Tests/Server/Utils/IncidentEpisode/IncidentEpisodePrivacyFilter.test.ts +175 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +181 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +1 -1
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +2 -2
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChannelsList.test.ts +341 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +2175 -0
- package/Tests/Server/Utils/Workspace/SlackChannelsList.test.ts +585 -0
- package/Tests/Types/Billing/MeteredPlan.test.ts +46 -0
- package/Tests/Types/DateUserTimezone.test.ts +205 -0
- package/Tests/Types/DateUserTimezoneOverridesBrowser.test.ts +73 -0
- package/Tests/Types/Workspace/NotificationRules/NotificationRuleCondition.test.ts +219 -1
- package/Tests/UI/Components/BulkUpdateForm.test.tsx +174 -0
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +315 -0
- package/Tests/UI/Components/ModelDetailSelect.test.tsx +229 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkSelectAll.test.tsx +1258 -0
- package/Tests/UI/Components/ModelTableExportFromColumns.test.ts +153 -0
- package/Tests/UI/Components/MonacoLoader.test.ts +67 -0
- package/Tests/UI/Components/MonacoRuntime.test.ts +121 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +212 -3
- package/Tests/UI/Components/TimePicker/TimePicker.test.tsx +11 -0
- package/Tests/UI/Utils/Project.test.ts +809 -0
- package/Tests/UI/Utils/TableColumnsToCsv.test.ts +360 -0
- package/Tests/Utils/Alerts/AlertMetricType.test.ts +143 -0
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +219 -0
- package/Tests/Utils/Incident/IncidentMetricType.test.ts +174 -0
- package/Tests/Utils/Monitor/MonitorProbeSelectionUtil.test.ts +145 -0
- package/Tests/Utils/StatusPage/ResourceUptime.test.ts +263 -0
- package/Types/Date.ts +119 -50
- package/Types/Workspace/NotificationRules/BaseNotificationRule.ts +4 -0
- package/Types/Workspace/NotificationRules/NotificationRuleCondition.ts +25 -10
- package/Types/Workspace/WorkspaceMessagePayload.ts +1 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +63 -8
- package/UI/Components/CodeEditor/CodeEditor.tsx +3 -0
- package/UI/Components/CodeEditor/MonacoLoader.ts +36 -0
- package/UI/Components/Forms/Fields/FormField.tsx +13 -3
- package/UI/Components/Forms/ModelForm.tsx +41 -18
- package/UI/Components/Input/Input.tsx +6 -1
- package/UI/Components/ModelDetail/CardModelDetail.tsx +23 -1
- package/UI/Components/ModelDetail/ModelDetail.tsx +46 -1
- package/UI/Components/ModelTable/BaseModelTable.tsx +300 -30
- package/UI/Components/ModelTable/Column.ts +12 -0
- package/UI/Components/ModelTable/ExportFromColumns.ts +57 -0
- package/UI/Components/Navbar/NavBar.tsx +66 -1
- package/UI/Components/Table/Table.tsx +52 -13
- package/UI/Components/Table/Types/Column.ts +18 -0
- package/UI/Components/TimePicker/TimePicker.tsx +8 -4
- package/UI/Utils/Project.ts +214 -18
- package/UI/Utils/TableColumnsToCsv.ts +86 -6
- package/UI/Utils/User.ts +19 -0
- package/UI/esbuild-config.js +34 -0
- package/Utils/Monitor/MonitorProbeSelectionUtil.ts +72 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js +1 -0
- package/build/dist/Models/DatabaseModels/DashboardDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +2 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisode.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Monitor.js +1 -0
- package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSite.js +1 -0
- package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/NetworkSiteStatusTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js +1 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyExecutionLog.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js +2 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicySchedule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Probe.js +13 -13
- package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceTemplate.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js +1 -0
- package/build/dist/Models/DatabaseModels/StatusPageDomain.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js +1 -0
- package/build/dist/Models/DatabaseModels/WorkspaceNotificationSummary.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +12 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +21 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +72 -2
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProbeAPI.js +2 -0
- package/build/dist/Server/API/ProbeAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +44 -0
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +98 -152
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785140242697-AddHotQueryIndexes.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js +26 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785148065137-AddHotQueryIndexesSecondPass.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js +254 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785240000000-RepairCrossProjectMonitorStatusReferences.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +10 -2
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/MonitorGroupService.js +118 -0
- package/build/dist/Server/Services/MonitorGroupService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +198 -24
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +238 -9
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageService.js +46 -39
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js +178 -3
- package/build/dist/Server/Services/WorkspaceNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +80 -0
- package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +67 -29
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js +31 -14
- package/build/dist/Server/Utils/Monitor/MonitorStatusTimeline.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +117 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -0
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +549 -7
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/Date.js +106 -35
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js +17 -7
- package/build/dist/Types/Workspace/NotificationRules/NotificationRuleCondition.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +37 -11
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +2 -0
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js +34 -0
- package/build/dist/UI/Components/CodeEditor/MonacoLoader.js.map +1 -0
- package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -3
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +37 -17
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +6 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +22 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js +31 -1
- package/build/dist/UI/Components/ModelDetail/ModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +234 -21
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ExportFromColumns.js +30 -0
- package/build/dist/UI/Components/ModelTable/ExportFromColumns.js.map +1 -0
- package/build/dist/UI/Components/Navbar/NavBar.js +39 -1
- package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
- package/build/dist/UI/Components/Table/Table.js +31 -11
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/TimePicker/TimePicker.js +4 -4
- package/build/dist/UI/Components/TimePicker/TimePicker.js.map +1 -1
- package/build/dist/UI/Utils/Project.js +161 -15
- package/build/dist/UI/Utils/Project.js.map +1 -1
- package/build/dist/UI/Utils/TableColumnsToCsv.js +67 -6
- package/build/dist/UI/Utils/TableColumnsToCsv.js.map +1 -1
- package/build/dist/UI/Utils/User.js +19 -0
- package/build/dist/UI/Utils/User.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js +52 -0
- package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js.map +1 -0
- package/package.json +4 -3
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.ts +0 -79
- package/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.ts +0 -41
- package/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.ts +0 -17
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.js +0 -34
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773414578773-MigrationName.js.map +0 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.js +0 -22
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1773500000000-MigrationName.js.map +0 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js +0 -12
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1776886248361-MigrationName.js.map +0 -1
|
@@ -31,6 +31,8 @@ import WorkspaceMessagePayload, {
|
|
|
31
31
|
WorkspacePayloadMarkdown,
|
|
32
32
|
} from "../../Types/Workspace/WorkspaceMessagePayload";
|
|
33
33
|
import WorkspaceProjectAuthToken, {
|
|
34
|
+
MicrosoftTeamsChat,
|
|
35
|
+
MicrosoftTeamsMiscData,
|
|
34
36
|
MiscData,
|
|
35
37
|
SlackMiscData,
|
|
36
38
|
} from "../../Models/DatabaseModels/WorkspaceProjectAuthToken";
|
|
@@ -445,6 +447,97 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
|
|
|
445
447
|
}
|
|
446
448
|
}
|
|
447
449
|
}
|
|
450
|
+
|
|
451
|
+
// Post test message to Microsoft Teams chats.
|
|
452
|
+
if (
|
|
453
|
+
rule.workspaceType === WorkspaceType.MicrosoftTeams &&
|
|
454
|
+
notificationRule.shouldPostToExistingChat
|
|
455
|
+
) {
|
|
456
|
+
const chatIds: Array<string> =
|
|
457
|
+
this.getExistingChatIdsFromNotificationRules({
|
|
458
|
+
notificationRules: [notificationRule],
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
if (chatIds.length === 0) {
|
|
462
|
+
throw new BadDataException(
|
|
463
|
+
"No Microsoft Teams chats are selected in this rule. Please edit the rule and select at least one chat.",
|
|
464
|
+
);
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
const connectedChats: Record<string, MicrosoftTeamsChat> =
|
|
468
|
+
await this.getConnectedMicrosoftTeamsChats({
|
|
469
|
+
projectId: data.projectId,
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
for (const chatId of chatIds) {
|
|
473
|
+
if (!connectedChats[chatId]) {
|
|
474
|
+
throw new BadDataException(
|
|
475
|
+
`The selected chat (id: ${chatId}) is no longer connected to OneUptime. Please add the OneUptime app to the chat in Microsoft Teams and update this rule.`,
|
|
476
|
+
);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
try {
|
|
481
|
+
const responses: Array<WorkspaceSendMessageResponse> =
|
|
482
|
+
await WorkspaceUtil.postMessageToAllWorkspaceChannelsAsBot({
|
|
483
|
+
projectId: data.projectId,
|
|
484
|
+
messagePayloadsByWorkspace: messageBlocksByWorkspaceTypes.map(
|
|
485
|
+
(messageBlocksByWorkspaceType: MessageBlocksByWorkspaceType) => {
|
|
486
|
+
const payload: WorkspaceMessagePayload = {
|
|
487
|
+
_type: "WorkspaceMessagePayload",
|
|
488
|
+
workspaceType: messageBlocksByWorkspaceType.workspaceType,
|
|
489
|
+
messageBlocks: messageBlocksByWorkspaceType.messageBlocks,
|
|
490
|
+
channelNames: [],
|
|
491
|
+
channelIds: [],
|
|
492
|
+
chatIds: chatIds,
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
return payload;
|
|
496
|
+
},
|
|
497
|
+
),
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
for (const res of responses) {
|
|
501
|
+
// Check for errors in the response
|
|
502
|
+
if (res.errors && res.errors.length > 0) {
|
|
503
|
+
const errorMessages: Array<string> = res.errors.map(
|
|
504
|
+
(error: { channel: WorkspaceChannel; error: string }) => {
|
|
505
|
+
return `Chat ${error.channel.name}: ${error.error}`;
|
|
506
|
+
},
|
|
507
|
+
);
|
|
508
|
+
throw new BadDataException(
|
|
509
|
+
`Failed to send test message to some chats: ${errorMessages.join(
|
|
510
|
+
"; ",
|
|
511
|
+
)}`,
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
for (const thread of res.threads) {
|
|
516
|
+
const log: WorkspaceNotificationLog =
|
|
517
|
+
new WorkspaceNotificationLog();
|
|
518
|
+
log.projectId = data.projectId;
|
|
519
|
+
log.workspaceType = res.workspaceType;
|
|
520
|
+
log.channelId = thread.channel.id;
|
|
521
|
+
log.channelName = thread.channel.name;
|
|
522
|
+
log.threadId = thread.threadId;
|
|
523
|
+
log.message = `This is a test message for rule **${rule.name?.trim()}**`;
|
|
524
|
+
log.status = WorkspaceNotificationStatus.Success;
|
|
525
|
+
log.statusMessage = "Test message posted to workspace chat";
|
|
526
|
+
log.userId = data.testByUserId;
|
|
527
|
+
log.actionType = WorkspaceNotificationActionType.SendMessage;
|
|
528
|
+
|
|
529
|
+
await WorkspaceNotificationLogService.create({
|
|
530
|
+
data: log,
|
|
531
|
+
props: { isRoot: true },
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
} catch (err) {
|
|
536
|
+
throw new BadDataException(
|
|
537
|
+
"Cannot post message to chat. " + (err as Error)?.message,
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
448
541
|
}
|
|
449
542
|
|
|
450
543
|
@CaptureSpan()
|
|
@@ -614,6 +707,29 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
|
|
|
614
707
|
};
|
|
615
708
|
workspaceNotificationPaylaods.push(workspaceMessagePayload);
|
|
616
709
|
}
|
|
710
|
+
|
|
711
|
+
// Microsoft Teams chats (group / personal chats the OneUptime app was added to).
|
|
712
|
+
const existingChatIds: Array<string> =
|
|
713
|
+
await this.getExistingChatIdsBasedOnEventType({
|
|
714
|
+
projectId: data.projectId,
|
|
715
|
+
notificationRuleEventType: this.getNotificationRuleEventType(
|
|
716
|
+
data.notificationFor,
|
|
717
|
+
),
|
|
718
|
+
workspaceType: messageBlocksByWorkspaceType.workspaceType,
|
|
719
|
+
notificationFor: data.notificationFor,
|
|
720
|
+
});
|
|
721
|
+
|
|
722
|
+
for (const chatId of existingChatIds) {
|
|
723
|
+
const workspaceMessagePayload: WorkspaceMessagePayload = {
|
|
724
|
+
_type: "WorkspaceMessagePayload",
|
|
725
|
+
workspaceType: messageBlocksByWorkspaceType.workspaceType,
|
|
726
|
+
messageBlocks: messageBlocksByWorkspaceType.messageBlocks,
|
|
727
|
+
channelNames: [],
|
|
728
|
+
channelIds: [],
|
|
729
|
+
chatIds: [chatId],
|
|
730
|
+
};
|
|
731
|
+
workspaceNotificationPaylaods.push(workspaceMessagePayload);
|
|
732
|
+
}
|
|
617
733
|
}
|
|
618
734
|
|
|
619
735
|
const responses: Array<WorkspaceSendMessageResponse> =
|
|
@@ -655,8 +771,8 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
|
|
|
655
771
|
const log: WorkspaceNotificationLog = new WorkspaceNotificationLog();
|
|
656
772
|
log.projectId = data.projectId;
|
|
657
773
|
log.workspaceType = res.workspaceType;
|
|
658
|
-
log.channelId = thread.channel.id;
|
|
659
|
-
log.channelName = thread.channel.name;
|
|
774
|
+
log.channelId = (thread.channel.id || "").substring(0, 100);
|
|
775
|
+
log.channelName = (thread.channel.name || "").substring(0, 100);
|
|
660
776
|
log.threadId = thread.threadId;
|
|
661
777
|
log.message = messageSummary;
|
|
662
778
|
log.status = WorkspaceNotificationStatus.Success;
|
|
@@ -683,6 +799,45 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
|
|
|
683
799
|
props: { isRoot: true },
|
|
684
800
|
});
|
|
685
801
|
}
|
|
802
|
+
|
|
803
|
+
/*
|
|
804
|
+
* Also log per-destination failures — otherwise a chat or channel
|
|
805
|
+
* send that errors (e.g. the OneUptime app was removed from a chat a
|
|
806
|
+
* rule still references) is invisible in the notification logs.
|
|
807
|
+
*/
|
|
808
|
+
for (const sendError of res.errors || []) {
|
|
809
|
+
const log: WorkspaceNotificationLog = new WorkspaceNotificationLog();
|
|
810
|
+
log.projectId = data.projectId;
|
|
811
|
+
log.workspaceType = res.workspaceType;
|
|
812
|
+
log.channelId = (sendError.channel.id || "").substring(0, 100);
|
|
813
|
+
log.channelName = (sendError.channel.name || "").substring(0, 100);
|
|
814
|
+
log.message = messageSummary;
|
|
815
|
+
log.status = WorkspaceNotificationStatus.Error;
|
|
816
|
+
log.actionType = WorkspaceNotificationActionType.SendMessage;
|
|
817
|
+
log.statusMessage = (sendError.error || "Failed to send message")
|
|
818
|
+
.toString()
|
|
819
|
+
.substring(0, 500);
|
|
820
|
+
|
|
821
|
+
if (data.notificationFor.incidentId) {
|
|
822
|
+
log.incidentId = data.notificationFor.incidentId;
|
|
823
|
+
}
|
|
824
|
+
if (data.notificationFor.alertId) {
|
|
825
|
+
log.alertId = data.notificationFor.alertId;
|
|
826
|
+
}
|
|
827
|
+
if (data.notificationFor.scheduledMaintenanceId) {
|
|
828
|
+
log.scheduledMaintenanceId =
|
|
829
|
+
data.notificationFor.scheduledMaintenanceId;
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
if (data.workspaceNotification.notifyUserId) {
|
|
833
|
+
log.userId = data.workspaceNotification.notifyUserId;
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
await WorkspaceNotificationLogService.create({
|
|
837
|
+
data: log,
|
|
838
|
+
props: { isRoot: true },
|
|
839
|
+
});
|
|
840
|
+
}
|
|
686
841
|
}
|
|
687
842
|
}
|
|
688
843
|
|
|
@@ -1951,6 +2106,81 @@ export class Service extends DatabaseService<WorkspaceNotificationRule> {
|
|
|
1951
2106
|
return channels;
|
|
1952
2107
|
}
|
|
1953
2108
|
|
|
2109
|
+
/*
|
|
2110
|
+
* Collects the Microsoft Teams chat ids (group / personal chats) that
|
|
2111
|
+
* matching rules want to post to. Chats are Teams-only destinations.
|
|
2112
|
+
*/
|
|
2113
|
+
public getExistingChatIdsFromNotificationRules(data: {
|
|
2114
|
+
notificationRules: Array<BaseNotificationRule>;
|
|
2115
|
+
}): Array<string> {
|
|
2116
|
+
const chatIds: Array<string> = [];
|
|
2117
|
+
|
|
2118
|
+
for (const notificationRule of data.notificationRules) {
|
|
2119
|
+
if (!notificationRule.shouldPostToExistingChat) {
|
|
2120
|
+
continue;
|
|
2121
|
+
}
|
|
2122
|
+
|
|
2123
|
+
for (const chatId of notificationRule.existingChatIds || []) {
|
|
2124
|
+
if (!chatId) {
|
|
2125
|
+
continue;
|
|
2126
|
+
}
|
|
2127
|
+
|
|
2128
|
+
if (!chatIds.includes(chatId)) {
|
|
2129
|
+
chatIds.push(chatId);
|
|
2130
|
+
}
|
|
2131
|
+
}
|
|
2132
|
+
}
|
|
2133
|
+
|
|
2134
|
+
return chatIds;
|
|
2135
|
+
}
|
|
2136
|
+
|
|
2137
|
+
@CaptureSpan()
|
|
2138
|
+
public async getExistingChatIdsBasedOnEventType(data: {
|
|
2139
|
+
projectId: ObjectID;
|
|
2140
|
+
workspaceType: WorkspaceType;
|
|
2141
|
+
notificationRuleEventType: NotificationRuleEventType;
|
|
2142
|
+
notificationFor: NotificationFor;
|
|
2143
|
+
}): Promise<Array<string>> {
|
|
2144
|
+
if (data.workspaceType !== WorkspaceType.MicrosoftTeams) {
|
|
2145
|
+
return []; // chats are only supported for Microsoft Teams.
|
|
2146
|
+
}
|
|
2147
|
+
|
|
2148
|
+
const notificationRules: Array<WorkspaceNotificationRule> =
|
|
2149
|
+
await this.getMatchingNotificationRules({
|
|
2150
|
+
projectId: data.projectId,
|
|
2151
|
+
workspaceType: data.workspaceType,
|
|
2152
|
+
notificationRuleEventType: data.notificationRuleEventType,
|
|
2153
|
+
notificationFor: data.notificationFor,
|
|
2154
|
+
});
|
|
2155
|
+
|
|
2156
|
+
return this.getExistingChatIdsFromNotificationRules({
|
|
2157
|
+
notificationRules: notificationRules.map(
|
|
2158
|
+
(rule: WorkspaceNotificationRule) => {
|
|
2159
|
+
return rule.notificationRule as BaseNotificationRule;
|
|
2160
|
+
},
|
|
2161
|
+
),
|
|
2162
|
+
});
|
|
2163
|
+
}
|
|
2164
|
+
|
|
2165
|
+
@CaptureSpan()
|
|
2166
|
+
public async getConnectedMicrosoftTeamsChats(data: {
|
|
2167
|
+
projectId: ObjectID;
|
|
2168
|
+
}): Promise<Record<string, MicrosoftTeamsChat>> {
|
|
2169
|
+
const projectAuth: WorkspaceProjectAuthToken | null =
|
|
2170
|
+
await WorkspaceProjectAuthTokenService.getProjectAuth({
|
|
2171
|
+
projectId: data.projectId,
|
|
2172
|
+
workspaceType: WorkspaceType.MicrosoftTeams,
|
|
2173
|
+
});
|
|
2174
|
+
|
|
2175
|
+
if (!projectAuth || !projectAuth.miscData) {
|
|
2176
|
+
return {};
|
|
2177
|
+
}
|
|
2178
|
+
|
|
2179
|
+
return (
|
|
2180
|
+
(projectAuth.miscData as MicrosoftTeamsMiscData).availableChats || {}
|
|
2181
|
+
);
|
|
2182
|
+
}
|
|
2183
|
+
|
|
1954
2184
|
public getnotificationChannelssFromNotificationRules(data: {
|
|
1955
2185
|
notificationEventType: NotificationRuleEventType;
|
|
1956
2186
|
notificationRules: Array<WorkspaceNotificationRule>;
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import BadDataException from "../../../Types/Exception/BadDataException";
|
|
2
|
+
import Exception from "../../../Types/Exception/Exception";
|
|
3
|
+
|
|
4
|
+
/*
|
|
5
|
+
* Postgres error codes we can turn into something the caller can act on.
|
|
6
|
+
* https://www.postgresql.org/docs/current/errcodes-appendix.html
|
|
7
|
+
*/
|
|
8
|
+
const FOREIGN_KEY_VIOLATION: string = "23503";
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* TypeORM reports driver failures as QueryFailedError, which does not extend
|
|
12
|
+
* Exception. Those fall all the way through to the generic handler in
|
|
13
|
+
* StartServer and reach the client as a bare 500 `{"error": "Server Error"}`
|
|
14
|
+
* with nothing to act on — the user just sees "Server Error" in the UI.
|
|
15
|
+
*
|
|
16
|
+
* Translate the failures a user can actually do something about into a
|
|
17
|
+
* BadDataException so the API answers with a 400 and a readable message.
|
|
18
|
+
* Anything we do not recognise is returned untouched, so behaviour for the
|
|
19
|
+
* rest of the error surface is unchanged.
|
|
20
|
+
*/
|
|
21
|
+
|
|
22
|
+
interface PostgresDriverError {
|
|
23
|
+
code?: string | undefined;
|
|
24
|
+
table?: string | undefined;
|
|
25
|
+
detail?: string | undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export default class PostgresErrorTranslator {
|
|
29
|
+
public static translate(error: unknown): unknown {
|
|
30
|
+
const driverError: PostgresDriverError | null =
|
|
31
|
+
this.getPostgresDriverError(error);
|
|
32
|
+
|
|
33
|
+
if (!driverError || driverError.code !== FOREIGN_KEY_VIOLATION) {
|
|
34
|
+
return error;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const detail: string = driverError.detail || "";
|
|
38
|
+
|
|
39
|
+
/*
|
|
40
|
+
* DELETE blocked by a child row:
|
|
41
|
+
* Key (_id)=(...) is still referenced from table "MonitorStatusTimeline".
|
|
42
|
+
*/
|
|
43
|
+
if (detail.includes("is still referenced from table")) {
|
|
44
|
+
const referencingTable: string =
|
|
45
|
+
this.getTableNameFromDetail(detail) || driverError.table || "";
|
|
46
|
+
|
|
47
|
+
return new BadDataException(
|
|
48
|
+
referencingTable
|
|
49
|
+
? `This item cannot be deleted because ${this.humanizeTableName(
|
|
50
|
+
referencingTable,
|
|
51
|
+
)} records still reference it. Please delete those records first, and then try again.`
|
|
52
|
+
: "This item cannot be deleted because other records still reference it. Please delete those records first, and then try again.",
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/*
|
|
57
|
+
* INSERT/UPDATE pointing at a row that does not exist:
|
|
58
|
+
* Key (monitorStatusId)=(...) is not present in table "MonitorStatus".
|
|
59
|
+
*/
|
|
60
|
+
if (detail.includes("is not present in table")) {
|
|
61
|
+
const referencedTable: string = this.getTableNameFromDetail(detail) || "";
|
|
62
|
+
|
|
63
|
+
return new BadDataException(
|
|
64
|
+
referencedTable
|
|
65
|
+
? `This request references ${this.humanizeTableName(
|
|
66
|
+
referencedTable,
|
|
67
|
+
)} that does not exist. Please check the request and try again.`
|
|
68
|
+
: "This request references an item that does not exist. Please check the request and try again.",
|
|
69
|
+
);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
return error;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* `throw`-friendly wrapper. Kept synchronous so callers can keep using
|
|
77
|
+
* `throw translator(...)` on the synchronous throw path.
|
|
78
|
+
*/
|
|
79
|
+
public static translateException(error: Exception): Exception {
|
|
80
|
+
return this.translate(error) as Exception;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private static getPostgresDriverError(
|
|
84
|
+
error: unknown,
|
|
85
|
+
): PostgresDriverError | null {
|
|
86
|
+
if (!error || typeof error !== "object") {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/*
|
|
91
|
+
* QueryFailedError hoists the pg fields onto itself and also keeps the
|
|
92
|
+
* original under `driverError`. Prefer whichever one carries the code.
|
|
93
|
+
*/
|
|
94
|
+
const candidate: PostgresDriverError = error as PostgresDriverError;
|
|
95
|
+
|
|
96
|
+
if (typeof candidate.code === "string") {
|
|
97
|
+
return candidate;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const driverError: unknown = (error as { driverError?: unknown })
|
|
101
|
+
.driverError;
|
|
102
|
+
|
|
103
|
+
if (
|
|
104
|
+
driverError &&
|
|
105
|
+
typeof driverError === "object" &&
|
|
106
|
+
typeof (driverError as PostgresDriverError).code === "string"
|
|
107
|
+
) {
|
|
108
|
+
return driverError as PostgresDriverError;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return null;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
private static getTableNameFromDetail(detail: string): string | null {
|
|
115
|
+
const match: RegExpMatchArray | null = detail.match(/table "([^"]+)"/);
|
|
116
|
+
|
|
117
|
+
return match && match[1] ? match[1] : null;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/*
|
|
121
|
+
* "MonitorStatusTimeline" -> "Monitor Status Timeline". Table names are the
|
|
122
|
+
* PascalCase model names, so this reads well enough to put in front of a
|
|
123
|
+
* user without maintaining a lookup table of display names.
|
|
124
|
+
*/
|
|
125
|
+
private static humanizeTableName(tableName: string): string {
|
|
126
|
+
return tableName
|
|
127
|
+
.replace(/([a-z0-9])([A-Z])/g, "$1 $2")
|
|
128
|
+
.replace(/([A-Z]+)([A-Z][a-z])/g, "$1 $2")
|
|
129
|
+
.trim();
|
|
130
|
+
}
|
|
131
|
+
}
|
|
@@ -27,7 +27,9 @@ import ObjectID from "../../../Types/ObjectID";
|
|
|
27
27
|
import ProbeApiIngestResponse from "../../../Types/Probe/ProbeApiIngestResponse";
|
|
28
28
|
import ProbeMonitorResponse from "../../../Types/Probe/ProbeMonitorResponse";
|
|
29
29
|
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
30
|
-
import MonitorProbe
|
|
30
|
+
import MonitorProbe, {
|
|
31
|
+
MonitorStepProbeResponse,
|
|
32
|
+
} from "../../../Models/DatabaseModels/MonitorProbe";
|
|
31
33
|
import MonitorStatus from "../../../Models/DatabaseModels/MonitorStatus";
|
|
32
34
|
import MonitorStatusTimeline from "../../../Models/DatabaseModels/MonitorStatusTimeline";
|
|
33
35
|
import OneUptimeDate from "../../../Types/Date";
|
|
@@ -241,6 +243,16 @@ export default class MonitorResourceUtil {
|
|
|
241
243
|
let probeName: string | undefined = undefined;
|
|
242
244
|
const monitorName: string | undefined = monitor.name || undefined;
|
|
243
245
|
|
|
246
|
+
/*
|
|
247
|
+
* All MonitorProbe rows for this monitor, fetched once per result and
|
|
248
|
+
* reused by checkProbeAgreement below. The rows carry the heavy
|
|
249
|
+
* lastMonitoringLog jsonb, so re-fetching them per result was the
|
|
250
|
+
* second-largest read on the hottest Postgres path. Safe to reuse: the
|
|
251
|
+
* per-monitor semaphore above serializes every writer of these rows
|
|
252
|
+
* for the duration of this function.
|
|
253
|
+
*/
|
|
254
|
+
let monitorProbesForMonitor: Array<MonitorProbe> | null = null;
|
|
255
|
+
|
|
244
256
|
/*
|
|
245
257
|
* SNMP trap responses are event-driven, not check results. They are
|
|
246
258
|
* evaluated ONLY against trap criteria; they must not overwrite the
|
|
@@ -266,21 +278,39 @@ export default class MonitorResourceUtil {
|
|
|
266
278
|
) {
|
|
267
279
|
dataToProcess = dataToProcess as ProbeMonitorResponse;
|
|
268
280
|
if ((dataToProcess as ProbeMonitorResponse).probeId) {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
281
|
+
/*
|
|
282
|
+
* One query for every probe assigned to this monitor (instead of
|
|
283
|
+
* one for the current probe here plus another for all of them in
|
|
284
|
+
* checkProbeAgreement). The select is the union both consumers
|
|
285
|
+
* need.
|
|
286
|
+
*/
|
|
287
|
+
monitorProbesForMonitor = await MonitorProbeService.findBy({
|
|
288
|
+
query: {
|
|
289
|
+
monitorId: monitor.id!,
|
|
290
|
+
},
|
|
291
|
+
select: {
|
|
292
|
+
_id: true,
|
|
293
|
+
probeId: true,
|
|
294
|
+
isEnabled: true,
|
|
295
|
+
lastMonitoringLog: true,
|
|
296
|
+
probe: {
|
|
297
|
+
name: true,
|
|
298
|
+
connectionStatus: true,
|
|
283
299
|
},
|
|
300
|
+
},
|
|
301
|
+
limit: LIMIT_PER_PROJECT,
|
|
302
|
+
skip: 0,
|
|
303
|
+
props: {
|
|
304
|
+
isRoot: true,
|
|
305
|
+
},
|
|
306
|
+
});
|
|
307
|
+
|
|
308
|
+
const monitorProbe: MonitorProbe | undefined =
|
|
309
|
+
monitorProbesForMonitor.find((mp: MonitorProbe) => {
|
|
310
|
+
return (
|
|
311
|
+
mp.probeId?.toString() ===
|
|
312
|
+
(dataToProcess as ProbeMonitorResponse).probeId!.toString()
|
|
313
|
+
);
|
|
284
314
|
});
|
|
285
315
|
|
|
286
316
|
if (!monitorProbe) {
|
|
@@ -297,26 +327,39 @@ export default class MonitorResourceUtil {
|
|
|
297
327
|
*/
|
|
298
328
|
|
|
299
329
|
if (!isSnmpTrapEvent) {
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
330
|
+
/*
|
|
331
|
+
* Runs once per probe check result — the hottest recurring write
|
|
332
|
+
* in the product. The full updateOneBy pipeline would re-SELECT
|
|
333
|
+
* this row (including the large lastMonitoringLog jsonb we just
|
|
334
|
+
* fetched above) and then save() it (another SELECT + UPDATE in a
|
|
335
|
+
* transaction). MonitorProbe has no workflow/audit/realtime
|
|
336
|
+
* decorators, so those hooks were inert anyway — a single
|
|
337
|
+
* UPDATE by the id we already hold is equivalent and 3x cheaper.
|
|
338
|
+
* See the Monitor heartbeat writes below for the same pattern.
|
|
339
|
+
*/
|
|
340
|
+
const updatedLastMonitoringLog: MonitorStepProbeResponse = {
|
|
341
|
+
...(monitorProbe.lastMonitoringLog || {}),
|
|
342
|
+
[(
|
|
343
|
+
dataToProcess as ProbeMonitorResponse
|
|
344
|
+
).monitorStepId.toString()]: {
|
|
345
|
+
...JSON.parse(JSON.stringify(dataToProcess)),
|
|
346
|
+
monitoredAt: OneUptimeDate.getCurrentDate(),
|
|
304
347
|
},
|
|
348
|
+
};
|
|
349
|
+
|
|
350
|
+
await MonitorProbeService.updateColumnsByIdWithoutHooks({
|
|
351
|
+
id: monitorProbe.id!,
|
|
305
352
|
data: {
|
|
306
|
-
lastMonitoringLog:
|
|
307
|
-
...(monitorProbe.lastMonitoringLog || {}),
|
|
308
|
-
[(
|
|
309
|
-
dataToProcess as ProbeMonitorResponse
|
|
310
|
-
).monitorStepId.toString()]: {
|
|
311
|
-
...JSON.parse(JSON.stringify(dataToProcess)),
|
|
312
|
-
monitoredAt: OneUptimeDate.getCurrentDate(),
|
|
313
|
-
},
|
|
314
|
-
} as any,
|
|
315
|
-
},
|
|
316
|
-
props: {
|
|
317
|
-
isRoot: true,
|
|
353
|
+
lastMonitoringLog: updatedLastMonitoringLog as any,
|
|
318
354
|
},
|
|
319
355
|
});
|
|
356
|
+
|
|
357
|
+
/*
|
|
358
|
+
* Mirror the write onto the in-memory row so checkProbeAgreement
|
|
359
|
+
* (which reuses monitorProbesForMonitor instead of re-querying)
|
|
360
|
+
* sees exactly what a fresh read would return.
|
|
361
|
+
*/
|
|
362
|
+
monitorProbe.lastMonitoringLog = updatedLastMonitoringLog;
|
|
320
363
|
}
|
|
321
364
|
}
|
|
322
365
|
}
|
|
@@ -639,6 +682,7 @@ export default class MonitorResourceUtil {
|
|
|
639
682
|
monitorStep: monitorStep,
|
|
640
683
|
currentCriteriaMetId: response.criteriaMetId || null,
|
|
641
684
|
currentRootCause: response.rootCause || null,
|
|
685
|
+
monitorProbes: monitorProbesForMonitor || undefined,
|
|
642
686
|
});
|
|
643
687
|
|
|
644
688
|
// Add probe agreement event to evaluation summary
|
|
@@ -1084,6 +1128,15 @@ export default class MonitorResourceUtil {
|
|
|
1084
1128
|
monitorStep: MonitorStep;
|
|
1085
1129
|
currentCriteriaMetId: string | null;
|
|
1086
1130
|
currentRootCause: string | null;
|
|
1131
|
+
/*
|
|
1132
|
+
* Pre-fetched MonitorProbe rows for this monitor (probeId, isEnabled,
|
|
1133
|
+
* lastMonitoringLog, probe.name/connectionStatus). The probe-result hot
|
|
1134
|
+
* path passes these to avoid re-reading every row's lastMonitoringLog
|
|
1135
|
+
* jsonb per result; safe because the caller holds the per-monitor lock,
|
|
1136
|
+
* so the rows cannot change underneath us. When absent, fall back to
|
|
1137
|
+
* querying.
|
|
1138
|
+
*/
|
|
1139
|
+
monitorProbes?: Array<MonitorProbe> | undefined;
|
|
1087
1140
|
}): Promise<ProbeAgreementResult> {
|
|
1088
1141
|
const { monitor, monitorStep, currentCriteriaMetId, currentRootCause } =
|
|
1089
1142
|
input;
|
|
@@ -1092,8 +1145,9 @@ export default class MonitorResourceUtil {
|
|
|
1092
1145
|
* If minimumProbeAgreement is not set, all probes must agree
|
|
1093
1146
|
* Get all MonitorProbes for this monitor with their probe connection status
|
|
1094
1147
|
*/
|
|
1095
|
-
const monitorProbes: Array<MonitorProbe> =
|
|
1096
|
-
|
|
1148
|
+
const monitorProbes: Array<MonitorProbe> =
|
|
1149
|
+
input.monitorProbes ||
|
|
1150
|
+
(await MonitorProbeService.findBy({
|
|
1097
1151
|
query: {
|
|
1098
1152
|
monitorId: monitor.id!,
|
|
1099
1153
|
},
|
|
@@ -1111,8 +1165,7 @@ export default class MonitorResourceUtil {
|
|
|
1111
1165
|
props: {
|
|
1112
1166
|
isRoot: true,
|
|
1113
1167
|
},
|
|
1114
|
-
}
|
|
1115
|
-
);
|
|
1168
|
+
}));
|
|
1116
1169
|
|
|
1117
1170
|
// Filter to only active probes (enabled AND connected)
|
|
1118
1171
|
const activeProbes: Array<MonitorProbe> = monitorProbes.filter(
|
|
@@ -27,6 +27,26 @@ export default class MonitorStatusTimelineUtil {
|
|
|
27
27
|
}): Promise<MonitorStatusTimeline | null> {
|
|
28
28
|
// criteria filters are met, now process the actions.
|
|
29
29
|
|
|
30
|
+
/*
|
|
31
|
+
* Steady-state fast-path: the criteria's target status is already the
|
|
32
|
+
* monitor's current status (the standard "online criteria matched on a
|
|
33
|
+
* healthy check" case, i.e. almost every probe result). The sorted
|
|
34
|
+
* timeline SELECT below could only confirm what currentMonitorStatusId
|
|
35
|
+
* already tells us — Monitor.currentMonitorStatusId is kept in lockstep
|
|
36
|
+
* with the latest timeline row by MonitorStatusTimelineService — and the
|
|
37
|
+
* function would return null via the same-as-last-status check anyway.
|
|
38
|
+
* Skip the query entirely. MonitorStatusTimelineService.onBeforeCreate
|
|
39
|
+
* still dedupes as the concurrency backstop.
|
|
40
|
+
*/
|
|
41
|
+
if (
|
|
42
|
+
input.criteriaInstance.data?.changeMonitorStatus &&
|
|
43
|
+
input.criteriaInstance.data?.monitorStatusId &&
|
|
44
|
+
input.criteriaInstance.data.monitorStatusId.toString() ===
|
|
45
|
+
input.monitor.currentMonitorStatusId?.toString()
|
|
46
|
+
) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
|
|
30
50
|
const lastMonitorStatusTimeline: MonitorStatusTimeline | null =
|
|
31
51
|
await MonitorStatusTimelineService.findOneBy({
|
|
32
52
|
query: {
|
|
@@ -56,7 +76,7 @@ export default class MonitorStatusTimelineUtil {
|
|
|
56
76
|
input.criteriaInstance.data?.changeMonitorStatus &&
|
|
57
77
|
input.criteriaInstance.data?.monitorStatusId &&
|
|
58
78
|
input.criteriaInstance.data?.monitorStatusId.toString() !==
|
|
59
|
-
lastMonitorStatusTimeline?.
|
|
79
|
+
lastMonitorStatusTimeline?.monitorStatusId?.toString()
|
|
60
80
|
) {
|
|
61
81
|
// if monitor status is changed, then create a new status timeline.
|
|
62
82
|
shouldUpdateStatus = true;
|