@oneuptime/common 14.0.8 → 14.0.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (639) hide show
  1. package/Models/DatabaseModels/Index.ts +2 -0
  2. package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
  3. package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
  4. package/Models/DatabaseModels/Project.ts +55 -5
  5. package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
  6. package/Models/DatabaseModels/RunnerJob.ts +75 -0
  7. package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
  8. package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
  9. package/Server/API/AIChatAPI.ts +6 -3
  10. package/Server/API/AIInvestigationAPI.ts +17 -10
  11. package/Server/API/AutoRemediationAPI.ts +207 -25
  12. package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
  13. package/Server/API/MicrosoftTeamsAPI.ts +93 -14
  14. package/Server/API/ProxmoxResourceAPI.ts +244 -14
  15. package/Server/Infrastructure/GlobalCache.ts +32 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  20. package/Server/Middleware/TelemetryIngest.ts +41 -18
  21. package/Server/Services/AIService.ts +70 -1
  22. package/Server/Services/AlertService.ts +352 -11
  23. package/Server/Services/AlertSeverityService.ts +65 -15
  24. package/Server/Services/AutoRemediationRuleService.ts +1 -1
  25. package/Server/Services/IncidentService.ts +280 -104
  26. package/Server/Services/IncidentSeverityService.ts +66 -15
  27. package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
  28. package/Server/Services/KubernetesAiAgentService.ts +1634 -0
  29. package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
  30. package/Server/Services/KubernetesClusterService.ts +129 -21
  31. package/Server/Services/ProjectService.ts +32 -0
  32. package/Server/Services/ProxmoxResourceService.ts +322 -5
  33. package/Server/Services/RunnerJobService.ts +241 -104
  34. package/Server/Services/RunnerService.ts +1 -1
  35. package/Server/Services/ScheduledMaintenanceService.ts +737 -1
  36. package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
  37. package/Server/Services/TeamComplianceSettingService.ts +1125 -9
  38. package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
  39. package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
  40. package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
  41. package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
  42. package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
  43. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
  44. package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
  45. package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
  46. package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
  47. package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
  48. package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
  49. package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
  50. package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
  51. package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
  52. package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
  53. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
  54. package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
  55. package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
  56. package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
  57. package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
  58. package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
  59. package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
  60. package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
  61. package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
  62. package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
  63. package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
  64. package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
  65. package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
  66. package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
  67. package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
  68. package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
  69. package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
  70. package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
  71. package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
  72. package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
  73. package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
  74. package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
  75. package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
  76. package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
  77. package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
  78. package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
  79. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
  80. package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
  81. package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
  82. package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
  83. package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
  84. package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
  85. package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
  86. package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
  87. package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
  88. package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
  89. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
  90. package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
  91. package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
  92. package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
  93. package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
  94. package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
  95. package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
  96. package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
  97. package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
  98. package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
  99. package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
  100. package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
  101. package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
  102. package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
  103. package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
  104. package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
  105. package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
  106. package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
  107. package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
  108. package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
  109. package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
  110. package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
  111. package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
  112. package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
  113. package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
  114. package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
  115. package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
  116. package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
  117. package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
  118. package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
  119. package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
  120. package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
  121. package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
  122. package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
  123. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
  124. package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
  125. package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
  126. package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
  127. package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
  128. package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
  129. package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
  130. package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
  131. package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
  132. package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
  133. package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
  134. package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
  135. package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
  136. package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
  137. package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
  138. package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
  139. package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
  140. package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
  141. package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
  142. package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
  143. package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
  144. package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
  145. package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
  146. package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
  147. package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
  148. package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
  149. package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
  150. package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
  151. package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
  152. package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
  153. package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
  154. package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
  155. package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
  156. package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
  157. package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
  158. package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
  159. package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
  160. package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
  161. package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
  162. package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
  163. package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
  164. package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
  165. package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
  166. package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
  167. package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
  168. package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
  169. package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
  170. package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
  171. package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
  172. package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
  173. package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
  174. package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
  175. package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
  176. package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
  177. package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
  178. package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
  179. package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
  180. package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
  181. package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
  182. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
  183. package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
  184. package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
  185. package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
  186. package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
  187. package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
  188. package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
  189. package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
  190. package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
  191. package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
  192. package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
  193. package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
  194. package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
  195. package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
  196. package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
  197. package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
  198. package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
  199. package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
  200. package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
  201. package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
  202. package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
  203. package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
  204. package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
  205. package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
  206. package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
  207. package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
  208. package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
  209. package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
  210. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
  211. package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
  212. package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
  213. package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
  214. package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
  215. package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
  216. package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
  217. package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
  218. package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
  219. package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
  220. package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
  221. package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
  222. package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
  223. package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
  224. package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
  225. package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
  226. package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
  227. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
  228. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
  229. package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
  230. package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
  231. package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
  232. package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
  233. package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
  234. package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
  235. package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
  236. package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
  237. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
  238. package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
  239. package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
  240. package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
  241. package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
  242. package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
  243. package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
  244. package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
  245. package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
  246. package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
  247. package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
  248. package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
  249. package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
  250. package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
  251. package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
  252. package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
  253. package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
  254. package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
  255. package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
  256. package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
  257. package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
  258. package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
  259. package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
  260. package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
  261. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
  262. package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
  263. package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
  264. package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
  265. package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
  266. package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
  267. package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
  268. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
  269. package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
  270. package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
  271. package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
  272. package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
  273. package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
  274. package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
  275. package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
  276. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
  277. package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
  278. package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
  279. package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
  280. package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
  281. package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
  282. package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
  283. package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
  284. package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
  285. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
  286. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
  287. package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
  288. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
  289. package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
  290. package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
  291. package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
  292. package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
  293. package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
  294. package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
  295. package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
  296. package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
  297. package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
  298. package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
  299. package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
  300. package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
  301. package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
  302. package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
  303. package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
  304. package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
  305. package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
  306. package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
  307. package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
  308. package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
  309. package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
  310. package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
  311. package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
  312. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
  313. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
  314. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
  315. package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
  316. package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
  317. package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
  318. package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
  319. package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
  320. package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
  321. package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
  322. package/Tests/UI/Components/Input.test.tsx +80 -1
  323. package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
  324. package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
  325. package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
  326. package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
  327. package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
  328. package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
  329. package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
  330. package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
  331. package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
  332. package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
  333. package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
  334. package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
  335. package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
  336. package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
  337. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
  338. package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
  339. package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
  340. package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
  341. package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
  342. package/Tests/UI/Components/Toggle.test.tsx +66 -0
  343. package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
  344. package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
  345. package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
  346. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
  347. package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
  348. package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
  349. package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
  350. package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
  351. package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
  352. package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
  353. package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
  354. package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
  355. package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
  356. package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
  357. package/Types/AI/AIRunType.ts +9 -4
  358. package/Types/AI/InvestigationNotStartedReason.ts +6 -0
  359. package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
  360. package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
  361. package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
  362. package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
  363. package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
  364. package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
  365. package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
  366. package/Types/Team/ComplianceNotificationChannel.ts +22 -0
  367. package/Types/Team/ComplianceRule.ts +434 -0
  368. package/Types/Team/ComplianceRuleType.ts +21 -0
  369. package/Types/Team/TeamComplianceStatus.ts +81 -0
  370. package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
  371. package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
  372. package/UI/Components/ActionButton/RowActions.tsx +238 -0
  373. package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
  374. package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
  375. package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
  376. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
  377. package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
  378. package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
  379. package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
  380. package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
  381. package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
  382. package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
  383. package/UI/Components/Charts/Line/LineChart.tsx +32 -2
  384. package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
  385. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
  386. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
  387. package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
  388. package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
  389. package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
  390. package/UI/Components/Charts/Utils/XAxis.ts +75 -0
  391. package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
  392. package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
  393. package/UI/Components/Forms/Fields/FormField.tsx +10 -0
  394. package/UI/Components/Input/Input.tsx +90 -57
  395. package/UI/Components/List/ListRow.tsx +8 -74
  396. package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
  397. package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
  398. package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
  399. package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
  400. package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
  401. package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
  402. package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
  403. package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
  404. package/UI/Components/OrderedStatesList/Item.tsx +7 -72
  405. package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
  406. package/UI/Components/Table/TableBody.tsx +8 -2
  407. package/UI/Components/Table/TableRow.tsx +20 -140
  408. package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
  409. package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
  410. package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
  411. package/UI/Components/TelemetryViewer/types.ts +2 -0
  412. package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
  413. package/UI/Components/Toggle/Toggle.tsx +26 -0
  414. package/UI/Components/Tooltip/Tooltip.tsx +8 -1
  415. package/UI/Styles/Theme.css +14 -2
  416. package/Utils/AiRemediation/CommandPolicy.ts +1 -1
  417. package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
  418. package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
  419. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  420. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  421. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
  422. package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
  423. package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
  424. package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
  425. package/build/dist/Models/DatabaseModels/Project.js +56 -5
  426. package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
  427. package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
  428. package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
  429. package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
  430. package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
  431. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
  432. package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
  433. package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
  434. package/build/dist/Server/API/AIChatAPI.js +6 -1
  435. package/build/dist/Server/API/AIChatAPI.js.map +1 -1
  436. package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
  437. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  438. package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
  439. package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
  440. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
  441. package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
  442. package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
  443. package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
  444. package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
  445. package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
  446. package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
  447. package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
  448. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
  449. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
  450. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
  451. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
  452. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
  453. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
  454. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  455. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  456. package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
  457. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  458. package/build/dist/Server/Services/AIService.js +59 -1
  459. package/build/dist/Server/Services/AIService.js.map +1 -1
  460. package/build/dist/Server/Services/AlertService.js +248 -13
  461. package/build/dist/Server/Services/AlertService.js.map +1 -1
  462. package/build/dist/Server/Services/AlertSeverityService.js +51 -15
  463. package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
  464. package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
  465. package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
  466. package/build/dist/Server/Services/IncidentService.js +199 -72
  467. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  468. package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
  469. package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
  470. package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
  471. package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
  472. package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
  473. package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
  474. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
  475. package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
  476. package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
  477. package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
  478. package/build/dist/Server/Services/ProjectService.js +22 -0
  479. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  480. package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
  481. package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
  482. package/build/dist/Server/Services/RunnerJobService.js +178 -92
  483. package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
  484. package/build/dist/Server/Services/RunnerService.js +1 -1
  485. package/build/dist/Server/Services/RunnerService.js.map +1 -1
  486. package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
  487. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  488. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
  489. package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
  490. package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
  491. package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
  492. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
  493. package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
  494. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
  495. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
  496. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
  497. package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
  498. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
  499. package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
  500. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
  501. package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
  502. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
  503. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  504. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
  505. package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
  506. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
  507. package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
  508. package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
  509. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
  510. package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
  511. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
  512. package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
  513. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
  514. package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
  515. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
  516. package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
  517. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
  518. package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
  519. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
  520. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  521. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
  522. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  523. package/build/dist/Types/AI/AIRunType.js +9 -4
  524. package/build/dist/Types/AI/AIRunType.js.map +1 -1
  525. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
  526. package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
  527. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
  528. package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
  529. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
  530. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
  531. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
  532. package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
  533. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
  534. package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
  535. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
  536. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
  537. package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
  538. package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
  539. package/build/dist/Types/Team/ComplianceRule.js +304 -0
  540. package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
  541. package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
  542. package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
  543. package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
  544. package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
  545. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
  546. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
  547. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
  548. package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
  549. package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
  550. package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
  551. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
  552. package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
  553. package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
  554. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  555. package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
  556. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  557. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
  558. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  559. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
  560. package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
  561. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
  562. package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
  563. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
  564. package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
  565. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
  566. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
  567. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
  568. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
  569. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
  570. package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
  571. package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
  572. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  573. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
  574. package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
  575. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
  576. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
  577. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
  578. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
  579. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
  580. package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
  581. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
  582. package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
  583. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
  584. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  585. package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
  586. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  587. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
  588. package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
  589. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
  590. package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
  591. package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
  592. package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
  593. package/build/dist/UI/Components/Input/Input.js +69 -54
  594. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  595. package/build/dist/UI/Components/List/ListRow.js +4 -39
  596. package/build/dist/UI/Components/List/ListRow.js.map +1 -1
  597. package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
  598. package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
  599. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
  600. package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
  601. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
  602. package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
  603. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
  604. package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
  605. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
  606. package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
  607. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
  608. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  609. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
  610. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  611. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
  612. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  613. package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
  614. package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
  615. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
  616. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
  617. package/build/dist/UI/Components/Table/TableBody.js +6 -2
  618. package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
  619. package/build/dist/UI/Components/Table/TableRow.js +12 -75
  620. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  621. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
  622. package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
  623. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
  624. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
  625. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
  626. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
  627. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
  628. package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
  629. package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
  630. package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
  631. package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
  632. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  633. package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
  634. package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
  635. package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
  636. package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
  637. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
  638. package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
  639. package/package.json +1 -1
@@ -0,0 +1,972 @@
1
+ import MonitorService from "../../../Server/Services/MonitorService";
2
+ import MonitorStatusService from "../../../Server/Services/MonitorStatusService";
3
+ import MonitorStatusTimelineService, {
4
+ MONITOR_STATUS_SAME_AS_PREVIOUS_ERROR_MESSAGE,
5
+ MONITOR_STATUS_TIMELINE_LOCK_ERROR_MESSAGE,
6
+ } from "../../../Server/Services/MonitorStatusTimelineService";
7
+ import ScheduledMaintenanceMeasurementService from "../../../Server/Services/ScheduledMaintenanceMeasurementService";
8
+ import ScheduledMaintenanceService from "../../../Server/Services/ScheduledMaintenanceService";
9
+ import ScheduledMaintenanceStateTimelineService from "../../../Server/Services/ScheduledMaintenanceStateTimelineService";
10
+ import { OnDelete } from "../../../Server/Types/Database/Hooks";
11
+ import MeasurementMetricWriter from "../../../Server/Utils/Measurement/MeasurementMetricWriter";
12
+ import logger, { LogAttributes } from "../../../Server/Utils/Logger";
13
+ import Monitor from "../../../Models/DatabaseModels/Monitor";
14
+ import MonitorStatus from "../../../Models/DatabaseModels/MonitorStatus";
15
+ import MonitorStatusTimeline from "../../../Models/DatabaseModels/MonitorStatusTimeline";
16
+ import ScheduledMaintenance from "../../../Models/DatabaseModels/ScheduledMaintenance";
17
+ import ScheduledMaintenanceState from "../../../Models/DatabaseModels/ScheduledMaintenanceState";
18
+ import ScheduledMaintenanceStateTimeline from "../../../Models/DatabaseModels/ScheduledMaintenanceStateTimeline";
19
+ import SortOrder from "../../../Types/BaseDatabase/SortOrder";
20
+ import LIMIT_MAX from "../../../Types/Database/LimitMax";
21
+ import Dictionary from "../../../Types/Dictionary";
22
+ import BadDataException from "../../../Types/Exception/BadDataException";
23
+ import ServerException from "../../../Types/Exception/ServerException";
24
+ import { JSONObject } from "../../../Types/JSON";
25
+ import ObjectID from "../../../Types/ObjectID";
26
+ import PositiveNumber from "../../../Types/PositiveNumber";
27
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
28
+
29
+ /*
30
+ * An event releases its monitors when it ends, is resolved or deleted, or has
31
+ * them detached - clearing the flag that stopped probing and putting each
32
+ * back in the operational status - unless another event still holds the
33
+ * monitor.
34
+ *
35
+ * "Holds" means the same thing wherever it is asked: moved to ongoing and not
36
+ * since to ended or resolved, which includes a state the project added itself
37
+ * past ongoing (say "Verifying"). The check for ANOTHER event used to count
38
+ * only events in an ongoing state, so a monitor also held by an event in
39
+ * "Verifying" was re-enabled the moment the other event let go of it.
40
+ *
41
+ * And one monitor failing to be released used to abort the loop, leaving the
42
+ * rest disabled for good.
43
+ *
44
+ * The database is a fake: the events, each with its monitors, current state
45
+ * and state timeline, answer the event and timeline reads.
46
+ */
47
+
48
+ const PROJECT_ID: ObjectID = new ObjectID(
49
+ "0193c0de-5a5a-4aaa-8bbb-000000000001",
50
+ );
51
+ const RELEASING_EVENT_ID: string = "0193c0de-5a5a-4aaa-8bbb-0000000000e1";
52
+ const OTHER_EVENT_ID: string = "0193c0de-5a5a-4aaa-8bbb-0000000000e2";
53
+ const THIRD_EVENT_ID: string = "0193c0de-5a5a-4aaa-8bbb-0000000000e3";
54
+
55
+ const OPERATIONAL_STATUS_ID: string = "0193c0de-5a5a-4aaa-8bbb-0000000000f0";
56
+ const MAINTENANCE_STATUS_ID: string = "0193c0de-5a5a-4aaa-8bbb-0000000000f1";
57
+
58
+ const MONITOR_A: string = "0193c0de-5a5a-4aaa-8bbb-0000000000a1";
59
+ const MONITOR_B: string = "0193c0de-5a5a-4aaa-8bbb-0000000000a2";
60
+ const MONITOR_C: string = "0193c0de-5a5a-4aaa-8bbb-0000000000a3";
61
+
62
+ // "custom" is a state the project added itself: none of the built-in kinds.
63
+ type StateKind = "scheduled" | "ongoing" | "ended" | "resolved" | "custom";
64
+
65
+ const STATE_IDS: Dictionary<string> = {
66
+ scheduled: "0193c0de-5a5a-4aaa-8bbb-0000000000d1",
67
+ ongoing: "0193c0de-5a5a-4aaa-8bbb-0000000000d2",
68
+ ended: "0193c0de-5a5a-4aaa-8bbb-0000000000d3",
69
+ resolved: "0193c0de-5a5a-4aaa-8bbb-0000000000d4",
70
+ custom: "0193c0de-5a5a-4aaa-8bbb-0000000000d5",
71
+ };
72
+
73
+ type StoredEvent = {
74
+ id: string;
75
+ state: StateKind;
76
+ monitors: Array<string>;
77
+ // Oldest first.
78
+ timeline: Array<StateKind>;
79
+ };
80
+
81
+ type EventFindBy = {
82
+ query: {
83
+ monitors: Array<ObjectID>;
84
+ currentScheduledMaintenanceState: Dictionary<boolean>;
85
+ };
86
+ select: JSONObject;
87
+ limit: number;
88
+ props: JSONObject;
89
+ };
90
+
91
+ type TimelineFindBy = {
92
+ query: { scheduledMaintenanceId: unknown; projectId: ObjectID };
93
+ select: JSONObject;
94
+ sort: JSONObject;
95
+ limit: number;
96
+ props: JSONObject;
97
+ };
98
+
99
+ type EventIdsOfFunction = (queryValue: unknown) => Array<string>;
100
+
101
+ /*
102
+ * The events a timeline read asks for: one event's id, or QueryHelper.any of
103
+ * several - a TypeORM Raw operator carrying them as a named parameter.
104
+ */
105
+ const eventIdsOf: EventIdsOfFunction = (queryValue: unknown): Array<string> => {
106
+ const parameters: Record<string, unknown> | undefined = (
107
+ queryValue as { objectLiteralParameters?: Record<string, unknown> }
108
+ )?.objectLiteralParameters;
109
+
110
+ const values: Array<unknown> | undefined = Object.values(
111
+ parameters || {},
112
+ ).find((parameter: unknown): boolean => {
113
+ return Array.isArray(parameter);
114
+ }) as Array<unknown> | undefined;
115
+
116
+ return (values || [queryValue]).map((value: unknown): string => {
117
+ return String(value);
118
+ });
119
+ };
120
+
121
+ // The events each timeline read asked for, in order.
122
+ function timelineReads(): Array<Array<string>> {
123
+ return timelineFindBy.mock.calls.map(
124
+ (call: Array<unknown>): Array<string> => {
125
+ return eventIdsOf(
126
+ (call[0] as TimelineFindBy).query.scheduledMaintenanceId,
127
+ );
128
+ },
129
+ );
130
+ }
131
+
132
+ type OnDeleteSuccessFunction = (
133
+ onDelete: OnDelete<ScheduledMaintenance>,
134
+ deletedItemIds: Array<ObjectID>,
135
+ ) => Promise<OnDelete<ScheduledMaintenance>>;
136
+
137
+ function state(kind: StateKind): ScheduledMaintenanceState {
138
+ const scheduledMaintenanceState: ScheduledMaintenanceState =
139
+ new ScheduledMaintenanceState();
140
+ scheduledMaintenanceState._id = STATE_IDS[kind]!;
141
+ scheduledMaintenanceState.isScheduledState = kind === "scheduled";
142
+ scheduledMaintenanceState.isOngoingState = kind === "ongoing";
143
+ scheduledMaintenanceState.isEndedState = kind === "ended";
144
+ scheduledMaintenanceState.isResolvedState = kind === "resolved";
145
+ return scheduledMaintenanceState;
146
+ }
147
+
148
+ // Whether a state matches a relation query such as { isOngoingState: true }.
149
+ function stateMatches(kind: StateKind, query: Dictionary<boolean>): boolean {
150
+ const scheduledMaintenanceState: ScheduledMaintenanceState = state(kind);
151
+
152
+ return Object.keys(query).every((key: string): boolean => {
153
+ return (
154
+ Boolean(
155
+ (scheduledMaintenanceState as unknown as Dictionary<unknown>)[key],
156
+ ) === query[key]
157
+ );
158
+ });
159
+ }
160
+
161
+ function eventsMatching(query: EventFindBy["query"]): Array<StoredEvent> {
162
+ const monitorIds: Array<string> = (query.monitors || []).map(
163
+ (id: ObjectID): string => {
164
+ return id.toString();
165
+ },
166
+ );
167
+
168
+ return storedEvents.filter((storedEvent: StoredEvent): boolean => {
169
+ return (
170
+ monitorIds.every((monitorId: string): boolean => {
171
+ return storedEvent.monitors.includes(monitorId);
172
+ }) &&
173
+ stateMatches(
174
+ storedEvent.state,
175
+ query.currentScheduledMaintenanceState || {},
176
+ )
177
+ );
178
+ });
179
+ }
180
+
181
+ function releasingEvent(monitorIds: Array<string>): ScheduledMaintenance {
182
+ const scheduledMaintenance: ScheduledMaintenance = new ScheduledMaintenance(
183
+ new ObjectID(RELEASING_EVENT_ID),
184
+ );
185
+ scheduledMaintenance.projectId = PROJECT_ID;
186
+ scheduledMaintenance.monitors = monitorIds.map((id: string): Monitor => {
187
+ return new Monitor(new ObjectID(id));
188
+ });
189
+ return scheduledMaintenance;
190
+ }
191
+
192
+ let storedEvents: Array<StoredEvent> = [];
193
+
194
+ // Distinct event ids, as many as asked for.
195
+ function manyEventIds(count: number): Array<string> {
196
+ const eventIds: Array<string> = [];
197
+
198
+ for (let index: number = 0; index < count; index++) {
199
+ eventIds.push(
200
+ `0193c0de-5a5a-4aaa-8ccc-${index.toString(16).padStart(12, "0")}`,
201
+ );
202
+ }
203
+
204
+ return eventIds;
205
+ }
206
+
207
+ let eventFindBy: jest.SpyInstance;
208
+ let timelineFindBy: jest.SpyInstance;
209
+ let monitorUpdateOneById: jest.SpyInstance;
210
+ let statusTimelineCreate: jest.SpyInstance;
211
+ let loggerError: jest.SpyInstance;
212
+
213
+ // The monitors whose flag was cleared, in order.
214
+ function releasedMonitorIds(): Array<string> {
215
+ return monitorUpdateOneById.mock.calls
216
+ .filter((call: Array<unknown>): boolean => {
217
+ return (
218
+ (call[0] as { data: JSONObject }).data[
219
+ "disableActiveMonitoringBecauseOfScheduledMaintenanceEvent"
220
+ ] === false
221
+ );
222
+ })
223
+ .map((call: Array<unknown>): string => {
224
+ return (call[0] as { id: ObjectID }).id.toString();
225
+ });
226
+ }
227
+
228
+ // The monitors put back in the operational status, in order.
229
+ function operationalMonitorIds(): Array<string> {
230
+ return statusTimelineCreate.mock.calls.map((call: Array<unknown>): string => {
231
+ const monitorStatusTimeline: MonitorStatusTimeline = (
232
+ call[0] as { data: MonitorStatusTimeline }
233
+ ).data;
234
+
235
+ expect(monitorStatusTimeline.monitorStatusId?.toString()).toBe(
236
+ OPERATIONAL_STATUS_ID,
237
+ );
238
+
239
+ return monitorStatusTimeline.monitorId!.toString();
240
+ });
241
+ }
242
+
243
+ beforeEach(() => {
244
+ storedEvents = [];
245
+
246
+ jest
247
+ .spyOn(MonitorStatusService, "findOneBy")
248
+ .mockImplementation((async (): Promise<MonitorStatus> => {
249
+ return new MonitorStatus(new ObjectID(OPERATIONAL_STATUS_ID));
250
+ }) as never);
251
+
252
+ // Every monitor sits in the maintenance status.
253
+ jest
254
+ .spyOn(MonitorService, "findOneById")
255
+ .mockImplementation((async (findOneById: {
256
+ id: ObjectID;
257
+ }): Promise<Monitor> => {
258
+ const monitor: Monitor = new Monitor(findOneById.id);
259
+ monitor.currentMonitorStatusId = new ObjectID(MAINTENANCE_STATUS_ID);
260
+ return monitor;
261
+ }) as never);
262
+
263
+ monitorUpdateOneById = jest
264
+ .spyOn(MonitorService, "updateOneById")
265
+ .mockResolvedValue(undefined as never);
266
+
267
+ statusTimelineCreate = jest
268
+ .spyOn(MonitorStatusTimelineService, "create")
269
+ .mockImplementation((async (createBy: {
270
+ data: MonitorStatusTimeline;
271
+ }): Promise<MonitorStatusTimeline> => {
272
+ return createBy.data;
273
+ }) as never);
274
+
275
+ eventFindBy = jest
276
+ .spyOn(ScheduledMaintenanceService, "findBy")
277
+ .mockImplementation((async (
278
+ findBy: EventFindBy,
279
+ ): Promise<Array<ScheduledMaintenance>> => {
280
+ return eventsMatching(findBy.query).map(
281
+ (storedEvent: StoredEvent): ScheduledMaintenance => {
282
+ const scheduledMaintenance: ScheduledMaintenance =
283
+ new ScheduledMaintenance(new ObjectID(storedEvent.id));
284
+ scheduledMaintenance.projectId = PROJECT_ID;
285
+ scheduledMaintenance.currentScheduledMaintenanceState = state(
286
+ storedEvent.state,
287
+ );
288
+ return scheduledMaintenance;
289
+ },
290
+ );
291
+ }) as never);
292
+
293
+ // The same fake, counted, for a check that counts instead of reading.
294
+ jest
295
+ .spyOn(ScheduledMaintenanceService, "countBy")
296
+ .mockImplementation((async (countBy: {
297
+ query: EventFindBy["query"];
298
+ }): Promise<PositiveNumber> => {
299
+ return new PositiveNumber(eventsMatching(countBy.query).length);
300
+ }) as never);
301
+
302
+ /*
303
+ * Every row of the events asked for, oldest first as startsAt orders them:
304
+ * the rows of different events interleave, each event's in its own order.
305
+ */
306
+ timelineFindBy = jest
307
+ .spyOn(ScheduledMaintenanceStateTimelineService, "findBy")
308
+ .mockImplementation((async (
309
+ findBy: TimelineFindBy,
310
+ ): Promise<Array<ScheduledMaintenanceStateTimeline>> => {
311
+ const eventIds: Array<string> = eventIdsOf(
312
+ findBy.query.scheduledMaintenanceId,
313
+ );
314
+
315
+ const rows: Array<{
316
+ position: number;
317
+ item: ScheduledMaintenanceStateTimeline;
318
+ }> = [];
319
+
320
+ for (const storedEvent of storedEvents) {
321
+ if (!eventIds.includes(storedEvent.id)) {
322
+ continue;
323
+ }
324
+
325
+ storedEvent.timeline.forEach((kind: StateKind, position: number) => {
326
+ const item: ScheduledMaintenanceStateTimeline =
327
+ new ScheduledMaintenanceStateTimeline();
328
+ item.scheduledMaintenanceId = new ObjectID(storedEvent.id);
329
+ item.scheduledMaintenanceState = state(kind);
330
+ rows.push({ position: position, item: item });
331
+ });
332
+ }
333
+
334
+ return rows
335
+ .sort(
336
+ (
337
+ first: { position: number },
338
+ second: { position: number },
339
+ ): number => {
340
+ return first.position - second.position;
341
+ },
342
+ )
343
+ .map(
344
+ (row: {
345
+ item: ScheduledMaintenanceStateTimeline;
346
+ }): ScheduledMaintenanceStateTimeline => {
347
+ return row.item;
348
+ },
349
+ );
350
+ }) as never);
351
+
352
+ loggerError = jest.spyOn(logger, "error").mockImplementation(() => {
353
+ return undefined;
354
+ });
355
+ });
356
+
357
+ afterEach(() => {
358
+ jest.restoreAllMocks();
359
+ });
360
+
361
+ describe("ScheduledMaintenanceStateTimelineService.isMonitorHeldInMaintenanceByAnyEvent", () => {
362
+ test("reads, as root, only the events on the monitor in a state that can hold it", async () => {
363
+ await ScheduledMaintenanceStateTimelineService.isMonitorHeldInMaintenanceByAnyEvent(
364
+ new ObjectID(MONITOR_A),
365
+ );
366
+
367
+ expect(eventFindBy).toHaveBeenCalledTimes(1);
368
+
369
+ const findBy: EventFindBy = eventFindBy.mock.calls[0]![0] as EventFindBy;
370
+
371
+ expect(
372
+ findBy.query.monitors.map((id: ObjectID): string => {
373
+ return id.toString();
374
+ }),
375
+ ).toEqual([MONITOR_A]);
376
+ // Scheduled, ended and resolved never hold anything.
377
+ expect(findBy.query.currentScheduledMaintenanceState).toEqual({
378
+ isScheduledState: false,
379
+ isEndedState: false,
380
+ isResolvedState: false,
381
+ });
382
+ expect(findBy.select).toEqual({
383
+ _id: true,
384
+ projectId: true,
385
+ currentScheduledMaintenanceState: {
386
+ _id: true,
387
+ isScheduledState: true,
388
+ isOngoingState: true,
389
+ isEndedState: true,
390
+ isResolvedState: true,
391
+ },
392
+ });
393
+ expect(findBy.props).toEqual({ isRoot: true });
394
+ });
395
+
396
+ test("no such event: not held, and no timeline is read", async () => {
397
+ storedEvents = [
398
+ {
399
+ id: OTHER_EVENT_ID,
400
+ state: "scheduled",
401
+ monitors: [MONITOR_A],
402
+ timeline: ["scheduled"],
403
+ },
404
+ {
405
+ id: THIRD_EVENT_ID,
406
+ state: "ended",
407
+ monitors: [MONITOR_A],
408
+ timeline: ["scheduled", "ongoing", "ended"],
409
+ },
410
+ ];
411
+
412
+ expect(
413
+ await ScheduledMaintenanceStateTimelineService.isMonitorHeldInMaintenanceByAnyEvent(
414
+ new ObjectID(MONITOR_A),
415
+ ),
416
+ ).toBe(false);
417
+ expect(timelineFindBy).not.toHaveBeenCalled();
418
+ });
419
+
420
+ test("an ongoing event holds it, with no timeline read even beside an event in a state of its own", async () => {
421
+ storedEvents = [
422
+ {
423
+ id: OTHER_EVENT_ID,
424
+ state: "custom",
425
+ monitors: [MONITOR_A],
426
+ timeline: ["scheduled", "custom"],
427
+ },
428
+ {
429
+ id: THIRD_EVENT_ID,
430
+ state: "ongoing",
431
+ monitors: [MONITOR_A],
432
+ timeline: ["scheduled", "ongoing"],
433
+ },
434
+ ];
435
+
436
+ expect(
437
+ await ScheduledMaintenanceStateTimelineService.isMonitorHeldInMaintenanceByAnyEvent(
438
+ new ObjectID(MONITOR_A),
439
+ ),
440
+ ).toBe(true);
441
+ expect(timelineFindBy).not.toHaveBeenCalled();
442
+ });
443
+
444
+ test.each([
445
+ [
446
+ "past ongoing and not yet ended",
447
+ true,
448
+ ["scheduled", "ongoing", "custom"],
449
+ ],
450
+ ["not yet ongoing", false, ["scheduled", "custom"]],
451
+ ["past ended", false, ["scheduled", "ongoing", "ended", "custom"]],
452
+ ] as Array<[string, boolean, Array<StateKind>]>)(
453
+ "an event in a state of the project's own %s: held is %s",
454
+ async (_label: string, isHeld: boolean, timeline: Array<StateKind>) => {
455
+ storedEvents = [
456
+ {
457
+ id: OTHER_EVENT_ID,
458
+ state: "custom",
459
+ monitors: [MONITOR_A],
460
+ timeline: timeline,
461
+ },
462
+ ];
463
+
464
+ expect(
465
+ await ScheduledMaintenanceStateTimelineService.isMonitorHeldInMaintenanceByAnyEvent(
466
+ new ObjectID(MONITOR_A),
467
+ ),
468
+ ).toBe(isHeld);
469
+
470
+ // Its own timeline, as root, held to its project, oldest first.
471
+ expect(timelineReads()).toEqual([[OTHER_EVENT_ID]]);
472
+ const findBy: TimelineFindBy = timelineFindBy.mock
473
+ .calls[0]![0] as TimelineFindBy;
474
+ expect(findBy.query.projectId.toString()).toBe(PROJECT_ID.toString());
475
+ // Which event each row belongs to, to split a read of several.
476
+ expect(findBy.select).toEqual({
477
+ _id: true,
478
+ scheduledMaintenanceId: true,
479
+ scheduledMaintenanceState: {
480
+ _id: true,
481
+ isScheduledState: true,
482
+ isOngoingState: true,
483
+ isEndedState: true,
484
+ isResolvedState: true,
485
+ },
486
+ });
487
+ expect(findBy.sort).toEqual({ startsAt: SortOrder.Ascending });
488
+ expect(findBy.limit).toBe(LIMIT_MAX);
489
+ expect(findBy.props).toEqual({ isRoot: true });
490
+ },
491
+ );
492
+
493
+ test("every event in a state of its own is replayed from one timeline read, each from its own rows", async () => {
494
+ /*
495
+ * The rows come back interleaved. Replayed as one run, OTHER's "ended"
496
+ * would land after THIRD's "ongoing" and hide that THIRD holds.
497
+ */
498
+ storedEvents = [
499
+ {
500
+ id: OTHER_EVENT_ID,
501
+ state: "custom",
502
+ monitors: [MONITOR_A],
503
+ timeline: ["scheduled", "ongoing", "ended", "custom"],
504
+ },
505
+ {
506
+ id: THIRD_EVENT_ID,
507
+ state: "custom",
508
+ monitors: [MONITOR_A],
509
+ timeline: ["scheduled", "ongoing", "custom"],
510
+ },
511
+ ];
512
+
513
+ expect(
514
+ await ScheduledMaintenanceStateTimelineService.isMonitorHeldInMaintenanceByAnyEvent(
515
+ new ObjectID(MONITOR_A),
516
+ ),
517
+ ).toBe(true);
518
+ expect(timelineReads()).toEqual([[OTHER_EVENT_ID, THIRD_EVENT_ID]]);
519
+ });
520
+
521
+ test("an event already known not to hold is not read again, and one known to hold answers with no read", async () => {
522
+ storedEvents = [
523
+ {
524
+ id: OTHER_EVENT_ID,
525
+ state: "custom",
526
+ monitors: [MONITOR_A],
527
+ timeline: ["scheduled", "custom"],
528
+ },
529
+ {
530
+ id: THIRD_EVENT_ID,
531
+ state: "custom",
532
+ monitors: [MONITOR_A],
533
+ timeline: ["scheduled", "custom"],
534
+ },
535
+ ];
536
+
537
+ const holdingByEventId: Map<string, boolean> = new Map<string, boolean>([
538
+ [OTHER_EVENT_ID, false],
539
+ ]);
540
+
541
+ expect(
542
+ await ScheduledMaintenanceStateTimelineService.isMonitorHeldInMaintenanceByAnyEvent(
543
+ new ObjectID(MONITOR_A),
544
+ holdingByEventId,
545
+ ),
546
+ ).toBe(false);
547
+ expect(timelineReads()).toEqual([[THIRD_EVENT_ID]]);
548
+ // What was replayed is remembered for the next monitor.
549
+ expect(holdingByEventId.get(THIRD_EVENT_ID)).toBe(false);
550
+
551
+ timelineFindBy.mockClear();
552
+ holdingByEventId.set(THIRD_EVENT_ID, true);
553
+
554
+ expect(
555
+ await ScheduledMaintenanceStateTimelineService.isMonitorHeldInMaintenanceByAnyEvent(
556
+ new ObjectID(MONITOR_A),
557
+ holdingByEventId,
558
+ ),
559
+ ).toBe(true);
560
+ expect(timelineFindBy).not.toHaveBeenCalled();
561
+ });
562
+
563
+ test("many events in a state of their own are read in batches that stay well inside one read's row limit", async () => {
564
+ const eventIds: Array<string> = manyEventIds(250);
565
+
566
+ storedEvents = eventIds.map((id: string): StoredEvent => {
567
+ // Say "Cancelled", reached from Scheduled: it never held anything.
568
+ return {
569
+ id: id,
570
+ state: "custom",
571
+ monitors: [MONITOR_A],
572
+ timeline: ["scheduled", "custom"],
573
+ };
574
+ });
575
+
576
+ expect(
577
+ await ScheduledMaintenanceStateTimelineService.isMonitorHeldInMaintenanceByAnyEvent(
578
+ new ObjectID(MONITOR_A),
579
+ ),
580
+ ).toBe(false);
581
+
582
+ const reads: Array<Array<string>> = timelineReads();
583
+
584
+ expect(reads).toHaveLength(3);
585
+ for (const read of reads) {
586
+ expect(read.length).toBeLessThanOrEqual(100);
587
+ }
588
+ // Each event read exactly once.
589
+ expect(reads.flat().sort()).toEqual([...eventIds].sort());
590
+ });
591
+
592
+ test("the batches stop at the first one with an event that holds", async () => {
593
+ const eventIds: Array<string> = manyEventIds(250);
594
+
595
+ storedEvents = eventIds.map((id: string, index: number): StoredEvent => {
596
+ return {
597
+ id: id,
598
+ state: "custom",
599
+ monitors: [MONITOR_A],
600
+ timeline:
601
+ index === 0
602
+ ? ["scheduled", "ongoing", "custom"]
603
+ : ["scheduled", "custom"],
604
+ };
605
+ });
606
+
607
+ expect(
608
+ await ScheduledMaintenanceStateTimelineService.isMonitorHeldInMaintenanceByAnyEvent(
609
+ new ObjectID(MONITOR_A),
610
+ ),
611
+ ).toBe(true);
612
+ expect(timelineFindBy).toHaveBeenCalledTimes(1);
613
+ });
614
+ });
615
+
616
+ describe("ScheduledMaintenanceStateTimelineService.isScheduledMaintenanceHoldingMonitors", () => {
617
+ test.each([
618
+ ["ongoing", true],
619
+ ["scheduled", false],
620
+ ["ended", false],
621
+ ["resolved", false],
622
+ ] as Array<[StateKind, boolean]>)(
623
+ "a built-in %s state answers for itself (%s), with no timeline read",
624
+ async (kind: StateKind, isHolding: boolean) => {
625
+ expect(
626
+ await ScheduledMaintenanceStateTimelineService.isScheduledMaintenanceHoldingMonitors(
627
+ {
628
+ scheduledMaintenanceId: new ObjectID(OTHER_EVENT_ID),
629
+ projectId: PROJECT_ID,
630
+ currentState: state(kind),
631
+ },
632
+ ),
633
+ ).toBe(isHolding);
634
+ expect(timelineFindBy).not.toHaveBeenCalled();
635
+ },
636
+ );
637
+
638
+ test("with no state or no project, nothing is held", async () => {
639
+ expect(
640
+ await ScheduledMaintenanceStateTimelineService.isScheduledMaintenanceHoldingMonitors(
641
+ {
642
+ scheduledMaintenanceId: new ObjectID(OTHER_EVENT_ID),
643
+ projectId: PROJECT_ID,
644
+ currentState: undefined,
645
+ },
646
+ ),
647
+ ).toBe(false);
648
+ expect(
649
+ await ScheduledMaintenanceStateTimelineService.isScheduledMaintenanceHoldingMonitors(
650
+ {
651
+ scheduledMaintenanceId: new ObjectID(OTHER_EVENT_ID),
652
+ projectId: undefined,
653
+ currentState: state("custom"),
654
+ },
655
+ ),
656
+ ).toBe(false);
657
+ expect(timelineFindBy).not.toHaveBeenCalled();
658
+ });
659
+ });
660
+
661
+ describe("ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors: what another event still holds", () => {
662
+ test("a monitor no other event holds is released and put back in the operational status", async () => {
663
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
664
+ releasingEvent([MONITOR_A, MONITOR_B]),
665
+ );
666
+
667
+ expect(releasedMonitorIds()).toEqual([MONITOR_A, MONITOR_B]);
668
+ expect(operationalMonitorIds()).toEqual([MONITOR_A, MONITOR_B]);
669
+ });
670
+
671
+ test("a monitor another event holds while ongoing is left in maintenance", async () => {
672
+ storedEvents = [
673
+ {
674
+ id: OTHER_EVENT_ID,
675
+ state: "ongoing",
676
+ monitors: [MONITOR_A],
677
+ timeline: ["scheduled", "ongoing"],
678
+ },
679
+ ];
680
+
681
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
682
+ releasingEvent([MONITOR_A, MONITOR_B]),
683
+ );
684
+
685
+ expect(releasedMonitorIds()).toEqual([MONITOR_B]);
686
+ expect(operationalMonitorIds()).toEqual([MONITOR_B]);
687
+ });
688
+
689
+ test("a monitor another event holds in a state of the project's own past ongoing is left in maintenance", async () => {
690
+ // Say "Verifying", between Ongoing and Ended: it released nothing.
691
+ storedEvents = [
692
+ {
693
+ id: OTHER_EVENT_ID,
694
+ state: "custom",
695
+ monitors: [MONITOR_A],
696
+ timeline: ["scheduled", "ongoing", "custom"],
697
+ },
698
+ ];
699
+
700
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
701
+ releasingEvent([MONITOR_A, MONITOR_B]),
702
+ );
703
+
704
+ expect(releasedMonitorIds()).toEqual([MONITOR_B]);
705
+ expect(operationalMonitorIds()).toEqual([MONITOR_B]);
706
+ });
707
+
708
+ test("a monitor whose other event is in a state of its own before ongoing is released", async () => {
709
+ // Say "Preparing", between Scheduled and Ongoing: it holds nothing yet.
710
+ storedEvents = [
711
+ {
712
+ id: OTHER_EVENT_ID,
713
+ state: "custom",
714
+ monitors: [MONITOR_A],
715
+ timeline: ["scheduled", "custom"],
716
+ },
717
+ ];
718
+
719
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
720
+ releasingEvent([MONITOR_A]),
721
+ );
722
+
723
+ expect(releasedMonitorIds()).toEqual([MONITOR_A]);
724
+ expect(operationalMonitorIds()).toEqual([MONITOR_A]);
725
+ });
726
+ });
727
+
728
+ /*
729
+ * Events in a state of their project's own pile up on the monitors: a
730
+ * "Cancelled" state reached from Scheduled is none of the built-in kinds, so
731
+ * every such event stays a candidate for as long as it is kept. Releasing an
732
+ * event used to replay each one's timeline again for every monitor it shares,
733
+ * one read per event per monitor, inside the request that ended or deleted
734
+ * the event.
735
+ */
736
+ describe("ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors: each event in a state of its own is replayed once per release", () => {
737
+ test("events shared by the monitors let go of are read together once, not once per monitor", async () => {
738
+ storedEvents = [OTHER_EVENT_ID, THIRD_EVENT_ID].map(
739
+ (id: string): StoredEvent => {
740
+ return {
741
+ id: id,
742
+ state: "custom",
743
+ monitors: [MONITOR_A, MONITOR_B, MONITOR_C],
744
+ timeline: ["scheduled", "custom"],
745
+ };
746
+ },
747
+ );
748
+
749
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
750
+ releasingEvent([MONITOR_A, MONITOR_B, MONITOR_C]),
751
+ );
752
+
753
+ expect(releasedMonitorIds()).toEqual([MONITOR_A, MONITOR_B, MONITOR_C]);
754
+ expect(operationalMonitorIds()).toEqual([MONITOR_A, MONITOR_B, MONITOR_C]);
755
+ expect(timelineReads()).toEqual([[OTHER_EVENT_ID, THIRD_EVENT_ID]]);
756
+ });
757
+
758
+ test("an event found to hold keeps the later monitors it holds in maintenance with no further read", async () => {
759
+ storedEvents = [
760
+ {
761
+ id: OTHER_EVENT_ID,
762
+ state: "custom",
763
+ monitors: [MONITOR_A, MONITOR_B],
764
+ timeline: ["scheduled", "ongoing", "custom"],
765
+ },
766
+ ];
767
+
768
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
769
+ releasingEvent([MONITOR_A, MONITOR_B, MONITOR_C]),
770
+ );
771
+
772
+ expect(releasedMonitorIds()).toEqual([MONITOR_C]);
773
+ expect(operationalMonitorIds()).toEqual([MONITOR_C]);
774
+ expect(timelineReads()).toEqual([[OTHER_EVENT_ID]]);
775
+ });
776
+
777
+ test("a later monitor reads only the events not yet replayed", async () => {
778
+ storedEvents = [
779
+ {
780
+ id: OTHER_EVENT_ID,
781
+ state: "custom",
782
+ monitors: [MONITOR_A, MONITOR_B],
783
+ timeline: ["scheduled", "custom"],
784
+ },
785
+ {
786
+ id: THIRD_EVENT_ID,
787
+ state: "custom",
788
+ monitors: [MONITOR_B],
789
+ timeline: ["scheduled", "ongoing", "custom"],
790
+ },
791
+ ];
792
+
793
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
794
+ releasingEvent([MONITOR_A, MONITOR_B]),
795
+ );
796
+
797
+ expect(releasedMonitorIds()).toEqual([MONITOR_A]);
798
+ expect(timelineReads()).toEqual([[OTHER_EVENT_ID], [THIRD_EVENT_ID]]);
799
+ });
800
+
801
+ test("a timeline read that fails is not remembered: that monitor is left as it was and the next reads again", async () => {
802
+ storedEvents = [
803
+ {
804
+ id: OTHER_EVENT_ID,
805
+ state: "custom",
806
+ monitors: [MONITOR_A, MONITOR_B],
807
+ timeline: ["scheduled", "custom"],
808
+ },
809
+ ];
810
+ timelineFindBy.mockRejectedValueOnce(
811
+ new Error("connection reset") as never,
812
+ );
813
+
814
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
815
+ releasingEvent([MONITOR_A, MONITOR_B]),
816
+ );
817
+
818
+ expect(releasedMonitorIds()).toEqual([MONITOR_B]);
819
+ expect(operationalMonitorIds()).toEqual([MONITOR_B]);
820
+ expect(timelineReads()).toEqual([[OTHER_EVENT_ID], [OTHER_EVENT_ID]]);
821
+ expect(
822
+ loggerError.mock.calls.some((call: Array<unknown>): boolean => {
823
+ return ((call[1] || {}) as LogAttributes)["monitorId"] === MONITOR_A;
824
+ }),
825
+ ).toBe(true);
826
+ });
827
+
828
+ test("what one release worked out is not kept for the next", async () => {
829
+ storedEvents = [
830
+ {
831
+ id: OTHER_EVENT_ID,
832
+ state: "custom",
833
+ monitors: [MONITOR_A],
834
+ timeline: ["scheduled", "custom"],
835
+ },
836
+ ];
837
+
838
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
839
+ releasingEvent([MONITOR_A]),
840
+ );
841
+
842
+ expect(releasedMonitorIds()).toEqual([MONITOR_A]);
843
+
844
+ // Since then it went ongoing and on to another state of its own.
845
+ storedEvents[0]!.timeline = ["scheduled", "custom", "ongoing", "custom"];
846
+ monitorUpdateOneById.mockClear();
847
+
848
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
849
+ releasingEvent([MONITOR_A]),
850
+ );
851
+
852
+ expect(releasedMonitorIds()).toEqual([]);
853
+ expect(timelineReads()).toEqual([[OTHER_EVENT_ID], [OTHER_EVENT_ID]]);
854
+ });
855
+ });
856
+
857
+ describe("ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors: one monitor failing", () => {
858
+ function expectFailureLoggedFor(monitorId: string): void {
859
+ const attributes: Array<LogAttributes> = loggerError.mock.calls
860
+ .map((call: Array<unknown>): LogAttributes => {
861
+ return (call[1] || {}) as LogAttributes;
862
+ })
863
+ .filter((logAttributes: LogAttributes): boolean => {
864
+ return logAttributes["monitorId"] === monitorId;
865
+ });
866
+
867
+ expect(attributes.length).toBeGreaterThan(0);
868
+
869
+ for (const logAttributes of attributes) {
870
+ expect(logAttributes.projectId).toBe(PROJECT_ID.toString());
871
+ expect(logAttributes["scheduledMaintenanceId"]).toBe(RELEASING_EVENT_ID);
872
+ }
873
+ }
874
+
875
+ test.each([
876
+ [
877
+ "the status timeline lock",
878
+ new ServerException(MONITOR_STATUS_TIMELINE_LOCK_ERROR_MESSAGE),
879
+ ],
880
+ [
881
+ "a status set by a concurrent writer",
882
+ new BadDataException(MONITOR_STATUS_SAME_AS_PREVIOUS_ERROR_MESSAGE),
883
+ ],
884
+ ] as Array<[string, Error]>)(
885
+ "the status write failing on %s leaves the remaining monitors released",
886
+ async (_label: string, error: Error) => {
887
+ statusTimelineCreate.mockRejectedValueOnce(error as never);
888
+
889
+ await expect(
890
+ ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
891
+ releasingEvent([MONITOR_A, MONITOR_B, MONITOR_C]),
892
+ ),
893
+ ).resolves.toBeUndefined();
894
+
895
+ expect(releasedMonitorIds()).toEqual([MONITOR_A, MONITOR_B, MONITOR_C]);
896
+ // All three were tried; only A's write failed.
897
+ expect(statusTimelineCreate).toHaveBeenCalledTimes(3);
898
+ expectFailureLoggedFor(MONITOR_A);
899
+ },
900
+ );
901
+
902
+ test("the flag write failing leaves the remaining monitors released", async () => {
903
+ monitorUpdateOneById.mockRejectedValueOnce(
904
+ new Error("connection reset") as never,
905
+ );
906
+
907
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
908
+ releasingEvent([MONITOR_A, MONITOR_B]),
909
+ );
910
+
911
+ // A was tried; its status was never written without its flag.
912
+ expect(monitorUpdateOneById).toHaveBeenCalledTimes(2);
913
+ expect(operationalMonitorIds()).toEqual([MONITOR_B]);
914
+ expectFailureLoggedFor(MONITOR_A);
915
+ });
916
+
917
+ test("asking whether another event holds one monitor failing leaves the remaining monitors released", async () => {
918
+ eventFindBy.mockRejectedValueOnce(new Error("connection reset") as never);
919
+
920
+ await ScheduledMaintenanceStateTimelineService.enableActiveMonitoringForMonitors(
921
+ releasingEvent([MONITOR_A, MONITOR_B]),
922
+ );
923
+
924
+ // A is left as it was rather than released on a guess.
925
+ expect(releasedMonitorIds()).toEqual([MONITOR_B]);
926
+ expect(operationalMonitorIds()).toEqual([MONITOR_B]);
927
+ expectFailureLoggedFor(MONITOR_A);
928
+ });
929
+ });
930
+
931
+ describe("ScheduledMaintenanceService.onDeleteSuccess: deleting an event releases what no other event holds", () => {
932
+ test("a monitor another event holds in a state of the project's own past ongoing stays in maintenance", async () => {
933
+ jest
934
+ .spyOn(ScheduledMaintenanceMeasurementService, "getMetricNamesForProject")
935
+ .mockResolvedValue([] as never);
936
+ jest
937
+ .spyOn(MeasurementMetricWriter, "tombstoneAll")
938
+ .mockResolvedValue(undefined as never);
939
+
940
+ // The deleted event is gone; "Verifying" still holds A.
941
+ storedEvents = [
942
+ {
943
+ id: OTHER_EVENT_ID,
944
+ state: "custom",
945
+ monitors: [MONITOR_A],
946
+ timeline: ["scheduled", "ongoing", "custom"],
947
+ },
948
+ ];
949
+
950
+ await (
951
+ ScheduledMaintenanceService as unknown as {
952
+ onDeleteSuccess: OnDeleteSuccessFunction;
953
+ }
954
+ ).onDeleteSuccess(
955
+ {
956
+ deleteBy: {
957
+ query: { _id: RELEASING_EVENT_ID },
958
+ props: { isRoot: true },
959
+ limit: 1,
960
+ skip: 0,
961
+ },
962
+ carryForward: {
963
+ scheduledMaintenanceEvents: [releasingEvent([MONITOR_A, MONITOR_B])],
964
+ },
965
+ },
966
+ [new ObjectID(RELEASING_EVENT_ID)],
967
+ );
968
+
969
+ expect(releasedMonitorIds()).toEqual([MONITOR_B]);
970
+ expect(operationalMonitorIds()).toEqual([MONITOR_B]);
971
+ });
972
+ });