@oneuptime/common 11.7.1 → 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/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/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/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/ModelForm.tsx +41 -18
- 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/Utils/Project.ts +214 -18
- package/UI/Utils/TableColumnsToCsv.ts +86 -6
- 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/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/ModelForm.js +37 -17
- package/build/dist/UI/Components/Forms/ModelForm.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/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/Utils/Monitor/MonitorProbeSelectionUtil.js +52 -0
- package/build/dist/Utils/Monitor/MonitorProbeSelectionUtil.js.map +1 -0
- package/package.json +4 -3
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
import { MigrationInterface, QueryRunner } from "typeorm";
|
|
2
|
+
|
|
3
|
+
/*
|
|
4
|
+
* Monitors can end up holding references to another project's MonitorStatus,
|
|
5
|
+
* IncidentSeverity or AlertSeverity — nothing validated that the ids embedded
|
|
6
|
+
* in `monitorSteps` belong to the monitor's own project, so a status id picked
|
|
7
|
+
* up from a second project got persisted and then copied onto every
|
|
8
|
+
* MonitorStatusTimeline row the monitor wrote.
|
|
9
|
+
*
|
|
10
|
+
* That makes the *referenced* project undeletable: deleting a Project cascades
|
|
11
|
+
* into its MonitorStatus rows, but MonitorStatusTimeline rows owned by a
|
|
12
|
+
* different project still point at them and the FK is ON DELETE NO ACTION, so
|
|
13
|
+
* Postgres raises 23503 and the API answers "Server Error".
|
|
14
|
+
*
|
|
15
|
+
* This migration:
|
|
16
|
+
* 1. repoints every cross-project reference at the equivalent record in the
|
|
17
|
+
* row's own project (matched by name, then by operational/offline role,
|
|
18
|
+
* then by nearest priority), and
|
|
19
|
+
* 2. switches the two status-timeline FKs to ON DELETE CASCADE, which is
|
|
20
|
+
* what they should always have been — a status timeline row is
|
|
21
|
+
* meaningless once its status is gone.
|
|
22
|
+
*
|
|
23
|
+
* Step 1 runs before step 2 on purpose: repairing first means the timeline
|
|
24
|
+
* history stays attached to the surviving project instead of being swept up by
|
|
25
|
+
* the new cascade.
|
|
26
|
+
*/
|
|
27
|
+
export class RepairCrossProjectMonitorStatusReferences1785240000000
|
|
28
|
+
implements MigrationInterface
|
|
29
|
+
{
|
|
30
|
+
public name: string =
|
|
31
|
+
"RepairCrossProjectMonitorStatusReferences1785240000000";
|
|
32
|
+
|
|
33
|
+
public async up(queryRunner: QueryRunner): Promise<void> {
|
|
34
|
+
/*
|
|
35
|
+
* The repair below scans MonitorStatusTimeline, which is in the millions of
|
|
36
|
+
* rows. Measured at ~4.5s on a production-sized table, but the connection
|
|
37
|
+
* carries the app's 30s statement_timeout (DATABASE_STATEMENT_TIMEOUT_MS)
|
|
38
|
+
* and a loaded database should not turn this into a failed deploy.
|
|
39
|
+
*/
|
|
40
|
+
await queryRunner.query(`SET LOCAL statement_timeout = '600s'`);
|
|
41
|
+
|
|
42
|
+
// 1. MonitorStatusTimeline rows pointing at another project's status.
|
|
43
|
+
await queryRunner.query(`
|
|
44
|
+
WITH bad AS (
|
|
45
|
+
SELECT t."_id" AS row_id,
|
|
46
|
+
t."projectId" AS project_id,
|
|
47
|
+
ms."name" AS status_name,
|
|
48
|
+
ms."isOperationalState" AS is_operational,
|
|
49
|
+
ms."isOfflineState" AS is_offline,
|
|
50
|
+
ms."priority" AS priority
|
|
51
|
+
FROM "MonitorStatusTimeline" t
|
|
52
|
+
JOIN "MonitorStatus" ms ON ms."_id" = t."monitorStatusId"
|
|
53
|
+
WHERE t."projectId" IS DISTINCT FROM ms."projectId"
|
|
54
|
+
), resolved AS (
|
|
55
|
+
SELECT b.row_id, COALESCE(
|
|
56
|
+
(SELECT s."_id" FROM "MonitorStatus" s
|
|
57
|
+
WHERE s."projectId" = b.project_id AND lower(s."name") = lower(b.status_name)
|
|
58
|
+
ORDER BY s."priority" LIMIT 1),
|
|
59
|
+
(SELECT s."_id" FROM "MonitorStatus" s
|
|
60
|
+
WHERE s."projectId" = b.project_id
|
|
61
|
+
AND s."isOperationalState" = b.is_operational
|
|
62
|
+
AND s."isOfflineState" = b.is_offline
|
|
63
|
+
ORDER BY s."priority" LIMIT 1),
|
|
64
|
+
(SELECT s."_id" FROM "MonitorStatus" s
|
|
65
|
+
WHERE s."projectId" = b.project_id
|
|
66
|
+
ORDER BY abs(s."priority" - b.priority), s."priority" LIMIT 1)
|
|
67
|
+
) AS good_status_id
|
|
68
|
+
FROM bad b
|
|
69
|
+
)
|
|
70
|
+
UPDATE "MonitorStatusTimeline" t
|
|
71
|
+
SET "monitorStatusId" = r.good_status_id
|
|
72
|
+
FROM resolved r
|
|
73
|
+
WHERE t."_id" = r.row_id AND r.good_status_id IS NOT NULL
|
|
74
|
+
`);
|
|
75
|
+
|
|
76
|
+
// 2. NetworkSiteStatusTimeline rows pointing at another project's status.
|
|
77
|
+
await queryRunner.query(`
|
|
78
|
+
WITH bad AS (
|
|
79
|
+
SELECT t."_id" AS row_id,
|
|
80
|
+
t."projectId" AS project_id,
|
|
81
|
+
ms."name" AS status_name,
|
|
82
|
+
ms."isOperationalState" AS is_operational,
|
|
83
|
+
ms."isOfflineState" AS is_offline,
|
|
84
|
+
ms."priority" AS priority
|
|
85
|
+
FROM "NetworkSiteStatusTimeline" t
|
|
86
|
+
JOIN "MonitorStatus" ms ON ms."_id" = t."monitorStatusId"
|
|
87
|
+
WHERE t."projectId" IS DISTINCT FROM ms."projectId"
|
|
88
|
+
), resolved AS (
|
|
89
|
+
SELECT b.row_id, COALESCE(
|
|
90
|
+
(SELECT s."_id" FROM "MonitorStatus" s
|
|
91
|
+
WHERE s."projectId" = b.project_id AND lower(s."name") = lower(b.status_name)
|
|
92
|
+
ORDER BY s."priority" LIMIT 1),
|
|
93
|
+
(SELECT s."_id" FROM "MonitorStatus" s
|
|
94
|
+
WHERE s."projectId" = b.project_id
|
|
95
|
+
AND s."isOperationalState" = b.is_operational
|
|
96
|
+
AND s."isOfflineState" = b.is_offline
|
|
97
|
+
ORDER BY s."priority" LIMIT 1),
|
|
98
|
+
(SELECT s."_id" FROM "MonitorStatus" s
|
|
99
|
+
WHERE s."projectId" = b.project_id
|
|
100
|
+
ORDER BY abs(s."priority" - b.priority), s."priority" LIMIT 1)
|
|
101
|
+
) AS good_status_id
|
|
102
|
+
FROM bad b
|
|
103
|
+
)
|
|
104
|
+
UPDATE "NetworkSiteStatusTimeline" t
|
|
105
|
+
SET "monitorStatusId" = r.good_status_id
|
|
106
|
+
FROM resolved r
|
|
107
|
+
WHERE t."_id" = r.row_id AND r.good_status_id IS NOT NULL
|
|
108
|
+
`);
|
|
109
|
+
|
|
110
|
+
/*
|
|
111
|
+
* 3. Monitor.currentMonitorStatusId pointing at another project's status.
|
|
112
|
+
* This FK stays NO ACTION — deleting a status that monitors are currently
|
|
113
|
+
* in should be blocked — so a cross-project value here blocks deletion the
|
|
114
|
+
* same way. The column is NOT NULL, hence a repoint rather than a null.
|
|
115
|
+
*/
|
|
116
|
+
await queryRunner.query(`
|
|
117
|
+
WITH bad AS (
|
|
118
|
+
SELECT m."_id" AS row_id,
|
|
119
|
+
m."projectId" AS project_id,
|
|
120
|
+
ms."name" AS status_name,
|
|
121
|
+
ms."isOperationalState" AS is_operational,
|
|
122
|
+
ms."isOfflineState" AS is_offline,
|
|
123
|
+
ms."priority" AS priority
|
|
124
|
+
FROM "Monitor" m
|
|
125
|
+
JOIN "MonitorStatus" ms ON ms."_id" = m."currentMonitorStatusId"
|
|
126
|
+
WHERE m."projectId" IS DISTINCT FROM ms."projectId"
|
|
127
|
+
), resolved AS (
|
|
128
|
+
SELECT b.row_id, COALESCE(
|
|
129
|
+
(SELECT s."_id" FROM "MonitorStatus" s
|
|
130
|
+
WHERE s."projectId" = b.project_id AND lower(s."name") = lower(b.status_name)
|
|
131
|
+
ORDER BY s."priority" LIMIT 1),
|
|
132
|
+
(SELECT s."_id" FROM "MonitorStatus" s
|
|
133
|
+
WHERE s."projectId" = b.project_id
|
|
134
|
+
AND s."isOperationalState" = b.is_operational
|
|
135
|
+
AND s."isOfflineState" = b.is_offline
|
|
136
|
+
ORDER BY s."priority" LIMIT 1),
|
|
137
|
+
(SELECT s."_id" FROM "MonitorStatus" s
|
|
138
|
+
WHERE s."projectId" = b.project_id
|
|
139
|
+
ORDER BY abs(s."priority" - b.priority), s."priority" LIMIT 1)
|
|
140
|
+
) AS good_status_id
|
|
141
|
+
FROM bad b
|
|
142
|
+
)
|
|
143
|
+
UPDATE "Monitor" m
|
|
144
|
+
SET "currentMonitorStatusId" = r.good_status_id
|
|
145
|
+
FROM resolved r
|
|
146
|
+
WHERE m."_id" = r.row_id AND r.good_status_id IS NOT NULL
|
|
147
|
+
`);
|
|
148
|
+
|
|
149
|
+
/*
|
|
150
|
+
* 4. Cross-project ids embedded in Monitor.monitorSteps. These carry no FK,
|
|
151
|
+
* so they would silently dangle once the other project is deleted (the
|
|
152
|
+
* monitor's default status, and the severity used to open incidents/alerts,
|
|
153
|
+
* would both point at nothing). Rewrite them onto the monitor's own
|
|
154
|
+
* project's equivalents.
|
|
155
|
+
*/
|
|
156
|
+
await queryRunner.query(`
|
|
157
|
+
DO $$
|
|
158
|
+
DECLARE
|
|
159
|
+
monitor_row record;
|
|
160
|
+
remap_row record;
|
|
161
|
+
steps_text text;
|
|
162
|
+
BEGIN
|
|
163
|
+
CREATE TEMP TABLE _cross_project_step_refs ON COMMIT DROP AS
|
|
164
|
+
SELECT m."_id" AS monitor_id,
|
|
165
|
+
m."projectId" AS project_id,
|
|
166
|
+
matched.arr[1]::uuid AS ref_id
|
|
167
|
+
FROM "Monitor" m
|
|
168
|
+
CROSS JOIN LATERAL regexp_matches(
|
|
169
|
+
m."monitorSteps"::text,
|
|
170
|
+
'[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}',
|
|
171
|
+
'g'
|
|
172
|
+
) AS matched(arr)
|
|
173
|
+
WHERE m."monitorSteps" IS NOT NULL;
|
|
174
|
+
|
|
175
|
+
CREATE TEMP TABLE _cross_project_step_remap ON COMMIT DROP AS
|
|
176
|
+
SELECT DISTINCT sr.monitor_id, sr.ref_id AS bad_id, COALESCE(
|
|
177
|
+
(SELECT x."_id" FROM "MonitorStatus" x
|
|
178
|
+
WHERE x."projectId" = sr.project_id AND lower(x."name") = lower(ms."name")
|
|
179
|
+
ORDER BY x."priority" LIMIT 1),
|
|
180
|
+
(SELECT x."_id" FROM "MonitorStatus" x
|
|
181
|
+
WHERE x."projectId" = sr.project_id
|
|
182
|
+
AND x."isOperationalState" = ms."isOperationalState"
|
|
183
|
+
AND x."isOfflineState" = ms."isOfflineState"
|
|
184
|
+
ORDER BY x."priority" LIMIT 1)
|
|
185
|
+
) AS good_id
|
|
186
|
+
FROM _cross_project_step_refs sr
|
|
187
|
+
JOIN "MonitorStatus" ms ON ms."_id" = sr.ref_id
|
|
188
|
+
WHERE ms."projectId" IS DISTINCT FROM sr.project_id
|
|
189
|
+
|
|
190
|
+
UNION ALL
|
|
191
|
+
|
|
192
|
+
SELECT DISTINCT sr.monitor_id, sr.ref_id, (
|
|
193
|
+
SELECT x."_id" FROM "IncidentSeverity" x
|
|
194
|
+
WHERE x."projectId" = sr.project_id AND lower(x."name") = lower(sev."name")
|
|
195
|
+
ORDER BY x."order" LIMIT 1
|
|
196
|
+
)
|
|
197
|
+
FROM _cross_project_step_refs sr
|
|
198
|
+
JOIN "IncidentSeverity" sev ON sev."_id" = sr.ref_id
|
|
199
|
+
WHERE sev."projectId" IS DISTINCT FROM sr.project_id
|
|
200
|
+
|
|
201
|
+
UNION ALL
|
|
202
|
+
|
|
203
|
+
SELECT DISTINCT sr.monitor_id, sr.ref_id, (
|
|
204
|
+
SELECT x."_id" FROM "AlertSeverity" x
|
|
205
|
+
WHERE x."projectId" = sr.project_id AND lower(x."name") = lower(sev."name")
|
|
206
|
+
ORDER BY x."order" LIMIT 1
|
|
207
|
+
)
|
|
208
|
+
FROM _cross_project_step_refs sr
|
|
209
|
+
JOIN "AlertSeverity" sev ON sev."_id" = sr.ref_id
|
|
210
|
+
WHERE sev."projectId" IS DISTINCT FROM sr.project_id;
|
|
211
|
+
|
|
212
|
+
FOR monitor_row IN
|
|
213
|
+
SELECT DISTINCT monitor_id FROM _cross_project_step_remap WHERE good_id IS NOT NULL
|
|
214
|
+
LOOP
|
|
215
|
+
SELECT "monitorSteps"::text INTO steps_text
|
|
216
|
+
FROM "Monitor" WHERE "_id" = monitor_row.monitor_id;
|
|
217
|
+
|
|
218
|
+
FOR remap_row IN
|
|
219
|
+
SELECT bad_id, good_id FROM _cross_project_step_remap
|
|
220
|
+
WHERE monitor_id = monitor_row.monitor_id AND good_id IS NOT NULL
|
|
221
|
+
LOOP
|
|
222
|
+
steps_text := replace(steps_text, remap_row.bad_id::text, remap_row.good_id::text);
|
|
223
|
+
END LOOP;
|
|
224
|
+
|
|
225
|
+
UPDATE "Monitor" SET "monitorSteps" = steps_text::jsonb
|
|
226
|
+
WHERE "_id" = monitor_row.monitor_id;
|
|
227
|
+
END LOOP;
|
|
228
|
+
END $$;
|
|
229
|
+
`);
|
|
230
|
+
|
|
231
|
+
/*
|
|
232
|
+
* 5. A status timeline row cannot outlive its status, so cascade instead of
|
|
233
|
+
* blocking. Without this, a leftover cross-project row (one we could not
|
|
234
|
+
* resolve above, e.g. a project with no statuses of its own) would still
|
|
235
|
+
* make the referenced project undeletable.
|
|
236
|
+
*
|
|
237
|
+
* Swapping a foreign key needs ACCESS EXCLUSIVE on MonitorStatusTimeline —
|
|
238
|
+
* the busiest write table there is — and the lock is held until this
|
|
239
|
+
* migration commits. The DDL itself measures ~1.3s, but the server has no
|
|
240
|
+
* lock_timeout, so without the line below a single slow transaction holding
|
|
241
|
+
* the table would make this statement queue *and* park every reader and
|
|
242
|
+
* writer behind it until the 30s statement_timeout fired. Failing fast and
|
|
243
|
+
* retrying the deploy is much cheaper than stalling the table.
|
|
244
|
+
*/
|
|
245
|
+
await queryRunner.query(`SET LOCAL lock_timeout = '5s'`);
|
|
246
|
+
|
|
247
|
+
await queryRunner.query(
|
|
248
|
+
`ALTER TABLE "MonitorStatusTimeline" DROP CONSTRAINT "FK_574feb4161c5216c2c7ee0faaf8"`,
|
|
249
|
+
);
|
|
250
|
+
await queryRunner.query(
|
|
251
|
+
`ALTER TABLE "MonitorStatusTimeline" ADD CONSTRAINT "FK_574feb4161c5216c2c7ee0faaf8" FOREIGN KEY ("monitorStatusId") REFERENCES "MonitorStatus"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
|
252
|
+
);
|
|
253
|
+
await queryRunner.query(
|
|
254
|
+
`ALTER TABLE "NetworkSiteStatusTimeline" DROP CONSTRAINT "FK_8886e21e28e5287abb5b26475a1"`,
|
|
255
|
+
);
|
|
256
|
+
await queryRunner.query(
|
|
257
|
+
`ALTER TABLE "NetworkSiteStatusTimeline" ADD CONSTRAINT "FK_8886e21e28e5287abb5b26475a1" FOREIGN KEY ("monitorStatusId") REFERENCES "MonitorStatus"("_id") ON DELETE CASCADE ON UPDATE NO ACTION`,
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
/*
|
|
262
|
+
* Only the constraints are reversible. The repaired references are not:
|
|
263
|
+
* the previous values pointed at another project's records and there is
|
|
264
|
+
* nothing worth restoring them to.
|
|
265
|
+
*/
|
|
266
|
+
public async down(queryRunner: QueryRunner): Promise<void> {
|
|
267
|
+
await queryRunner.query(
|
|
268
|
+
`ALTER TABLE "NetworkSiteStatusTimeline" DROP CONSTRAINT "FK_8886e21e28e5287abb5b26475a1"`,
|
|
269
|
+
);
|
|
270
|
+
await queryRunner.query(
|
|
271
|
+
`ALTER TABLE "NetworkSiteStatusTimeline" ADD CONSTRAINT "FK_8886e21e28e5287abb5b26475a1" FOREIGN KEY ("monitorStatusId") REFERENCES "MonitorStatus"("_id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
|
272
|
+
);
|
|
273
|
+
await queryRunner.query(
|
|
274
|
+
`ALTER TABLE "MonitorStatusTimeline" DROP CONSTRAINT "FK_574feb4161c5216c2c7ee0faaf8"`,
|
|
275
|
+
);
|
|
276
|
+
await queryRunner.query(
|
|
277
|
+
`ALTER TABLE "MonitorStatusTimeline" ADD CONSTRAINT "FK_574feb4161c5216c2c7ee0faaf8" FOREIGN KEY ("monitorStatusId") REFERENCES "MonitorStatus"("_id") ON DELETE NO ACTION ON UPDATE NO ACTION`,
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
@@ -470,6 +470,9 @@ import { AddNetworkDevicePollingColumns1784970388777 } from "./1784970388777-Add
|
|
|
470
470
|
import { AddNetworkSiteTypeTable1784986826214 } from "./1784986826214-AddNetworkSiteTypeTable";
|
|
471
471
|
import { AddServiceLevelObjective1784987015619 } from "./1784987015619-AddServiceLevelObjective";
|
|
472
472
|
import { MigrationName1785066759532 } from "./1785066759532-MigrationName";
|
|
473
|
+
import { AddHotQueryIndexes1785140242697 } from "./1785140242697-AddHotQueryIndexes";
|
|
474
|
+
import { AddHotQueryIndexesSecondPass1785148065137 } from "./1785148065137-AddHotQueryIndexesSecondPass";
|
|
475
|
+
import { RepairCrossProjectMonitorStatusReferences1785240000000 } from "./1785240000000-RepairCrossProjectMonitorStatusReferences";
|
|
473
476
|
|
|
474
477
|
export default [
|
|
475
478
|
InitialMigration,
|
|
@@ -944,4 +947,7 @@ export default [
|
|
|
944
947
|
AddNetworkSiteTypeTable1784986826214,
|
|
945
948
|
AddServiceLevelObjective1784987015619,
|
|
946
949
|
MigrationName1785066759532,
|
|
950
|
+
AddHotQueryIndexes1785140242697,
|
|
951
|
+
AddHotQueryIndexesSecondPass1785148065137,
|
|
952
|
+
RepairCrossProjectMonitorStatusReferences1785240000000,
|
|
947
953
|
];
|
|
@@ -30,6 +30,7 @@ import UpdateByID from "../Types/Database/UpdateByID";
|
|
|
30
30
|
import UpdateByIDAndFetch from "../Types/Database/UpdateByIDAndFetch";
|
|
31
31
|
import UpdateOneBy from "../Types/Database/UpdateOneBy";
|
|
32
32
|
import Encryption from "../Utils/Encryption";
|
|
33
|
+
import PostgresErrorTranslator from "../Utils/Database/PostgresErrorTranslator";
|
|
33
34
|
import logger, { LogAttributes } from "../Utils/Logger";
|
|
34
35
|
import BaseService from "./BaseService";
|
|
35
36
|
import BaseModel from "../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
@@ -453,7 +454,7 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
453
454
|
* propagates the original exception on the synchronous throw path.
|
|
454
455
|
*/
|
|
455
456
|
protected getException(error: Exception): never {
|
|
456
|
-
throw error;
|
|
457
|
+
throw PostgresErrorTranslator.translateException(error);
|
|
457
458
|
}
|
|
458
459
|
|
|
459
460
|
private generateSlug(createBy: CreateBy<TBaseModel>): CreateBy<TBaseModel> {
|
|
@@ -2015,10 +2016,17 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2015
2016
|
return await this._updateBy(updateBy);
|
|
2016
2017
|
}
|
|
2017
2018
|
|
|
2019
|
+
/*
|
|
2020
|
+
* Returns how many rows the update actually matched. Callers that need to
|
|
2021
|
+
* tell "saved" apart from "matched nothing" must check it: the permission
|
|
2022
|
+
* layer narrows the query after the caller builds it (tenant scope, access
|
|
2023
|
+
* control labels, owned scope), so an update can legitimately reach here
|
|
2024
|
+
* and write nothing at all.
|
|
2025
|
+
*/
|
|
2018
2026
|
@CaptureSpan()
|
|
2019
2027
|
public async updateOneById(
|
|
2020
2028
|
updateById: UpdateByID<TBaseModel>,
|
|
2021
|
-
): Promise<
|
|
2029
|
+
): Promise<number> {
|
|
2022
2030
|
if (!updateById.id) {
|
|
2023
2031
|
throw new BadDataException("updateById.id is required");
|
|
2024
2032
|
}
|
|
@@ -2048,7 +2056,7 @@ class DatabaseService<TBaseModel extends BaseModel> extends BaseService {
|
|
|
2048
2056
|
props: updateById.props,
|
|
2049
2057
|
});
|
|
2050
2058
|
|
|
2051
|
-
await this.updateOneBy({
|
|
2059
|
+
return await this.updateOneBy({
|
|
2052
2060
|
query: {
|
|
2053
2061
|
_id: updateById.id.toString() as any,
|
|
2054
2062
|
},
|
|
@@ -4,6 +4,7 @@ import MonitorGroupResourceService from "./MonitorGroupResourceService";
|
|
|
4
4
|
import MonitorStatusService from "./MonitorStatusService";
|
|
5
5
|
import MonitorStatusTimelineService from "./MonitorStatusTimelineService";
|
|
6
6
|
import DatabaseCommonInteractionProps from "../../Types/BaseDatabase/DatabaseCommonInteractionProps";
|
|
7
|
+
import Dictionary from "../../Types/Dictionary";
|
|
7
8
|
import SortOrder from "../../Types/BaseDatabase/SortOrder";
|
|
8
9
|
import LIMIT_MAX, { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
9
10
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
@@ -221,5 +222,149 @@ export class Service extends DatabaseService<MonitorGroup> {
|
|
|
221
222
|
|
|
222
223
|
return currentStatus;
|
|
223
224
|
}
|
|
225
|
+
|
|
226
|
+
/*
|
|
227
|
+
* Batched fetch of MonitorGroupResource rows for many groups in ONE query
|
|
228
|
+
* (paged only if the total exceeds LIMIT_MAX). The public status-page read
|
|
229
|
+
* paths used to issue one query per monitor group per page view; on a page
|
|
230
|
+
* with G groups that was an N+1 of G index lookups per request. Every
|
|
231
|
+
* requested group id gets an entry in the returned dictionary (empty array
|
|
232
|
+
* when the group has no resources).
|
|
233
|
+
*/
|
|
234
|
+
@CaptureSpan()
|
|
235
|
+
public async getMonitorGroupResourcesByGroupIds(
|
|
236
|
+
monitorGroupIds: Array<ObjectID>,
|
|
237
|
+
): Promise<Dictionary<Array<MonitorGroupResource>>> {
|
|
238
|
+
const resourcesByGroupId: Dictionary<Array<MonitorGroupResource>> = {};
|
|
239
|
+
|
|
240
|
+
const dedupedGroupIds: Array<ObjectID> = [];
|
|
241
|
+
for (const monitorGroupId of monitorGroupIds) {
|
|
242
|
+
if (monitorGroupId && !resourcesByGroupId[monitorGroupId.toString()]) {
|
|
243
|
+
resourcesByGroupId[monitorGroupId.toString()] = [];
|
|
244
|
+
dedupedGroupIds.push(monitorGroupId);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
if (dedupedGroupIds.length === 0) {
|
|
249
|
+
return resourcesByGroupId;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const monitorGroupResources: Array<MonitorGroupResource> =
|
|
253
|
+
await MonitorGroupResourceService.findAllBy({
|
|
254
|
+
query: {
|
|
255
|
+
monitorGroupId: QueryHelper.any(dedupedGroupIds),
|
|
256
|
+
},
|
|
257
|
+
select: {
|
|
258
|
+
monitorGroupId: true,
|
|
259
|
+
monitorId: true,
|
|
260
|
+
monitor: {
|
|
261
|
+
currentMonitorStatusId: true,
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
props: {
|
|
265
|
+
isRoot: true,
|
|
266
|
+
},
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
for (const monitorGroupResource of monitorGroupResources) {
|
|
270
|
+
if (!monitorGroupResource.monitorGroupId) {
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
resourcesByGroupId[monitorGroupResource.monitorGroupId.toString()]?.push(
|
|
275
|
+
monitorGroupResource,
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
return resourcesByGroupId;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
// Batched twin of the per-group monitor-id lookup: ONE query for all groups.
|
|
283
|
+
@CaptureSpan()
|
|
284
|
+
public async getMonitorIdsInMonitorGroups(
|
|
285
|
+
monitorGroupIds: Array<ObjectID>,
|
|
286
|
+
): Promise<Dictionary<Array<ObjectID>>> {
|
|
287
|
+
const resourcesByGroupId: Dictionary<Array<MonitorGroupResource>> =
|
|
288
|
+
await this.getMonitorGroupResourcesByGroupIds(monitorGroupIds);
|
|
289
|
+
|
|
290
|
+
const monitorIdsByGroupId: Dictionary<Array<ObjectID>> = {};
|
|
291
|
+
|
|
292
|
+
for (const groupId of Object.keys(resourcesByGroupId)) {
|
|
293
|
+
monitorIdsByGroupId[groupId] = (resourcesByGroupId[groupId] || [])
|
|
294
|
+
.map((resource: MonitorGroupResource) => {
|
|
295
|
+
return resource.monitorId!;
|
|
296
|
+
})
|
|
297
|
+
.filter((id: ObjectID) => {
|
|
298
|
+
return Boolean(id); // remove nulls
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
return monitorIdsByGroupId;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
/*
|
|
306
|
+
* Batched twin of getCurrentStatus, preserving its exact semantics: each
|
|
307
|
+
* group's status starts at the project's operational state and is replaced
|
|
308
|
+
* by the highest-priority-number status among its monitors. Callers supply
|
|
309
|
+
* the project's monitor statuses (they already have them in hand on every
|
|
310
|
+
* path that renders a status page), so this issues at most ONE query — the
|
|
311
|
+
* group-resource fetch — instead of three per group.
|
|
312
|
+
*/
|
|
313
|
+
@CaptureSpan()
|
|
314
|
+
public async getCurrentStatusesForMonitorGroups(data: {
|
|
315
|
+
monitorGroupIds: Array<ObjectID>;
|
|
316
|
+
monitorStatuses: Array<MonitorStatus>;
|
|
317
|
+
monitorGroupResources?: Dictionary<Array<MonitorGroupResource>> | undefined;
|
|
318
|
+
}): Promise<Dictionary<MonitorStatus>> {
|
|
319
|
+
const currentStatuses: Dictionary<MonitorStatus> = {};
|
|
320
|
+
|
|
321
|
+
if (data.monitorGroupIds.length === 0) {
|
|
322
|
+
return currentStatuses;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
const operationalStatus: MonitorStatus | undefined =
|
|
326
|
+
data.monitorStatuses.find((monitorStatus: MonitorStatus) => {
|
|
327
|
+
return monitorStatus.isOperationalState;
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
if (!operationalStatus) {
|
|
331
|
+
throw new BadDataException("Operational state not found.");
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
const resourcesByGroupId: Dictionary<Array<MonitorGroupResource>> =
|
|
335
|
+
data.monitorGroupResources ||
|
|
336
|
+
(await this.getMonitorGroupResourcesByGroupIds(data.monitorGroupIds));
|
|
337
|
+
|
|
338
|
+
for (const monitorGroupId of data.monitorGroupIds) {
|
|
339
|
+
let currentStatus: MonitorStatus = operationalStatus;
|
|
340
|
+
|
|
341
|
+
for (const monitorGroupResource of resourcesByGroupId[
|
|
342
|
+
monitorGroupId.toString()
|
|
343
|
+
] || []) {
|
|
344
|
+
if (!monitorGroupResource.monitor) {
|
|
345
|
+
continue;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
const monitorStatus: MonitorStatus | undefined =
|
|
349
|
+
data.monitorStatuses.find((monitorStatus: MonitorStatus) => {
|
|
350
|
+
return (
|
|
351
|
+
monitorStatus.id?.toString() ===
|
|
352
|
+
monitorGroupResource.monitor!.currentMonitorStatusId?.toString()
|
|
353
|
+
);
|
|
354
|
+
});
|
|
355
|
+
|
|
356
|
+
if (
|
|
357
|
+
monitorStatus &&
|
|
358
|
+
currentStatus.priority! < monitorStatus.priority!
|
|
359
|
+
) {
|
|
360
|
+
currentStatus = monitorStatus;
|
|
361
|
+
}
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
currentStatuses[monitorGroupId.toString()] = currentStatus;
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
return currentStatuses;
|
|
368
|
+
}
|
|
224
369
|
}
|
|
225
370
|
export default new Service();
|