@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
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* StatusPageService monitor-group batching.
|
|
3
|
+
*
|
|
4
|
+
* Public status-page reads (overview, badge, subscriber reports) used to
|
|
5
|
+
* issue one MonitorGroupResource query PER STATUS PAGE RESOURCE (not even per
|
|
6
|
+
* distinct group) and re-fetch the page's StatusPageResource rows the caller
|
|
7
|
+
* already had in hand. These tests pin the two batched entry points:
|
|
8
|
+
* - getMonitorIdsOnStatusPage: skips the StatusPageResource query entirely
|
|
9
|
+
* when the caller supplies the rows, resolves ALL groups through ONE
|
|
10
|
+
* MonitorGroupService.getMonitorIdsInMonitorGroups call, and de-dupes a
|
|
11
|
+
* monitor that is both directly on the page and inside a group,
|
|
12
|
+
* - getMonitorGroupCurrentStatuses: one batched group-resource fetch for
|
|
13
|
+
* any number of resources (including repeated references to the same
|
|
14
|
+
* group), while keeping its OWN worst-status semantics (see below).
|
|
15
|
+
*
|
|
16
|
+
* SEMANTICS NOTE (deliberate, preserved as-is by the batching change):
|
|
17
|
+
* StatusPageService.getMonitorGroupCurrentStatuses picks the LOWEST
|
|
18
|
+
* priority-number status as "worst" and produces NO entry for a group whose
|
|
19
|
+
* member statuses cannot be resolved — the exact OPPOSITE of
|
|
20
|
+
* MonitorGroupService.getCurrentStatus(es), where the HIGHEST priority number
|
|
21
|
+
* wins on top of an operational default. Both behaviors predate the batching
|
|
22
|
+
* and both are pinned so neither can silently converge on the other.
|
|
23
|
+
*
|
|
24
|
+
* All service reads are spied on — no database is touched.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
import StatusPageService from "../../../Server/Services/StatusPageService";
|
|
28
|
+
import StatusPageResourceService from "../../../Server/Services/StatusPageResourceService";
|
|
29
|
+
import MonitorGroupService from "../../../Server/Services/MonitorGroupService";
|
|
30
|
+
import StatusPageResource from "../../../Models/DatabaseModels/StatusPageResource";
|
|
31
|
+
import MonitorGroupResource from "../../../Models/DatabaseModels/MonitorGroupResource";
|
|
32
|
+
import MonitorStatus from "../../../Models/DatabaseModels/MonitorStatus";
|
|
33
|
+
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
34
|
+
import Dictionary from "../../../Types/Dictionary";
|
|
35
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
36
|
+
import { afterEach, describe, expect, jest, test } from "@jest/globals";
|
|
37
|
+
|
|
38
|
+
function makeStatusPageResource(data: {
|
|
39
|
+
monitorId?: ObjectID | undefined;
|
|
40
|
+
monitorGroupId?: ObjectID | undefined;
|
|
41
|
+
}): StatusPageResource {
|
|
42
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
43
|
+
if (data.monitorId) {
|
|
44
|
+
resource.monitorId = data.monitorId;
|
|
45
|
+
}
|
|
46
|
+
if (data.monitorGroupId) {
|
|
47
|
+
resource.monitorGroupId = data.monitorGroupId;
|
|
48
|
+
}
|
|
49
|
+
return resource;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function makeGroupResource(data: {
|
|
53
|
+
monitorGroupId: ObjectID;
|
|
54
|
+
monitorId?: ObjectID | undefined;
|
|
55
|
+
currentMonitorStatusId?: ObjectID | undefined;
|
|
56
|
+
omitMonitor?: boolean;
|
|
57
|
+
}): MonitorGroupResource {
|
|
58
|
+
const resource: MonitorGroupResource = new MonitorGroupResource();
|
|
59
|
+
resource.monitorGroupId = data.monitorGroupId;
|
|
60
|
+
if (data.monitorId) {
|
|
61
|
+
resource.monitorId = data.monitorId;
|
|
62
|
+
}
|
|
63
|
+
if (!data.omitMonitor) {
|
|
64
|
+
const monitor: Monitor = new Monitor();
|
|
65
|
+
if (data.currentMonitorStatusId) {
|
|
66
|
+
monitor.currentMonitorStatusId = data.currentMonitorStatusId;
|
|
67
|
+
}
|
|
68
|
+
resource.monitor = monitor;
|
|
69
|
+
}
|
|
70
|
+
return resource;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function makeStatus(data: { priority: number; name: string }): MonitorStatus {
|
|
74
|
+
const status: MonitorStatus = new MonitorStatus();
|
|
75
|
+
status.id = ObjectID.generate();
|
|
76
|
+
status.priority = data.priority;
|
|
77
|
+
status.name = data.name;
|
|
78
|
+
return status;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function toStrings(ids: Array<ObjectID>): Array<string> {
|
|
82
|
+
return ids.map((id: ObjectID) => {
|
|
83
|
+
return id.toString();
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
describe("StatusPageService monitor-group batching", () => {
|
|
88
|
+
afterEach(() => {
|
|
89
|
+
jest.restoreAllMocks();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
describe("getMonitorIdsOnStatusPage", () => {
|
|
93
|
+
test("supplied resources skip the StatusPageResource query; groups resolve via ONE batched call; direct+group monitor de-dupes", async () => {
|
|
94
|
+
const statusPageId: ObjectID = ObjectID.generate();
|
|
95
|
+
const groupA: ObjectID = ObjectID.generate();
|
|
96
|
+
const groupB: ObjectID = ObjectID.generate();
|
|
97
|
+
const directMonitor: ObjectID = ObjectID.generate();
|
|
98
|
+
const groupOnlyMonitorA: ObjectID = ObjectID.generate();
|
|
99
|
+
const groupOnlyMonitorB: ObjectID = ObjectID.generate();
|
|
100
|
+
|
|
101
|
+
const statusPageResources: Array<StatusPageResource> = [
|
|
102
|
+
// Direct monitor, no group: must contribute no group lookup.
|
|
103
|
+
makeStatusPageResource({ monitorId: directMonitor }),
|
|
104
|
+
makeStatusPageResource({ monitorGroupId: groupA }),
|
|
105
|
+
makeStatusPageResource({ monitorGroupId: groupB }),
|
|
106
|
+
];
|
|
107
|
+
|
|
108
|
+
const getResourcesSpy: jest.SpyInstance = jest.spyOn(
|
|
109
|
+
StatusPageService,
|
|
110
|
+
"getStatusPageResources",
|
|
111
|
+
) as unknown as jest.SpyInstance;
|
|
112
|
+
const findBySpy: jest.SpyInstance = jest.spyOn(
|
|
113
|
+
StatusPageResourceService,
|
|
114
|
+
"findBy",
|
|
115
|
+
) as unknown as jest.SpyInstance;
|
|
116
|
+
const batchedSpy: jest.SpyInstance = (
|
|
117
|
+
jest.spyOn(
|
|
118
|
+
MonitorGroupService,
|
|
119
|
+
"getMonitorIdsInMonitorGroups",
|
|
120
|
+
) as unknown as jest.SpyInstance
|
|
121
|
+
).mockResolvedValue({
|
|
122
|
+
// groupA contains the direct monitor too — it must appear only once.
|
|
123
|
+
[groupA.toString()]: [directMonitor, groupOnlyMonitorA],
|
|
124
|
+
[groupB.toString()]: [groupOnlyMonitorB],
|
|
125
|
+
} as never);
|
|
126
|
+
|
|
127
|
+
const result: {
|
|
128
|
+
monitorsOnStatusPage: Array<ObjectID>;
|
|
129
|
+
monitorsInGroup: Dictionary<Array<ObjectID>>;
|
|
130
|
+
} = await StatusPageService.getMonitorIdsOnStatusPage({
|
|
131
|
+
statusPageId: statusPageId,
|
|
132
|
+
statusPageResources: statusPageResources,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
// Supplied rows mean the (identical) StatusPageResource query never runs.
|
|
136
|
+
expect(getResourcesSpy).not.toHaveBeenCalled();
|
|
137
|
+
expect(findBySpy).not.toHaveBeenCalled();
|
|
138
|
+
|
|
139
|
+
// ONE batched group lookup for all groups; null groupIds filtered out.
|
|
140
|
+
expect(batchedSpy).toHaveBeenCalledTimes(1);
|
|
141
|
+
expect(
|
|
142
|
+
toStrings(batchedSpy.mock.calls[0]![0] as Array<ObjectID>),
|
|
143
|
+
).toEqual(toStrings([groupA, groupB]));
|
|
144
|
+
|
|
145
|
+
// Union of direct + group monitors, without duplicating directMonitor.
|
|
146
|
+
expect(toStrings(result.monitorsOnStatusPage)).toEqual(
|
|
147
|
+
toStrings([directMonitor, groupOnlyMonitorA, groupOnlyMonitorB]),
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
// The per-group membership dict is passed through exactly.
|
|
151
|
+
expect(Object.keys(result.monitorsInGroup).sort()).toEqual(
|
|
152
|
+
[groupA.toString(), groupB.toString()].sort(),
|
|
153
|
+
);
|
|
154
|
+
expect(toStrings(result.monitorsInGroup[groupA.toString()]!)).toEqual(
|
|
155
|
+
toStrings([directMonitor, groupOnlyMonitorA]),
|
|
156
|
+
);
|
|
157
|
+
expect(toStrings(result.monitorsInGroup[groupB.toString()]!)).toEqual(
|
|
158
|
+
toStrings([groupOnlyMonitorB]),
|
|
159
|
+
);
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
test("without supplied resources it fetches them once itself", async () => {
|
|
163
|
+
const statusPageId: ObjectID = ObjectID.generate();
|
|
164
|
+
const directMonitor: ObjectID = ObjectID.generate();
|
|
165
|
+
|
|
166
|
+
const getResourcesSpy: jest.SpyInstance = (
|
|
167
|
+
jest.spyOn(
|
|
168
|
+
StatusPageService,
|
|
169
|
+
"getStatusPageResources",
|
|
170
|
+
) as unknown as jest.SpyInstance
|
|
171
|
+
).mockResolvedValue([
|
|
172
|
+
makeStatusPageResource({ monitorId: directMonitor }),
|
|
173
|
+
] as never);
|
|
174
|
+
const batchedSpy: jest.SpyInstance = (
|
|
175
|
+
jest.spyOn(
|
|
176
|
+
MonitorGroupService,
|
|
177
|
+
"getMonitorIdsInMonitorGroups",
|
|
178
|
+
) as unknown as jest.SpyInstance
|
|
179
|
+
).mockResolvedValue({} as never);
|
|
180
|
+
|
|
181
|
+
const result: {
|
|
182
|
+
monitorsOnStatusPage: Array<ObjectID>;
|
|
183
|
+
monitorsInGroup: Dictionary<Array<ObjectID>>;
|
|
184
|
+
} = await StatusPageService.getMonitorIdsOnStatusPage({
|
|
185
|
+
statusPageId: statusPageId,
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
expect(getResourcesSpy).toHaveBeenCalledTimes(1);
|
|
189
|
+
expect(getResourcesSpy).toHaveBeenCalledWith({
|
|
190
|
+
statusPageId: statusPageId,
|
|
191
|
+
});
|
|
192
|
+
expect(batchedSpy).toHaveBeenCalledTimes(1);
|
|
193
|
+
expect(batchedSpy).toHaveBeenCalledWith([]);
|
|
194
|
+
expect(toStrings(result.monitorsOnStatusPage)).toEqual(
|
|
195
|
+
toStrings([directMonitor]),
|
|
196
|
+
);
|
|
197
|
+
expect(result.monitorsInGroup).toEqual({});
|
|
198
|
+
});
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
describe("getMonitorGroupCurrentStatuses", () => {
|
|
202
|
+
const statusOperational: MonitorStatus = makeStatus({
|
|
203
|
+
priority: 1,
|
|
204
|
+
name: "Operational",
|
|
205
|
+
});
|
|
206
|
+
const statusDegraded: MonitorStatus = makeStatus({
|
|
207
|
+
priority: 2,
|
|
208
|
+
name: "Degraded",
|
|
209
|
+
});
|
|
210
|
+
const statusOffline: MonitorStatus = makeStatus({
|
|
211
|
+
priority: 3,
|
|
212
|
+
name: "Offline",
|
|
213
|
+
});
|
|
214
|
+
const allStatuses: Array<MonitorStatus> = [
|
|
215
|
+
statusOperational,
|
|
216
|
+
statusDegraded,
|
|
217
|
+
statusOffline,
|
|
218
|
+
];
|
|
219
|
+
|
|
220
|
+
test("one batched fetch for many resources; LOWEST priority number wins; direct/unresolvable groups get no entry", async () => {
|
|
221
|
+
const groupA: ObjectID = ObjectID.generate();
|
|
222
|
+
const groupB: ObjectID = ObjectID.generate();
|
|
223
|
+
|
|
224
|
+
const statusPageResources: Array<StatusPageResource> = [
|
|
225
|
+
makeStatusPageResource({ monitorGroupId: groupA }),
|
|
226
|
+
makeStatusPageResource({ monitorGroupId: groupB }),
|
|
227
|
+
// The same group referenced twice must not trigger a second fetch.
|
|
228
|
+
makeStatusPageResource({ monitorGroupId: groupA }),
|
|
229
|
+
// Direct monitor resource: contributes no group, gets no entry.
|
|
230
|
+
makeStatusPageResource({ monitorId: ObjectID.generate() }),
|
|
231
|
+
];
|
|
232
|
+
|
|
233
|
+
const batchedSpy: jest.SpyInstance = (
|
|
234
|
+
jest.spyOn(
|
|
235
|
+
MonitorGroupService,
|
|
236
|
+
"getMonitorGroupResourcesByGroupIds",
|
|
237
|
+
) as unknown as jest.SpyInstance
|
|
238
|
+
).mockResolvedValue({
|
|
239
|
+
[groupA.toString()]: [
|
|
240
|
+
makeGroupResource({
|
|
241
|
+
monitorGroupId: groupA,
|
|
242
|
+
monitorId: ObjectID.generate(),
|
|
243
|
+
currentMonitorStatusId: statusDegraded.id!,
|
|
244
|
+
}),
|
|
245
|
+
makeGroupResource({
|
|
246
|
+
monitorGroupId: groupA,
|
|
247
|
+
monitorId: ObjectID.generate(),
|
|
248
|
+
currentMonitorStatusId: statusOffline.id!,
|
|
249
|
+
}),
|
|
250
|
+
/*
|
|
251
|
+
* Would be the "worst" under these semantics (lowest number), but
|
|
252
|
+
* the row has no monitorId, so the filter must drop it.
|
|
253
|
+
*/
|
|
254
|
+
makeGroupResource({
|
|
255
|
+
monitorGroupId: groupA,
|
|
256
|
+
currentMonitorStatusId: statusOperational.id!,
|
|
257
|
+
}),
|
|
258
|
+
// No monitor relation loaded: filtered out.
|
|
259
|
+
makeGroupResource({
|
|
260
|
+
monitorGroupId: groupA,
|
|
261
|
+
monitorId: ObjectID.generate(),
|
|
262
|
+
omitMonitor: true,
|
|
263
|
+
}),
|
|
264
|
+
],
|
|
265
|
+
// groupB's only member points at a status that no longer exists.
|
|
266
|
+
[groupB.toString()]: [
|
|
267
|
+
makeGroupResource({
|
|
268
|
+
monitorGroupId: groupB,
|
|
269
|
+
monitorId: ObjectID.generate(),
|
|
270
|
+
currentMonitorStatusId: ObjectID.generate(),
|
|
271
|
+
}),
|
|
272
|
+
],
|
|
273
|
+
} as never);
|
|
274
|
+
|
|
275
|
+
const result: Dictionary<ObjectID> =
|
|
276
|
+
await StatusPageService.getMonitorGroupCurrentStatuses({
|
|
277
|
+
statusPageResources: statusPageResources,
|
|
278
|
+
monitorStatuses: allStatuses,
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// ONE batched query serves every resource on the page.
|
|
282
|
+
expect(batchedSpy).toHaveBeenCalledTimes(1);
|
|
283
|
+
/*
|
|
284
|
+
* The raw (non-deduped, null-filtered) group id list is handed to the
|
|
285
|
+
* helper — deduping is the helper's job, pinned in its own suite.
|
|
286
|
+
*/
|
|
287
|
+
expect(
|
|
288
|
+
toStrings(batchedSpy.mock.calls[0]![0] as Array<ObjectID>),
|
|
289
|
+
).toEqual(toStrings([groupA, groupB, groupA]));
|
|
290
|
+
|
|
291
|
+
/*
|
|
292
|
+
* StatusPageService semantics: the LOWEST priority number is "worst",
|
|
293
|
+
* so Degraded (2) beats Offline (3). This is the OPPOSITE of
|
|
294
|
+
* MonitorGroupService.getCurrentStatus(es), where the HIGHEST number
|
|
295
|
+
* wins — both pre-existing behaviors are preserved as-is by the
|
|
296
|
+
* batching change, and this assertion keeps them from converging.
|
|
297
|
+
*/
|
|
298
|
+
expect(result[groupA.toString()]).toBeInstanceOf(ObjectID);
|
|
299
|
+
expect(result[groupA.toString()]!.toString()).toBe(
|
|
300
|
+
statusDegraded.id!.toString(),
|
|
301
|
+
);
|
|
302
|
+
|
|
303
|
+
// No resolvable member status -> NO dict entry (no operational default).
|
|
304
|
+
expect(result[groupB.toString()]).toBeUndefined();
|
|
305
|
+
// Direct-monitor resources never produce group entries.
|
|
306
|
+
expect(Object.keys(result)).toEqual([groupA.toString()]);
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
test("no group resources at all yields an empty dictionary", async () => {
|
|
310
|
+
const batchedSpy: jest.SpyInstance = (
|
|
311
|
+
jest.spyOn(
|
|
312
|
+
MonitorGroupService,
|
|
313
|
+
"getMonitorGroupResourcesByGroupIds",
|
|
314
|
+
) as unknown as jest.SpyInstance
|
|
315
|
+
).mockResolvedValue({} as never);
|
|
316
|
+
|
|
317
|
+
const result: Dictionary<ObjectID> =
|
|
318
|
+
await StatusPageService.getMonitorGroupCurrentStatuses({
|
|
319
|
+
statusPageResources: [
|
|
320
|
+
makeStatusPageResource({ monitorId: ObjectID.generate() }),
|
|
321
|
+
],
|
|
322
|
+
monitorStatuses: allStatuses,
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
expect(result).toEqual({});
|
|
326
|
+
// Still exactly one batched call (with an empty id list).
|
|
327
|
+
expect(batchedSpy).toHaveBeenCalledTimes(1);
|
|
328
|
+
expect(batchedSpy).toHaveBeenCalledWith([]);
|
|
329
|
+
});
|
|
330
|
+
});
|
|
331
|
+
});
|
|
@@ -261,8 +261,8 @@ describe("UserOnCallLogService.claimNotificationRuleExecution", () => {
|
|
|
261
261
|
.mockResolvedValue(fakeLog(logId, {}));
|
|
262
262
|
const updateOneById: jest.SpyInstance = jest
|
|
263
263
|
.spyOn(UserOnCallLogService, "updateOneById")
|
|
264
|
-
.mockImplementation((): Promise<
|
|
265
|
-
return Promise.resolve();
|
|
264
|
+
.mockImplementation((): Promise<number> => {
|
|
265
|
+
return Promise.resolve(1);
|
|
266
266
|
});
|
|
267
267
|
|
|
268
268
|
const claimed: boolean =
|
|
@@ -319,8 +319,8 @@ describe("UserOnCallLogService.claimNotificationRuleExecution", () => {
|
|
|
319
319
|
.mockResolvedValue(fakeLog(logId, undefined));
|
|
320
320
|
const updateOneById: jest.SpyInstance = jest
|
|
321
321
|
.spyOn(UserOnCallLogService, "updateOneById")
|
|
322
|
-
.mockImplementation((): Promise<
|
|
323
|
-
return Promise.resolve();
|
|
322
|
+
.mockImplementation((): Promise<number> => {
|
|
323
|
+
return Promise.resolve(1);
|
|
324
324
|
});
|
|
325
325
|
|
|
326
326
|
const claimed: boolean =
|
|
@@ -355,8 +355,8 @@ describe("UserOnCallLogService.claimNotificationRuleExecution", () => {
|
|
|
355
355
|
);
|
|
356
356
|
const updateOneById: jest.SpyInstance = jest
|
|
357
357
|
.spyOn(UserOnCallLogService, "updateOneById")
|
|
358
|
-
.mockImplementation((): Promise<
|
|
359
|
-
return Promise.resolve();
|
|
358
|
+
.mockImplementation((): Promise<number> => {
|
|
359
|
+
return Promise.resolve(1);
|
|
360
360
|
});
|
|
361
361
|
|
|
362
362
|
const claimed: boolean =
|
|
@@ -394,8 +394,8 @@ describe("UserOnCallLogService.claimNotificationRuleExecution", () => {
|
|
|
394
394
|
);
|
|
395
395
|
const updateOneById: jest.SpyInstance = jest
|
|
396
396
|
.spyOn(UserOnCallLogService, "updateOneById")
|
|
397
|
-
.mockImplementation((): Promise<
|
|
398
|
-
return Promise.resolve();
|
|
397
|
+
.mockImplementation((): Promise<number> => {
|
|
398
|
+
return Promise.resolve(1);
|
|
399
399
|
});
|
|
400
400
|
|
|
401
401
|
const claimed: boolean =
|
|
@@ -419,8 +419,8 @@ describe("UserOnCallLogService.claimNotificationRuleExecution", () => {
|
|
|
419
419
|
.mockResolvedValue(null);
|
|
420
420
|
const updateOneById: jest.SpyInstance = jest
|
|
421
421
|
.spyOn(UserOnCallLogService, "updateOneById")
|
|
422
|
-
.mockImplementation((): Promise<
|
|
423
|
-
return Promise.resolve();
|
|
422
|
+
.mockImplementation((): Promise<number> => {
|
|
423
|
+
return Promise.resolve(1);
|
|
424
424
|
});
|
|
425
425
|
|
|
426
426
|
const claimed: boolean =
|
|
@@ -445,8 +445,8 @@ describe("UserOnCallLogService.claimNotificationRuleExecution", () => {
|
|
|
445
445
|
.mockResolvedValue(fakeLog(ObjectID.generate(), {}));
|
|
446
446
|
jest
|
|
447
447
|
.spyOn(UserOnCallLogService, "updateOneById")
|
|
448
|
-
.mockImplementation((): Promise<
|
|
449
|
-
return Promise.resolve();
|
|
448
|
+
.mockImplementation((): Promise<number> => {
|
|
449
|
+
return Promise.resolve(1);
|
|
450
450
|
});
|
|
451
451
|
|
|
452
452
|
await UserOnCallLogService.claimNotificationRuleExecution({
|