@oneuptime/common 14.0.3 → 14.0.6

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 (663) hide show
  1. package/Models/DatabaseModels/Alert.ts +55 -0
  2. package/Models/DatabaseModels/DatabaseServer.ts +1650 -0
  3. package/Models/DatabaseModels/DatabaseServerEndpoint.ts +540 -0
  4. package/Models/DatabaseModels/DatabaseServerFeed.ts +625 -0
  5. package/Models/DatabaseModels/DatabaseServerLabelRule.ts +514 -0
  6. package/Models/DatabaseModels/DatabaseServerOwnerRule.ts +596 -0
  7. package/Models/DatabaseModels/DatabaseServerOwnerTeam.ts +491 -0
  8. package/Models/DatabaseModels/DatabaseServerOwnerUser.ts +490 -0
  9. package/Models/DatabaseModels/Incident.ts +55 -0
  10. package/Models/DatabaseModels/Index.ts +16 -0
  11. package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +26 -0
  12. package/Models/DatabaseModels/ScheduledMaintenance.ts +55 -0
  13. package/Models/DatabaseModels/TelemetryException.ts +2 -0
  14. package/Models/DatabaseModels/UserProjectSsoConsent.ts +146 -0
  15. package/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.ts +673 -0
  16. package/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.ts +51 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +4 -0
  18. package/Server/Middleware/MultipartFormData.ts +1 -1
  19. package/Server/Middleware/TelemetryIngest.ts +15 -8
  20. package/Server/Services/AlertReminderRuleService.ts +100 -24
  21. package/Server/Services/DatabaseServerEndpointService.ts +895 -0
  22. package/Server/Services/DatabaseServerFeedService.ts +128 -0
  23. package/Server/Services/DatabaseServerLabelRuleEngineService.ts +340 -0
  24. package/Server/Services/DatabaseServerLabelRuleService.ts +14 -0
  25. package/Server/Services/DatabaseServerOwnerRuleEngineService.ts +425 -0
  26. package/Server/Services/DatabaseServerOwnerRuleService.ts +14 -0
  27. package/Server/Services/DatabaseServerOwnerTeamService.ts +180 -0
  28. package/Server/Services/DatabaseServerOwnerUserService.ts +173 -0
  29. package/Server/Services/DatabaseServerService.ts +4546 -0
  30. package/Server/Services/DatabaseService.ts +14 -8
  31. package/Server/Services/DockerResourceService.ts +43 -2
  32. package/Server/Services/IncidentReminderRuleService.ts +103 -23
  33. package/Server/Services/Index.ts +16 -0
  34. package/Server/Services/InventoryItemService.ts +113 -5
  35. package/Server/Services/IoTDeviceService.ts +145 -37
  36. package/Server/Services/MetricService.ts +1 -1
  37. package/Server/Services/OpenTelemetryIngestService.ts +19 -0
  38. package/Server/Services/PodmanResourceService.ts +43 -2
  39. package/Server/Services/ScheduledMaintenanceReminderRuleService.ts +107 -23
  40. package/Server/Services/ServiceService.ts +1 -1
  41. package/Server/Services/TelemetryUsageBillingService.ts +29 -2
  42. package/Server/Services/UserProjectSsoConsentService.ts +74 -0
  43. package/Server/Services/UserService.ts +14 -1
  44. package/Server/Types/Database/Permissions/OwnerTableRegistry.ts +18 -0
  45. package/Server/Types/Database/QueryHelper.ts +84 -2
  46. package/Server/Utils/AI/Toolbox/MetricTools.ts +13 -2
  47. package/Server/Utils/AI/Toolbox/ResourceTools.ts +81 -2
  48. package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +1 -1
  49. package/Server/Utils/Database/AffectedResourceRelations.ts +6 -0
  50. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +33 -0
  51. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +192 -9
  52. package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
  53. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +2264 -755
  54. package/Server/Utils/Monitor/MonitorIncident.ts +28 -1
  55. package/Server/Utils/Monitor/MonitorMaintenanceSuppression.ts +268 -6
  56. package/Server/Utils/Monitor/MonitorResource.ts +47 -11
  57. package/Server/Utils/Monitor/MonitorResourceContext.ts +1 -0
  58. package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +239 -21
  59. package/Server/Utils/Monitor/PerSeriesResolutionRootCause.ts +43 -0
  60. package/Server/Utils/Monitor/SeriesResourceLabels.ts +291 -0
  61. package/Server/Utils/Monitor/SeriesResourceLinker.ts +111 -12
  62. package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +25 -0
  63. package/Server/Utils/StartServer.ts +26 -6
  64. package/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.ts +2 -0
  65. package/Server/Utils/Telemetry/DatabaseEndpointDiscovery.ts +688 -0
  66. package/Server/Utils/Telemetry/EntityRegistry.ts +5 -1
  67. package/Server/Utils/Telemetry/LogExceptionExtractor.ts +18 -1
  68. package/Server/Utils/Telemetry/ResourceEntityFilter.ts +175 -12
  69. package/Server/Utils/Telemetry/ResourceFacetResolver.ts +12 -0
  70. package/Server/Utils/Telemetry/ServiceDependencyDiscovery.ts +606 -36
  71. package/Server/Utils/Telemetry/Telemetry.ts +16 -0
  72. package/Server/Utils/Telemetry/TelemetryEntity.ts +22 -22
  73. package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +16 -10
  74. package/Tests/App/Accounts/SignedInRedirect.test.tsx +241 -0
  75. package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +221 -4
  76. package/Tests/App/Dashboard/AIInsightsResourcePageContext.test.ts +42 -0
  77. package/Tests/App/Dashboard/AffectedResourcesDisplay.test.tsx +100 -0
  78. package/Tests/App/Dashboard/AffectedResourcesPicker.test.tsx +86 -0
  79. package/Tests/App/Dashboard/AffectedResourcesPickerHiddenTypes.test.tsx +2 -0
  80. package/Tests/App/Dashboard/AffectedResourcesSummaryStep.test.tsx +19 -0
  81. package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +14 -0
  82. package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +26 -1
  83. package/Tests/App/Dashboard/DashboardAppSsoError.test.tsx +501 -0
  84. package/Tests/App/Dashboard/DashboardHeaderRequests.test.tsx +64 -1
  85. package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +178 -18
  86. package/Tests/App/Dashboard/DashboardMasterPageSsoRedirect.test.tsx +444 -0
  87. package/Tests/App/Dashboard/DashboardNavBarForeignHiddenRule.test.tsx +573 -0
  88. package/Tests/App/Dashboard/DatabaseChartAxes.test.tsx +431 -0
  89. package/Tests/App/Dashboard/DatabaseContainerDetailBadge.test.tsx +249 -0
  90. package/Tests/App/Dashboard/DatabaseDashboardE2EFixes.test.ts +681 -0
  91. package/Tests/App/Dashboard/DatabaseDocumentationMarkdown.test.ts +1801 -0
  92. package/Tests/App/Dashboard/DatabaseFleetSummaryRefresh.test.tsx +280 -0
  93. package/Tests/App/Dashboard/DatabaseFormHelpers.test.tsx +321 -0
  94. package/Tests/App/Dashboard/DatabaseMetricMonitorLink.test.tsx +795 -0
  95. package/Tests/App/Dashboard/DatabaseOverviewHeader.test.tsx +580 -0
  96. package/Tests/App/Dashboard/DatabaseRecommendationsEngineMetrics.test.ts +270 -0
  97. package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +1486 -0
  98. package/Tests/App/Dashboard/DatabaseServerEngineMetricsFilter.test.ts +223 -0
  99. package/Tests/App/Dashboard/DatabaseServerPresentation.test.ts +435 -0
  100. package/Tests/App/Dashboard/DatabaseServerSummary.test.ts +186 -0
  101. package/Tests/App/Dashboard/DatabaseServerTelemetryQueries.test.ts +1553 -0
  102. package/Tests/App/Dashboard/DatabaseTableTruncation.test.tsx +326 -0
  103. package/Tests/App/Dashboard/DatabaseTelemetryScope.test.ts +512 -0
  104. package/Tests/App/Dashboard/DatabaseTelemetryTabs.test.tsx +774 -0
  105. package/Tests/App/Dashboard/DatabaseViewLayout.test.tsx +233 -0
  106. package/Tests/App/Dashboard/DatabaseWorkloadBadge.test.tsx +972 -0
  107. package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +5 -0
  108. package/Tests/App/Dashboard/DiscoveryReviewUnnamedHostHint.test.tsx +1061 -0
  109. package/Tests/App/Dashboard/EventOverlayScope.test.ts +147 -0
  110. package/Tests/App/Dashboard/EventOverlayScopeHook.test.tsx +1 -0
  111. package/Tests/App/Dashboard/LogsResourceFacetChips.test.tsx +1 -0
  112. package/Tests/App/Dashboard/MetricsViewerDrillDown.test.tsx +310 -0
  113. package/Tests/App/Dashboard/MetricsViewerEntityScopedRows.test.tsx +495 -0
  114. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +132 -1
  115. package/Tests/App/Dashboard/NavigationSearchKeywords.test.tsx +5 -0
  116. package/Tests/App/Dashboard/NetworkTopologyExplorerSearch.test.tsx +1267 -0
  117. package/Tests/App/Dashboard/ProductOrientation.test.tsx +4 -3
  118. package/Tests/App/Dashboard/RecommendationsLoadedContext.test.tsx +349 -0
  119. package/Tests/App/Dashboard/ResourceArchiveSettings.test.tsx +62 -0
  120. package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
  121. package/Tests/App/Dashboard/ResourceFeedPagesFilterSort.test.tsx +14 -0
  122. package/Tests/App/Dashboard/ResourceOverviewTileTooltips.test.tsx +4 -1
  123. package/Tests/App/Dashboard/RumOverviewPage.test.tsx +127 -0
  124. package/Tests/App/Dashboard/ScheduledMaintenanceOverviewPage.test.tsx +2 -0
  125. package/Tests/App/Dashboard/ServiceMapDatabaseLink.test.tsx +1085 -0
  126. package/Tests/App/Dashboard/SiteSearchBox.test.tsx +2110 -0
  127. package/Tests/App/Dashboard/TelemetryMetricsSignals.test.ts +168 -8
  128. package/Tests/App/Dashboard/TelemetryResourceRetentionSettings.test.tsx +10 -0
  129. package/Tests/App/Dashboard/TracesResourceFacets.test.tsx +2 -1
  130. package/Tests/App/StatusPage/StatusPageHeaderForeignHiddenRule.test.tsx +284 -0
  131. package/Tests/ClassStrings.ts +178 -0
  132. package/Tests/Fixtures/MobilePasskeyUser.json +1 -2
  133. package/Tests/ForeignHiddenRuleGuard.ts +2152 -0
  134. package/Tests/Models/DatabaseModels/DatabaseServerModels.test.ts +2250 -0
  135. package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +24 -5
  136. package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +4 -2
  137. package/Tests/RenderedMarkup.ts +518 -0
  138. package/Tests/ResponsiveVisibility.test.ts +490 -4
  139. package/Tests/ResponsiveVisibility.ts +505 -52
  140. package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +3 -0
  141. package/Tests/Server/Infrastructure/Postgres/AddDatabaseServerTablesMigration.test.ts +541 -0
  142. package/Tests/Server/Infrastructure/Postgres/AddUserProjectSsoConsentMigration.test.ts +234 -0
  143. package/Tests/Server/Services/ContainerSnapshotUpsertPostgres.test.ts +319 -0
  144. package/Tests/Server/Services/CrossProjectRelationWriteGuard.test.ts +13 -0
  145. package/Tests/Server/Services/DatabaseServerEndpointService.test.ts +2033 -0
  146. package/Tests/Server/Services/DatabaseServerFeedAndOwnerServices.test.ts +529 -0
  147. package/Tests/Server/Services/DatabaseServerLifecycle.test.ts +3302 -0
  148. package/Tests/Server/Services/DatabaseServerRegistries.test.ts +603 -0
  149. package/Tests/Server/Services/DatabaseServerRuleEngines.test.ts +754 -0
  150. package/Tests/Server/Services/DatabaseServerService.test.ts +3558 -0
  151. package/Tests/Server/Services/DatabaseServerSqlPostgres.test.ts +2045 -0
  152. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedPostgres.test.ts +362 -0
  153. package/Tests/Server/Services/DatabaseServiceUpdateColumnsIfUnlockedReply.test.ts +210 -0
  154. package/Tests/Server/Services/HookFreeWriteLengthClamp.test.ts +18 -0
  155. package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +2 -0
  156. package/Tests/Server/Services/IncomingCallPolicyEscalationRuleTarget.test.ts +225 -0
  157. package/Tests/Server/Services/InventoryItemDisplayName.test.ts +280 -2
  158. package/Tests/Server/Services/InventoryUpsertSchemaParity.test.ts +8 -2
  159. package/Tests/Server/Services/IoTDeviceLatestMetricsPostgres.test.ts +689 -0
  160. package/Tests/Server/Services/IoTDeviceService.test.ts +379 -0
  161. package/Tests/Server/Services/LatestMetricsValuesShape.test.ts +360 -0
  162. package/Tests/Server/Services/LlmModelPriceService.test.ts +402 -0
  163. package/Tests/Server/Services/MetricServiceAggregate.test.ts +12 -0
  164. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +7 -1
  165. package/Tests/Server/Services/OwnerRowUniqueness.test.ts +61 -9
  166. package/Tests/Server/Services/ReminderRuleLookupPostgres.test.ts +2250 -0
  167. package/Tests/Server/Services/ReminderRuleRefreshOpenSubjectsPaging.test.ts +653 -0
  168. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +68 -20
  169. package/Tests/Server/Services/SavedViewDefaults.test.ts +325 -0
  170. package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +3 -3
  171. package/Tests/Server/Services/StatusPageCertificateService.test.ts +169 -0
  172. package/Tests/Server/Services/TeamComplianceSettingService.test.ts +191 -0
  173. package/Tests/Server/Services/TelemetryResourceFacetCounting.test.ts +9 -0
  174. package/Tests/Server/Services/UserProjectSsoConsentPostgres.test.ts +153 -0
  175. package/Tests/Server/Services/UserProjectSsoConsentService.test.ts +192 -0
  176. package/Tests/Server/Services/UserServiceFirstMasterAdminElection.test.ts +71 -0
  177. package/Tests/Server/Services/UserSignupIdTakeoverPostgres.test.ts +238 -0
  178. package/Tests/Server/Services/WorkspaceNotificationSummaryHelpers.test.ts +592 -0
  179. package/Tests/Server/Types/Billing/PayAsYouGoMeteredAuthorization.test.ts +3 -0
  180. package/Tests/Server/Types/Database/EventOverlayScopePostgres.test.ts +1 -0
  181. package/Tests/Server/Types/Database/FeedEventTypeFilterQuery.test.ts +11 -0
  182. package/Tests/Server/Types/Database/Permissions/InfrastructureOwnershipScope.test.ts +8 -0
  183. package/Tests/Server/Types/Database/QueryHelperBooleanForCriteriaBackedRule.test.ts +1094 -0
  184. package/Tests/Server/Types/Database/QueryHelperSearchAllWords.test.ts +651 -0
  185. package/Tests/Server/Utils/AI/AlertWriteTools.test.ts +256 -0
  186. package/Tests/Server/Utils/AI/IncidentWriteTools.test.ts +482 -0
  187. package/Tests/Server/Utils/AI/ResourceTools.test.ts +134 -0
  188. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +54 -0
  189. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +41 -0
  190. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +279 -9
  191. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaDisplayContext.test.ts +933 -0
  192. package/Tests/Server/Utils/Monitor/DatabaseMaintenanceSuppression.test.ts +582 -0
  193. package/Tests/Server/Utils/Monitor/DatabaseServerMonitorLinking.test.ts +753 -0
  194. package/Tests/Server/Utils/Monitor/MetricDecimalThreshold.test.ts +417 -0
  195. package/Tests/Server/Utils/Monitor/MonitorActionTimestamps.test.ts +1 -0
  196. package/Tests/Server/Utils/Monitor/MonitorAlertCriteriaReferences.test.ts +1 -0
  197. package/Tests/Server/Utils/Monitor/MonitorAlertResourceLinking.test.ts +119 -0
  198. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +60 -25
  199. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorAffectedResourceEdgeCases.test.ts +1815 -0
  200. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesAffectedResources.test.ts +1447 -0
  201. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorKubernetesRatioEmail.test.ts +352 -0
  202. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformMetricUnits.test.ts +717 -0
  203. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformRawScan.test.ts +2125 -0
  204. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorPlatformSeriesAffectedResources.test.ts +1288 -0
  205. package/Tests/Server/Utils/Monitor/MonitorDependencySuppressionCreatorSkip.test.ts +1 -0
  206. package/Tests/Server/Utils/Monitor/MonitorIncidentCriteriaReferences.test.ts +1 -0
  207. package/Tests/Server/Utils/Monitor/MonitorIncidentMemberRoleMembership.test.ts +1 -0
  208. package/Tests/Server/Utils/Monitor/MonitorIncidentResourceLinking.test.ts +88 -0
  209. package/Tests/Server/Utils/Monitor/MonitorMaintenanceSuppression.test.ts +205 -0
  210. package/Tests/Server/Utils/Monitor/MonitorResourceContext.test.ts +62 -0
  211. package/Tests/Server/Utils/Monitor/MonitorResourceDatabaseMaintenance.test.ts +803 -0
  212. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +133 -3
  213. package/Tests/Server/Utils/Monitor/MonitorSummaryPersistence.test.ts +1 -0
  214. package/Tests/Server/Utils/Monitor/PerSeriesRecoveryResolution.test.ts +472 -1
  215. package/Tests/Server/Utils/Monitor/PerSeriesSecondEntityAlerting.test.ts +1 -0
  216. package/Tests/Server/Utils/Monitor/SeriesResourceLinker.test.ts +236 -2
  217. package/Tests/Server/Utils/SeriesResourceLabels.test.ts +70 -0
  218. package/Tests/Server/Utils/StartServerProtobufContentType.test.ts +54 -0
  219. package/Tests/Server/Utils/Telemetry/DatabaseEndpointDiscovery.test.ts +1895 -0
  220. package/Tests/Server/Utils/Telemetry/EntityRegistry.test.ts +7 -0
  221. package/Tests/Server/Utils/Telemetry/LogExceptionExtractorSeverityGate.test.ts +90 -0
  222. package/Tests/Server/Utils/Telemetry/ResourceEntityFilter.test.ts +570 -0
  223. package/Tests/Server/Utils/Telemetry/ResourceFacetResolver.test.ts +97 -2
  224. package/Tests/Server/Utils/Telemetry/ServiceDependencyDiscovery.test.ts +1337 -0
  225. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +295 -0
  226. package/Tests/Types/AI/AIResourceContext.test.ts +30 -0
  227. package/Tests/Types/BaseDatabase/AggregationType.test.ts +5 -1
  228. package/Tests/Types/Dashboard/KubernetesDashboardTemplate.test.ts +184 -0
  229. package/Tests/Types/Dashboard/KubernetesDashboardTemplates.test.ts +107 -0
  230. package/Tests/Types/DatabaseServer/DatabaseContainerClassifier.test.ts +3644 -0
  231. package/Tests/Types/DatabaseServer/DatabaseContainerCommand.test.ts +340 -0
  232. package/Tests/Types/DatabaseServer/DatabaseContainerCommandCorpus.ts +826 -0
  233. package/Tests/Types/DatabaseServer/DatabaseContainerCommandPostgres.test.ts +150 -0
  234. package/Tests/Types/DatabaseServer/DatabaseEndpoint.test.ts +2657 -0
  235. package/Tests/Types/DatabaseServer/DatabaseServerDiscoverySource.test.ts +64 -0
  236. package/Tests/Types/DatabaseServer/DatabaseServerEntityKeys.test.ts +952 -0
  237. package/Tests/Types/DatabaseServer/DatabaseServerMetricCatalog.test.ts +1161 -0
  238. package/Tests/Types/DatabaseServer/DatabaseSystem.test.ts +1514 -0
  239. package/Tests/Types/DatabaseServer/DatabaseTelemetryResolver.test.ts +1558 -0
  240. package/Tests/Types/Monitor/CephAlertTemplates.test.ts +255 -6
  241. package/Tests/Types/Monitor/DatabaseAlertTemplates.test.ts +2254 -0
  242. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +292 -0
  243. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +272 -0
  244. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +253 -9
  245. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +13 -5
  246. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +4 -2
  247. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +111 -2
  248. package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +61 -0
  249. package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +114 -0
  250. package/Tests/Types/Monitor/Utils/KubernetesAgentEmittedMetrics.ts +202 -0
  251. package/Tests/Types/NetworkDevice/DiscoveredHostNamingStatus.test.ts +401 -0
  252. package/Tests/Types/Telemetry/ResourceEntityFacet.test.ts +4 -1
  253. package/Tests/Types/Telemetry/ResourceFacetCatalog.test.ts +24 -2
  254. package/Tests/UI/Components/Card.test.tsx +93 -2
  255. package/Tests/UI/Components/FeedOptions.test.ts +6 -0
  256. package/Tests/UI/Components/FeedOptionsButton.test.tsx +8 -1
  257. package/Tests/UI/Components/ForeignHiddenRuleResilience.test.tsx +1147 -0
  258. package/Tests/UI/Components/HeaderRightRail.test.tsx +41 -1
  259. package/Tests/UI/Components/LogsEntityNames.test.ts +16 -1
  260. package/Tests/UI/Components/LogsFacetSidebarHiddenFacets.test.tsx +7 -3
  261. package/Tests/UI/Components/ResourceFacetConfigs.test.ts +2 -1
  262. package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
  263. package/Tests/UI/Components/SrOnlyForeignRule.test.tsx +994 -0
  264. package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +34 -1
  265. package/Tests/UI/Components/TelemetryFacetSidebarHiddenFacets.test.tsx +4 -1
  266. package/Tests/UI/ForeignHiddenRuleGuard.test.ts +1951 -0
  267. package/Tests/UI/Rum/ChunkLoader.test.ts +76 -0
  268. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +19 -1
  269. package/Tests/UI/Rum/ReplayRailDetail.test.tsx +53 -0
  270. package/Tests/UI/Rum/ReplaySignals.test.ts +54 -0
  271. package/Tests/UI/Rum/ReplayTabSwitcher.test.tsx +19 -1
  272. package/Tests/UI/TailwindPlayCdnForeignHiddenRule.test.ts +1005 -0
  273. package/Tests/UI/ThemeGroupHoverReveal.test.ts +847 -0
  274. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +70 -0
  275. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +21 -0
  276. package/Tests/UI/Utils/ModelAPISsoRedirect.test.ts +127 -0
  277. package/Tests/UI/Utils/Navigation.test.ts +102 -0
  278. package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +23 -0
  279. package/Tests/Utils/Dashboard/KubernetesClusterCpuTileRepair.test.ts +370 -0
  280. package/Tests/Utils/LabelRuleImportExport.test.ts +5 -0
  281. package/Tests/Utils/MetricUnitUtil.test.ts +227 -2
  282. package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +288 -2
  283. package/Tests/Utils/Monitor/PlatformResourceIdentityAndUnits.test.ts +1559 -0
  284. package/Tests/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.test.ts +2546 -0
  285. package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +315 -0
  286. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +336 -1
  287. package/Tests/Utils/NetworkDiscovery/NetbiosNameUtil.test.ts +244 -0
  288. package/Tests/Utils/Rules/MonitorTypeRuleCriteria.test.ts +181 -0
  289. package/Tests/Utils/ValueFormatter.test.ts +50 -0
  290. package/Tests/tsconfig.json +16 -0
  291. package/Types/AI/AIResourceContext.ts +7 -0
  292. package/Types/BaseDatabase/AggregationType.ts +7 -0
  293. package/Types/Dashboard/DashboardTemplates.ts +22 -8
  294. package/Types/DatabaseServer/DatabaseConnectionSpan.ts +77 -0
  295. package/Types/DatabaseServer/DatabaseContainerClassifier.ts +2068 -0
  296. package/Types/DatabaseServer/DatabaseContainerCommand.ts +801 -0
  297. package/Types/DatabaseServer/DatabaseEndpoint.ts +1943 -0
  298. package/Types/DatabaseServer/DatabaseServerDiscoverySource.ts +68 -0
  299. package/Types/DatabaseServer/DatabaseServerMetricCatalog.ts +966 -0
  300. package/Types/DatabaseServer/DatabaseSystem.ts +2124 -0
  301. package/Types/DatabaseServer/DatabaseTelemetryResolver.ts +555 -0
  302. package/Types/Email/EmailTemplateType.ts +1 -0
  303. package/Types/Monitor/CephAlertTemplates.ts +18 -0
  304. package/Types/Monitor/DatabaseAlertTemplates.ts +2020 -0
  305. package/Types/Monitor/KubernetesAlertTemplates.ts +64 -24
  306. package/Types/Monitor/KubernetesMetricCatalog.ts +118 -19
  307. package/Types/Monitor/MetricMonitor/MetricCriteriaContext.ts +14 -0
  308. package/Types/Monitor/MetricMonitor/MetricMonitorResponse.ts +93 -10
  309. package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +109 -8
  310. package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +36 -7
  311. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +43 -2
  312. package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +41 -6
  313. package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +26 -0
  314. package/Types/NetworkDevice/DiscoveredHostNamingStatus.ts +147 -0
  315. package/Types/Permission.ts +267 -0
  316. package/Types/Probe/ProbeApiIngestResponse.ts +8 -0
  317. package/Types/Rules/RuleCriteriaFieldRegistry.ts +10 -0
  318. package/Types/Rules/RuleRun.ts +12 -0
  319. package/Types/Rum/SessionReplayCustomEvents.ts +29 -3
  320. package/Types/Rum/WebVitals.ts +13 -0
  321. package/Types/Telemetry/EntityType.ts +23 -0
  322. package/Types/Telemetry/ResourceFacetCatalog.ts +14 -0
  323. package/Types/Telemetry/ServiceType.ts +8 -0
  324. package/UI/Components/Banner/Banner.tsx +1 -1
  325. package/UI/Components/Card/Card.tsx +1 -1
  326. package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +2 -2
  327. package/UI/Components/Charts/Line/LineChart.tsx +7 -0
  328. package/UI/Components/CommandPalette/CommandPalette.tsx +1 -1
  329. package/UI/Components/Date/StartAndEndDate.tsx +5 -5
  330. package/UI/Components/Diagram/HorizontalStepChain.tsx +1 -1
  331. package/UI/Components/Feed/FeedOptionsButton.tsx +1 -1
  332. package/UI/Components/Forms/BasicForm.tsx +1 -1
  333. package/UI/Components/Header/Header.tsx +1 -1
  334. package/UI/Components/Header/HeaderIconDropdownButton.tsx +1 -1
  335. package/UI/Components/HeaderAlert/HeaderAlert.tsx +1 -1
  336. package/UI/Components/Input/Input.tsx +11 -0
  337. package/UI/Components/LogsViewer/components/FacetValueRow.tsx +1 -1
  338. package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +1 -1
  339. package/UI/Components/Modal/ModalFooter.tsx +1 -1
  340. package/UI/Components/ModelDetail/CardModelDetail.tsx +2 -2
  341. package/UI/Components/ModelTable/BaseModelTable.tsx +4 -4
  342. package/UI/Components/Navbar/NavBar.tsx +9 -2
  343. package/UI/Components/Navbar/NavBarMenuModal.tsx +2 -2
  344. package/UI/Components/Page/Page.tsx +1 -1
  345. package/UI/Components/Pagination/Pagination.tsx +3 -3
  346. package/UI/Components/ProgressButtons/ProgressButtonItem.tsx +1 -1
  347. package/UI/Components/SideMenu/SideMenu.tsx +4 -1
  348. package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +4 -2
  349. package/UI/Components/StatusPage/ResourceList.tsx +6 -6
  350. package/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.tsx +1 -1
  351. package/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.tsx +1 -1
  352. package/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.tsx +1 -1
  353. package/UI/Components/Workflow/ComponentsModal.tsx +2 -2
  354. package/UI/Components/Workflow/StepTraceViewer.tsx +1 -1
  355. package/UI/Styles/Theme.css +8 -1
  356. package/UI/Utils/Navigation.ts +89 -42
  357. package/UI/Utils/Telemetry/TelemetryEntityNames.ts +7 -0
  358. package/UI/Utils/TelemetryService.ts +4 -3
  359. package/Utils/Dashboard/KubernetesClusterCpuTileRepair.ts +147 -0
  360. package/Utils/LabelRuleImportExport.ts +2 -0
  361. package/Utils/MetricUnitUtil.ts +103 -11
  362. package/Utils/Monitor/MetricValueFormatter.ts +66 -13
  363. package/Utils/Monitor/PlatformMetricUnitUtil.ts +215 -0
  364. package/Utils/Monitor/PlatformResourceIdentity.ts +514 -0
  365. package/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.ts +425 -0
  366. package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +32 -0
  367. package/Utils/NetworkDiscovery/NetbiosNameUtil.ts +73 -0
  368. package/Utils/Telemetry/DatabaseServerEntityKeys.ts +275 -0
  369. package/Utils/Telemetry/EntityKey.ts +176 -0
  370. package/Utils/ValueFormatter.ts +16 -0
  371. package/build/dist/Models/DatabaseModels/Alert.js +54 -0
  372. package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
  373. package/build/dist/Models/DatabaseModels/DatabaseServer.js +1683 -0
  374. package/build/dist/Models/DatabaseModels/DatabaseServer.js.map +1 -0
  375. package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js +559 -0
  376. package/build/dist/Models/DatabaseModels/DatabaseServerEndpoint.js.map +1 -0
  377. package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js +648 -0
  378. package/build/dist/Models/DatabaseModels/DatabaseServerFeed.js.map +1 -0
  379. package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js +522 -0
  380. package/build/dist/Models/DatabaseModels/DatabaseServerLabelRule.js.map +1 -0
  381. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js +603 -0
  382. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerRule.js.map +1 -0
  383. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js +506 -0
  384. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerTeam.js.map +1 -0
  385. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js +505 -0
  386. package/build/dist/Models/DatabaseModels/DatabaseServerOwnerUser.js.map +1 -0
  387. package/build/dist/Models/DatabaseModels/Incident.js +54 -0
  388. package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
  389. package/build/dist/Models/DatabaseModels/Index.js +16 -0
  390. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  391. package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
  392. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js +54 -0
  393. package/build/dist/Models/DatabaseModels/ScheduledMaintenance.js.map +1 -1
  394. package/build/dist/Models/DatabaseModels/TelemetryException.js +2 -0
  395. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  396. package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js +162 -0
  397. package/build/dist/Models/DatabaseModels/UserProjectSsoConsent.js.map +1 -0
  398. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js +240 -0
  399. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795000000000-AddDatabaseServerTables.js.map +1 -0
  400. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js +26 -0
  401. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1795100000000-AddUserProjectSsoConsent.js.map +1 -0
  402. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +4 -0
  403. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  404. package/build/dist/Server/Middleware/MultipartFormData.js +1 -1
  405. package/build/dist/Server/Middleware/TelemetryIngest.js +15 -8
  406. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  407. package/build/dist/Server/Services/AlertReminderRuleService.js +72 -23
  408. package/build/dist/Server/Services/AlertReminderRuleService.js.map +1 -1
  409. package/build/dist/Server/Services/DatabaseServerEndpointService.js +694 -0
  410. package/build/dist/Server/Services/DatabaseServerEndpointService.js.map +1 -0
  411. package/build/dist/Server/Services/DatabaseServerFeedService.js +110 -0
  412. package/build/dist/Server/Services/DatabaseServerFeedService.js.map +1 -0
  413. package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js +272 -0
  414. package/build/dist/Server/Services/DatabaseServerLabelRuleEngineService.js.map +1 -0
  415. package/build/dist/Server/Services/DatabaseServerLabelRuleService.js +13 -0
  416. package/build/dist/Server/Services/DatabaseServerLabelRuleService.js.map +1 -0
  417. package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js +340 -0
  418. package/build/dist/Server/Services/DatabaseServerOwnerRuleEngineService.js.map +1 -0
  419. package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js +13 -0
  420. package/build/dist/Server/Services/DatabaseServerOwnerRuleService.js.map +1 -0
  421. package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js +160 -0
  422. package/build/dist/Server/Services/DatabaseServerOwnerTeamService.js.map +1 -0
  423. package/build/dist/Server/Services/DatabaseServerOwnerUserService.js +153 -0
  424. package/build/dist/Server/Services/DatabaseServerOwnerUserService.js.map +1 -0
  425. package/build/dist/Server/Services/DatabaseServerService.js +3272 -0
  426. package/build/dist/Server/Services/DatabaseServerService.js.map +1 -0
  427. package/build/dist/Server/Services/DatabaseService.js +8 -4
  428. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  429. package/build/dist/Server/Services/DockerResourceService.js +30 -3
  430. package/build/dist/Server/Services/DockerResourceService.js.map +1 -1
  431. package/build/dist/Server/Services/IncidentReminderRuleService.js +73 -23
  432. package/build/dist/Server/Services/IncidentReminderRuleService.js.map +1 -1
  433. package/build/dist/Server/Services/Index.js +16 -0
  434. package/build/dist/Server/Services/Index.js.map +1 -1
  435. package/build/dist/Server/Services/InventoryItemService.js +88 -5
  436. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  437. package/build/dist/Server/Services/IoTDeviceService.js +111 -24
  438. package/build/dist/Server/Services/IoTDeviceService.js.map +1 -1
  439. package/build/dist/Server/Services/MetricService.js +1 -1
  440. package/build/dist/Server/Services/OpenTelemetryIngestService.js +24 -9
  441. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  442. package/build/dist/Server/Services/PodmanResourceService.js +30 -3
  443. package/build/dist/Server/Services/PodmanResourceService.js.map +1 -1
  444. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js +75 -23
  445. package/build/dist/Server/Services/ScheduledMaintenanceReminderRuleService.js.map +1 -1
  446. package/build/dist/Server/Services/ServiceService.js +1 -1
  447. package/build/dist/Server/Services/ServiceService.js.map +1 -1
  448. package/build/dist/Server/Services/TelemetryUsageBillingService.js +23 -2
  449. package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
  450. package/build/dist/Server/Services/UserProjectSsoConsentService.js +79 -0
  451. package/build/dist/Server/Services/UserProjectSsoConsentService.js.map +1 -0
  452. package/build/dist/Server/Services/UserService.js +13 -1
  453. package/build/dist/Server/Services/UserService.js.map +1 -1
  454. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js +18 -0
  455. package/build/dist/Server/Types/Database/Permissions/OwnerTableRegistry.js.map +1 -1
  456. package/build/dist/Server/Types/Database/QueryHelper.js +73 -2
  457. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  458. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js +13 -2
  459. package/build/dist/Server/Utils/AI/Toolbox/MetricTools.js.map +1 -1
  460. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js +58 -1
  461. package/build/dist/Server/Utils/AI/Toolbox/ResourceTools.js.map +1 -1
  462. package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +1 -1
  463. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js +6 -0
  464. package/build/dist/Server/Utils/Database/AffectedResourceRelations.js.map +1 -1
  465. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +31 -0
  466. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  467. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +135 -14
  468. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  469. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +22 -6
  470. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  471. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +1489 -524
  472. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  473. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +22 -6
  474. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  475. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js +196 -6
  476. package/build/dist/Server/Utils/Monitor/MonitorMaintenanceSuppression.js.map +1 -1
  477. package/build/dist/Server/Utils/Monitor/MonitorResource.js +36 -9
  478. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  479. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js +1 -0
  480. package/build/dist/Server/Utils/Monitor/MonitorResourceContext.js.map +1 -1
  481. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +175 -17
  482. package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
  483. package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js +29 -0
  484. package/build/dist/Server/Utils/Monitor/PerSeriesResolutionRootCause.js.map +1 -0
  485. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js +211 -0
  486. package/build/dist/Server/Utils/Monitor/SeriesResourceLabels.js.map +1 -1
  487. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js +82 -10
  488. package/build/dist/Server/Utils/Monitor/SeriesResourceLinker.js.map +1 -1
  489. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +25 -0
  490. package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -1
  491. package/build/dist/Server/Utils/StartServer.js +20 -4
  492. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  493. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js +2 -0
  494. package/build/dist/Server/Utils/TeamMember/ProjectLeaveResourceCleanup.js.map +1 -1
  495. package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js +442 -0
  496. package/build/dist/Server/Utils/Telemetry/DatabaseEndpointDiscovery.js.map +1 -0
  497. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +5 -1
  498. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  499. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js +15 -1
  500. package/build/dist/Server/Utils/Telemetry/LogExceptionExtractor.js.map +1 -1
  501. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js +121 -5
  502. package/build/dist/Server/Utils/Telemetry/ResourceEntityFilter.js.map +1 -1
  503. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js +12 -0
  504. package/build/dist/Server/Utils/Telemetry/ResourceFacetResolver.js.map +1 -1
  505. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js +438 -36
  506. package/build/dist/Server/Utils/Telemetry/ServiceDependencyDiscovery.js.map +1 -1
  507. package/build/dist/Server/Utils/Telemetry/Telemetry.js +12 -0
  508. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  509. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +17 -18
  510. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  511. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +16 -10
  512. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -1
  513. package/build/dist/Types/AI/AIResourceContext.js +7 -0
  514. package/build/dist/Types/AI/AIResourceContext.js.map +1 -1
  515. package/build/dist/Types/BaseDatabase/AggregationType.js +7 -0
  516. package/build/dist/Types/BaseDatabase/AggregationType.js.map +1 -1
  517. package/build/dist/Types/Dashboard/DashboardTemplates.js +22 -8
  518. package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
  519. package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js +71 -0
  520. package/build/dist/Types/DatabaseServer/DatabaseConnectionSpan.js.map +1 -0
  521. package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js +1462 -0
  522. package/build/dist/Types/DatabaseServer/DatabaseContainerClassifier.js.map +1 -0
  523. package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js +691 -0
  524. package/build/dist/Types/DatabaseServer/DatabaseContainerCommand.js.map +1 -0
  525. package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js +1407 -0
  526. package/build/dist/Types/DatabaseServer/DatabaseEndpoint.js.map +1 -0
  527. package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js +60 -0
  528. package/build/dist/Types/DatabaseServer/DatabaseServerDiscoverySource.js.map +1 -0
  529. package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js +804 -0
  530. package/build/dist/Types/DatabaseServer/DatabaseServerMetricCatalog.js.map +1 -0
  531. package/build/dist/Types/DatabaseServer/DatabaseSystem.js +1860 -0
  532. package/build/dist/Types/DatabaseServer/DatabaseSystem.js.map +1 -0
  533. package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js +406 -0
  534. package/build/dist/Types/DatabaseServer/DatabaseTelemetryResolver.js.map +1 -0
  535. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  536. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  537. package/build/dist/Types/Monitor/CephAlertTemplates.js +18 -0
  538. package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
  539. package/build/dist/Types/Monitor/DatabaseAlertTemplates.js +1518 -0
  540. package/build/dist/Types/Monitor/DatabaseAlertTemplates.js.map +1 -0
  541. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +57 -24
  542. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  543. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +104 -18
  544. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
  545. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +80 -3
  546. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -1
  547. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +10 -3
  548. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -1
  549. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +29 -0
  550. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  551. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +21 -6
  552. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -1
  553. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +25 -0
  554. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -1
  555. package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js +136 -0
  556. package/build/dist/Types/NetworkDevice/DiscoveredHostNamingStatus.js.map +1 -0
  557. package/build/dist/Types/Permission.js +234 -0
  558. package/build/dist/Types/Permission.js.map +1 -1
  559. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +10 -0
  560. package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
  561. package/build/dist/Types/Rules/RuleRun.js +4 -0
  562. package/build/dist/Types/Rules/RuleRun.js.map +1 -1
  563. package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -1
  564. package/build/dist/Types/Rum/WebVitals.js +12 -0
  565. package/build/dist/Types/Rum/WebVitals.js.map +1 -1
  566. package/build/dist/Types/Telemetry/EntityType.js +23 -0
  567. package/build/dist/Types/Telemetry/EntityType.js.map +1 -1
  568. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js +14 -0
  569. package/build/dist/Types/Telemetry/ResourceFacetCatalog.js.map +1 -1
  570. package/build/dist/Types/Telemetry/ServiceType.js +8 -0
  571. package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
  572. package/build/dist/UI/Components/Banner/Banner.js +1 -1
  573. package/build/dist/UI/Components/Banner/Banner.js.map +1 -1
  574. package/build/dist/UI/Components/Card/Card.js +1 -1
  575. package/build/dist/UI/Components/Card/Card.js.map +1 -1
  576. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +2 -2
  577. package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
  578. package/build/dist/UI/Components/Charts/Line/LineChart.js +1 -1
  579. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  580. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +1 -1
  581. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
  582. package/build/dist/UI/Components/Date/StartAndEndDate.js +5 -5
  583. package/build/dist/UI/Components/Date/StartAndEndDate.js.map +1 -1
  584. package/build/dist/UI/Components/Diagram/HorizontalStepChain.js +1 -1
  585. package/build/dist/UI/Components/Diagram/HorizontalStepChain.js.map +1 -1
  586. package/build/dist/UI/Components/Feed/FeedOptionsButton.js +1 -1
  587. package/build/dist/UI/Components/Feed/FeedOptionsButton.js.map +1 -1
  588. package/build/dist/UI/Components/Forms/BasicForm.js +1 -1
  589. package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
  590. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js +1 -1
  591. package/build/dist/UI/Components/Header/HeaderIconDropdownButton.js.map +1 -1
  592. package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js +1 -1
  593. package/build/dist/UI/Components/HeaderAlert/HeaderAlert.js.map +1 -1
  594. package/build/dist/UI/Components/Input/Input.js +1 -1
  595. package/build/dist/UI/Components/Input/Input.js.map +1 -1
  596. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js +1 -1
  597. package/build/dist/UI/Components/LogsViewer/components/FacetValueRow.js.map +1 -1
  598. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +1 -1
  599. package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
  600. package/build/dist/UI/Components/Modal/ModalFooter.js +1 -1
  601. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  602. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +2 -2
  603. package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
  604. package/build/dist/UI/Components/ModelTable/BaseModelTable.js +4 -4
  605. package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
  606. package/build/dist/UI/Components/Navbar/NavBar.js +9 -2
  607. package/build/dist/UI/Components/Navbar/NavBar.js.map +1 -1
  608. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js +2 -2
  609. package/build/dist/UI/Components/Navbar/NavBarMenuModal.js.map +1 -1
  610. package/build/dist/UI/Components/Page/Page.js +1 -1
  611. package/build/dist/UI/Components/Page/Page.js.map +1 -1
  612. package/build/dist/UI/Components/Pagination/Pagination.js +3 -3
  613. package/build/dist/UI/Components/Pagination/Pagination.js.map +1 -1
  614. package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js +1 -1
  615. package/build/dist/UI/Components/ProgressButtons/ProgressButtonItem.js.map +1 -1
  616. package/build/dist/UI/Components/SideMenu/SideMenu.js +4 -1
  617. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  618. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +3 -1
  619. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -1
  620. package/build/dist/UI/Components/StatusPage/ResourceList.js +6 -6
  621. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
  622. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js +1 -1
  623. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetSidebar.js.map +1 -1
  624. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js +1 -1
  625. package/build/dist/UI/Components/TelemetryViewer/components/TelemetryFacetValueRow.js.map +1 -1
  626. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js +1 -1
  627. package/build/dist/UI/Components/Workflow/ColumnEditor/ModelQueryBuilder.js.map +1 -1
  628. package/build/dist/UI/Components/Workflow/ComponentsModal.js +2 -2
  629. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  630. package/build/dist/UI/Components/Workflow/StepTraceViewer.js +1 -1
  631. package/build/dist/UI/Components/Workflow/StepTraceViewer.js.map +1 -1
  632. package/build/dist/UI/Utils/Navigation.js +66 -33
  633. package/build/dist/UI/Utils/Navigation.js.map +1 -1
  634. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +7 -0
  635. package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -1
  636. package/build/dist/UI/Utils/TelemetryService.js +3 -2
  637. package/build/dist/UI/Utils/TelemetryService.js.map +1 -1
  638. package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js +110 -0
  639. package/build/dist/Utils/Dashboard/KubernetesClusterCpuTileRepair.js.map +1 -0
  640. package/build/dist/Utils/LabelRuleImportExport.js +2 -0
  641. package/build/dist/Utils/LabelRuleImportExport.js.map +1 -1
  642. package/build/dist/Utils/MetricUnitUtil.js +77 -13
  643. package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
  644. package/build/dist/Utils/Monitor/MetricValueFormatter.js +58 -13
  645. package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -1
  646. package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js +161 -0
  647. package/build/dist/Utils/Monitor/PlatformMetricUnitUtil.js.map +1 -0
  648. package/build/dist/Utils/Monitor/PlatformResourceIdentity.js +323 -0
  649. package/build/dist/Utils/Monitor/PlatformResourceIdentity.js.map +1 -0
  650. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js +270 -0
  651. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostNamingDiagnosis.js.map +1 -0
  652. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +23 -0
  653. package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
  654. package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js +61 -0
  655. package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js.map +1 -1
  656. package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js +178 -0
  657. package/build/dist/Utils/Telemetry/DatabaseServerEntityKeys.js.map +1 -0
  658. package/build/dist/Utils/Telemetry/EntityKey.js +133 -0
  659. package/build/dist/Utils/Telemetry/EntityKey.js.map +1 -1
  660. package/build/dist/Utils/ValueFormatter.js +15 -0
  661. package/build/dist/Utils/ValueFormatter.js.map +1 -1
  662. package/package.json +1 -1
  663. package/tsconfig.tests.json +7 -5
@@ -0,0 +1,1288 @@
1
+ /*
2
+ * MonitorCriteriaEvaluator reaches the template renderer, which loads the
3
+ * native isolated-vm addon. Nothing here uses the sandbox and the
4
+ * prebuilt binary cannot always dlopen in the test environment.
5
+ */
6
+ jest.mock("isolated-vm", () => {
7
+ return {};
8
+ });
9
+
10
+ import Monitor from "../../../../Models/DatabaseModels/Monitor";
11
+ import MonitorCriteriaEvaluator from "../../../../Server/Utils/Monitor/MonitorCriteriaEvaluator";
12
+ import AggregateModel from "../../../../Types/BaseDatabase/AggregatedModel";
13
+ import AggregatedResult from "../../../../Types/BaseDatabase/AggregatedResult";
14
+ import Dictionary from "../../../../Types/Dictionary";
15
+ import { JSONObject } from "../../../../Types/JSON";
16
+ import MetricFormulaConfigData from "../../../../Types/Metrics/MetricFormulaConfigData";
17
+ import MetricQueryConfigData from "../../../../Types/Metrics/MetricQueryConfigData";
18
+ import MetricsViewConfig from "../../../../Types/Metrics/MetricsViewConfig";
19
+ import {
20
+ CephAlertTemplate,
21
+ getCephAlertTemplateById,
22
+ } from "../../../../Types/Monitor/CephAlertTemplates";
23
+ import { getCephMetricByMetricName } from "../../../../Types/Monitor/CephMetricCatalog";
24
+ import {
25
+ DockerSwarmAlertTemplate,
26
+ getDockerSwarmAlertTemplateById,
27
+ } from "../../../../Types/Monitor/DockerSwarmAlertTemplates";
28
+ import { getDockerSwarmMetricByMetricName } from "../../../../Types/Monitor/DockerSwarmMetricCatalog";
29
+ import MetricMonitorResponse from "../../../../Types/Monitor/MetricMonitor/MetricMonitorResponse";
30
+ import MetricSeriesResult from "../../../../Types/Monitor/MetricMonitor/MetricSeriesResult";
31
+ import MonitorEvaluationSummary from "../../../../Types/Monitor/MonitorEvaluationSummary";
32
+ import MonitorStep from "../../../../Types/Monitor/MonitorStep";
33
+ import MonitorType from "../../../../Types/Monitor/MonitorType";
34
+ import {
35
+ getProxmoxAlertTemplateById,
36
+ ProxmoxAlertTemplate,
37
+ } from "../../../../Types/Monitor/ProxmoxAlertTemplates";
38
+ import { getProxmoxMetricByMetricName } from "../../../../Types/Monitor/ProxmoxMetricCatalog";
39
+ import {
40
+ getVMwareAlertTemplateById,
41
+ VMwareAlertTemplate,
42
+ VMwareGroupByKey,
43
+ } from "../../../../Types/Monitor/VMwareAlertTemplates";
44
+ import { getVMwareMetricByMetricName } from "../../../../Types/Monitor/VMwareMetricCatalog";
45
+ import ObjectID from "../../../../Types/ObjectID";
46
+ import ProbeApiIngestResponse from "../../../../Types/Probe/ProbeApiIngestResponse";
47
+ import MetricFormulaEvaluator from "../../../../Utils/Metrics/MetricFormulaEvaluator";
48
+ import MetricSeriesFingerprint from "../../../../Utils/Metrics/MetricSeriesFingerprint";
49
+ import PlatformMetricUnitUtil, {
50
+ MetricCatalogPlatform,
51
+ } from "../../../../Utils/Monitor/PlatformMetricUnitUtil";
52
+ import PlatformResourceIdentity, {
53
+ CephResourceIdentity,
54
+ DockerSwarmResourceIdentity,
55
+ ProxmoxResourceIdentity,
56
+ VMwareResourceIdentity,
57
+ } from "../../../../Utils/Monitor/PlatformResourceIdentity";
58
+ import { describe, expect, test } from "@jest/globals";
59
+
60
+ /*
61
+ * Why this suite exists.
62
+ *
63
+ * A GROUPED platform monitor (one alert per node / pool / VM / task) used
64
+ * to render its "Affected Resources" list from the worker's raw scan of
65
+ * whichever query happened to be scanned last — for a ratio template that
66
+ * is the DENOMINATOR, so a "Node Memory Utilization > 85%" email listed
67
+ * every node at its total RAM in raw bytes, breaching or not; a Ceph
68
+ * "Pool Near Full" email led with the EMPTIEST pool, ranked by free
69
+ * bytes; and "Node Offline" / "OSD Down" listed the healthy resources
70
+ * (value 1) and dropped the down one (value 0).
71
+ *
72
+ * The list is now built from the series the criteria actually matched,
73
+ * valued with each series' worst breaching sample in the criteria's own
74
+ * unit. The Kubernetes ratio email has its own suite
75
+ * (MonitorCriteriaEvaluatorKubernetesRatioEmail.test.ts); these tests
76
+ * drive the REAL Proxmox, Ceph, VMware and Docker Swarm templates through
77
+ * the public evaluator entry point with a worker-shaped response —
78
+ * seriesBreakdown aligned [queries..., formulas...], formula slots
79
+ * computed by the same MetricFormulaEvaluator the worker uses, query rows
80
+ * carrying the raw datapoint attributes, one alias-tagged raw-scan
81
+ * breakdown per query, and the catalog-overlaid unit map — and pin the
82
+ * rendered markdown.
83
+ */
84
+
85
+ const MINUTES: Array<Date> = [0, 1, 2, 3, 4].map((minute: number) => {
86
+ return new Date(Date.UTC(2026, 8, 25, 10, minute, 0));
87
+ });
88
+
89
+ const PVE_CLUSTER: string = "pve-lab";
90
+ const CEPH_CLUSTER: string = "ceph-prod";
91
+ const VCENTER: string = "vcsa-prod";
92
+ const SWARM_CLUSTER: string = "swarm-prod";
93
+
94
+ // No rendered value may be a raw, unscaled 10+ digit number (bytes, epochs).
95
+ const RAW_NUMBER: RegExp = /\b\d{10,}\b/;
96
+
97
+ /*
98
+ * One resource as the worker's raw rows see it: the datapoint attributes
99
+ * every row of it carries, and one value per minute for each query alias.
100
+ */
101
+ interface RawSeries {
102
+ attributes: JSONObject;
103
+ valuesByAlias: Dictionary<Array<number>>;
104
+ }
105
+
106
+ type ScannedResource<I> = I & {
107
+ metricValue: number;
108
+ lowestMetricValue: number;
109
+ };
110
+
111
+ // A platform breakdown without its cluster / vCenter name.
112
+ interface ScannedBreakdown<I> {
113
+ metricName: string;
114
+ metricFriendlyName: string;
115
+ metricAlias: string | undefined;
116
+ metricUnit: string | undefined;
117
+ attributes: Dictionary<string>;
118
+ affectedResources: Array<ScannedResource<I>>;
119
+ }
120
+
121
+ interface PlatformFixture<I> {
122
+ monitorType: MonitorType;
123
+ platform: MetricCatalogPlatform;
124
+ friendlyName: (metricName: string) => string | undefined;
125
+ toIdentity: (attributes: JSONObject) => I;
126
+ identityKey: (identity: I) => string;
127
+ attach: (
128
+ response: MetricMonitorResponse,
129
+ breakdowns: Array<ScannedBreakdown<I>>,
130
+ ) => void;
131
+ }
132
+
133
+ const PROXMOX: PlatformFixture<ProxmoxResourceIdentity> = {
134
+ monitorType: MonitorType.Proxmox,
135
+ platform: "proxmox",
136
+ friendlyName: (metricName: string): string | undefined => {
137
+ return getProxmoxMetricByMetricName(metricName)?.friendlyName;
138
+ },
139
+ toIdentity: PlatformResourceIdentity.proxmox,
140
+ identityKey: PlatformResourceIdentity.proxmoxKey,
141
+ attach: (
142
+ response: MetricMonitorResponse,
143
+ breakdowns: Array<ScannedBreakdown<ProxmoxResourceIdentity>>,
144
+ ): void => {
145
+ response.proxmoxResourceBreakdowns = breakdowns.map(
146
+ (breakdown: ScannedBreakdown<ProxmoxResourceIdentity>) => {
147
+ return { ...breakdown, clusterName: PVE_CLUSTER };
148
+ },
149
+ );
150
+ },
151
+ };
152
+
153
+ const CEPH: PlatformFixture<CephResourceIdentity> = {
154
+ monitorType: MonitorType.Ceph,
155
+ platform: "ceph",
156
+ friendlyName: (metricName: string): string | undefined => {
157
+ return getCephMetricByMetricName(metricName)?.friendlyName;
158
+ },
159
+ toIdentity: PlatformResourceIdentity.ceph,
160
+ identityKey: PlatformResourceIdentity.cephKey,
161
+ attach: (
162
+ response: MetricMonitorResponse,
163
+ breakdowns: Array<ScannedBreakdown<CephResourceIdentity>>,
164
+ ): void => {
165
+ response.cephResourceBreakdowns = breakdowns.map(
166
+ (breakdown: ScannedBreakdown<CephResourceIdentity>) => {
167
+ return { ...breakdown, clusterName: CEPH_CLUSTER };
168
+ },
169
+ );
170
+ },
171
+ };
172
+
173
+ const VMWARE: PlatformFixture<VMwareResourceIdentity> = {
174
+ monitorType: MonitorType.VMware,
175
+ platform: "vmware",
176
+ friendlyName: (metricName: string): string | undefined => {
177
+ return getVMwareMetricByMetricName(metricName)?.friendlyName;
178
+ },
179
+ toIdentity: PlatformResourceIdentity.vmware,
180
+ identityKey: PlatformResourceIdentity.vmwareKey,
181
+ attach: (
182
+ response: MetricMonitorResponse,
183
+ breakdowns: Array<ScannedBreakdown<VMwareResourceIdentity>>,
184
+ ): void => {
185
+ response.vmwareResourceBreakdowns = breakdowns.map(
186
+ (breakdown: ScannedBreakdown<VMwareResourceIdentity>) => {
187
+ return { ...breakdown, vcenterName: VCENTER };
188
+ },
189
+ );
190
+ },
191
+ };
192
+
193
+ const DOCKER_SWARM: PlatformFixture<DockerSwarmResourceIdentity> = {
194
+ monitorType: MonitorType.DockerSwarm,
195
+ platform: "dockerSwarm",
196
+ friendlyName: (metricName: string): string | undefined => {
197
+ return getDockerSwarmMetricByMetricName(metricName)?.friendlyName;
198
+ },
199
+ toIdentity: PlatformResourceIdentity.dockerSwarm,
200
+ identityKey: PlatformResourceIdentity.dockerSwarmKey,
201
+ attach: (
202
+ response: MetricMonitorResponse,
203
+ breakdowns: Array<ScannedBreakdown<DockerSwarmResourceIdentity>>,
204
+ ): void => {
205
+ response.dockerSwarmResourceBreakdowns = breakdowns.map(
206
+ (breakdown: ScannedBreakdown<DockerSwarmResourceIdentity>) => {
207
+ return { ...breakdown, clusterName: SWARM_CLUSTER };
208
+ },
209
+ );
210
+ },
211
+ };
212
+
213
+ interface CommonTemplateArgs {
214
+ onlineMonitorStatusId: ObjectID;
215
+ offlineMonitorStatusId: ObjectID;
216
+ defaultIncidentSeverityId: ObjectID;
217
+ defaultAlertSeverityId: ObjectID;
218
+ monitorName: string;
219
+ }
220
+
221
+ function commonArgs(monitorName: string): CommonTemplateArgs {
222
+ return {
223
+ onlineMonitorStatusId: ObjectID.generate(),
224
+ offlineMonitorStatusId: ObjectID.generate(),
225
+ defaultIncidentSeverityId: ObjectID.generate(),
226
+ defaultAlertSeverityId: ObjectID.generate(),
227
+ monitorName: monitorName,
228
+ };
229
+ }
230
+
231
+ function requireTemplate<T>(template: T | undefined, id: string): T {
232
+ if (!template) {
233
+ throw new Error(`${id} template missing`);
234
+ }
235
+
236
+ return template;
237
+ }
238
+
239
+ function proxmoxStep(templateId: string): MonitorStep {
240
+ const template: ProxmoxAlertTemplate = requireTemplate(
241
+ getProxmoxAlertTemplateById(templateId),
242
+ templateId,
243
+ );
244
+
245
+ return template.getMonitorStep({
246
+ clusterIdentifier: PVE_CLUSTER,
247
+ ...commonArgs(`${PVE_CLUSTER} - ${template.name}`),
248
+ });
249
+ }
250
+
251
+ function cephStep(templateId: string): MonitorStep {
252
+ const template: CephAlertTemplate = requireTemplate(
253
+ getCephAlertTemplateById(templateId),
254
+ templateId,
255
+ );
256
+
257
+ return template.getMonitorStep({
258
+ clusterIdentifier: CEPH_CLUSTER,
259
+ ...commonArgs(`${CEPH_CLUSTER} - ${template.name}`),
260
+ });
261
+ }
262
+
263
+ function vmwareStep(templateId: string): MonitorStep {
264
+ const template: VMwareAlertTemplate = requireTemplate(
265
+ getVMwareAlertTemplateById(templateId),
266
+ templateId,
267
+ );
268
+
269
+ return template.getMonitorStep({
270
+ vcenterIdentifier: VCENTER,
271
+ ...commonArgs(`${VCENTER} - ${template.name}`),
272
+ });
273
+ }
274
+
275
+ function dockerSwarmStep(templateId: string): MonitorStep {
276
+ const template: DockerSwarmAlertTemplate = requireTemplate(
277
+ getDockerSwarmAlertTemplateById(templateId),
278
+ templateId,
279
+ );
280
+
281
+ return template.getMonitorStep({
282
+ clusterIdentifier: SWARM_CLUSTER,
283
+ ...commonArgs(`${SWARM_CLUSTER} - ${template.name}`),
284
+ });
285
+ }
286
+
287
+ function viewConfigOf(monitorStep: MonitorStep): MetricsViewConfig {
288
+ const config: MetricsViewConfig | undefined =
289
+ MonitorStep.getMetricsViewConfig(monitorStep);
290
+
291
+ if (!config) {
292
+ throw new Error("template has no metric view config");
293
+ }
294
+
295
+ return config;
296
+ }
297
+
298
+ function aliasOf(config: {
299
+ metricAliasData?: { metricVariable?: string | undefined } | undefined;
300
+ }): string {
301
+ return config.metricAliasData?.metricVariable || "";
302
+ }
303
+
304
+ function metricNameOf(queryConfig: MetricQueryConfigData): string {
305
+ return (queryConfig.metricQueryData.filterData.metricName as string) || "";
306
+ }
307
+
308
+ /*
309
+ * What the telemetry worker hands the evaluator for a grouped platform
310
+ * monitor, built the way the worker builds it:
311
+ *
312
+ * - per query, one row per minute per resource, each carrying the raw
313
+ * datapoint attributes (aggregatePerSeriesFromRawMetrics keeps the
314
+ * bucket's first datapoint's attribute map);
315
+ * - series labels / fingerprints from the template's group-by keys;
316
+ * - each series' formula slots evaluated by MetricFormulaEvaluator, as
317
+ * appendFormulaResults does — their rows carry only the group labels;
318
+ * - one raw-scan breakdown per query, tagged with the query's alias and
319
+ * the unit the monitor's unit map gives it;
320
+ * - nativeUnitsByMetricName from the catalog-overlaid unit map.
321
+ */
322
+ function workerResponse<I>(input: {
323
+ monitorStep: MonitorStep;
324
+ fixture: PlatformFixture<I>;
325
+ series: Array<RawSeries>;
326
+ declaredUnits?: Dictionary<string> | undefined;
327
+ }): MetricMonitorResponse {
328
+ const viewConfig: MetricsViewConfig = viewConfigOf(input.monitorStep);
329
+ const queryConfigs: Array<MetricQueryConfigData> = viewConfig.queryConfigs;
330
+ const formulaConfigs: Array<MetricFormulaConfigData> =
331
+ viewConfig.formulaConfigs || [];
332
+ const groupByKeys: Array<string> = MonitorStep.getGroupByAttributeKeys(
333
+ input.monitorStep,
334
+ );
335
+
336
+ if (groupByKeys.length === 0) {
337
+ throw new Error("these tests are about GROUPED monitors");
338
+ }
339
+
340
+ const declaredUnits: Map<string, string> = new Map<string, string>(
341
+ Object.entries(input.declaredUnits || {}).map(
342
+ ([metricName, unit]: [string, string]): [string, string] => {
343
+ return [metricName.toLowerCase(), unit];
344
+ },
345
+ ),
346
+ );
347
+
348
+ const unitsByMetricName: Dictionary<string> =
349
+ PlatformMetricUnitUtil.buildUnitsByMetricName({
350
+ platform: input.fixture.platform,
351
+ metricNames: queryConfigs.map(metricNameOf),
352
+ declaredUnitsByMetricName: declaredUnits,
353
+ });
354
+
355
+ const queryRows: (
356
+ series: RawSeries,
357
+ alias: string,
358
+ ) => Array<AggregateModel> = (
359
+ series: RawSeries,
360
+ alias: string,
361
+ ): Array<AggregateModel> => {
362
+ const values: Array<number> = series.valuesByAlias[alias] || [];
363
+
364
+ return values.map((value: number, i: number): AggregateModel => {
365
+ return {
366
+ timestamp: MINUTES[i]!,
367
+ value: value,
368
+ attributes: series.attributes,
369
+ } as unknown as AggregateModel;
370
+ });
371
+ };
372
+
373
+ const seriesBreakdown: Array<MetricSeriesResult> = input.series.map(
374
+ (series: RawSeries): MetricSeriesResult => {
375
+ const results: Array<AggregatedResult> = queryConfigs.map(
376
+ (queryConfig: MetricQueryConfigData): AggregatedResult => {
377
+ return { data: queryRows(series, aliasOf(queryConfig)) };
378
+ },
379
+ );
380
+
381
+ formulaConfigs.forEach(
382
+ (formulaConfig: MetricFormulaConfigData, index: number): void => {
383
+ results.push(
384
+ MetricFormulaEvaluator.evaluateFormula({
385
+ formula: formulaConfig.metricFormulaData.metricFormula,
386
+ queryConfigs: queryConfigs,
387
+ formulaConfigs: formulaConfigs.slice(0, index),
388
+ results: [...results],
389
+ }),
390
+ );
391
+ },
392
+ );
393
+
394
+ const labels: JSONObject = MetricSeriesFingerprint.extractSeriesLabels({
395
+ sample: { attributes: series.attributes } as unknown as AggregateModel,
396
+ attributeKeys: groupByKeys,
397
+ });
398
+
399
+ return {
400
+ fingerprint: MetricSeriesFingerprint.computeFingerprint(labels),
401
+ labels: labels,
402
+ aggregatedResults: results,
403
+ };
404
+ },
405
+ );
406
+
407
+ const breakdowns: Array<ScannedBreakdown<I>> = queryConfigs.map(
408
+ (queryConfig: MetricQueryConfigData): ScannedBreakdown<I> => {
409
+ const alias: string = aliasOf(queryConfig);
410
+ const metricName: string = metricNameOf(queryConfig);
411
+ const byKey: Map<string, ScannedResource<I>> = new Map();
412
+
413
+ for (const series of input.series) {
414
+ for (const row of queryRows(series, alias)) {
415
+ const identity: I = input.fixture.toIdentity(series.attributes);
416
+ const key: string = input.fixture.identityKey(identity);
417
+ const existing: ScannedResource<I> | undefined = byKey.get(key);
418
+
419
+ if (!existing) {
420
+ byKey.set(key, {
421
+ ...identity,
422
+ metricValue: row.value,
423
+ lowestMetricValue: row.value,
424
+ });
425
+ continue;
426
+ }
427
+
428
+ existing.metricValue = Math.max(existing.metricValue, row.value);
429
+ existing.lowestMetricValue = Math.min(
430
+ existing.lowestMetricValue,
431
+ row.value,
432
+ );
433
+ }
434
+ }
435
+
436
+ return {
437
+ metricName: metricName,
438
+ metricFriendlyName:
439
+ input.fixture.friendlyName(metricName) || metricName,
440
+ metricAlias: alias,
441
+ metricUnit: unitsByMetricName[metricName.toLowerCase()],
442
+ attributes: {
443
+ ...((queryConfig.metricQueryData.filterData.attributes ||
444
+ {}) as Dictionary<string>),
445
+ },
446
+ affectedResources: Array.from(byKey.values()),
447
+ };
448
+ },
449
+ );
450
+
451
+ const response: MetricMonitorResponse = {
452
+ projectId: ObjectID.generate(),
453
+ monitorId: ObjectID.generate(),
454
+ metricResult: [],
455
+ metricViewConfig: viewConfig,
456
+ seriesBreakdown: seriesBreakdown,
457
+ nativeUnitsByMetricName: unitsByMetricName,
458
+ };
459
+
460
+ input.fixture.attach(
461
+ response,
462
+ breakdowns.filter((breakdown: ScannedBreakdown<I>) => {
463
+ return breakdown.affectedResources.length > 0;
464
+ }),
465
+ );
466
+
467
+ return response;
468
+ }
469
+
470
+ async function rootCauseOf<I>(input: {
471
+ monitorStep: MonitorStep;
472
+ fixture: PlatformFixture<I>;
473
+ series: Array<RawSeries>;
474
+ declaredUnits?: Dictionary<string> | undefined;
475
+ }): Promise<string> {
476
+ const monitor: Monitor = new Monitor();
477
+ monitor._id = ObjectID.generate().toString();
478
+ monitor.monitorType = input.fixture.monitorType;
479
+ monitor.name = "platform grouped monitor";
480
+
481
+ const response: ProbeApiIngestResponse =
482
+ await MonitorCriteriaEvaluator.processMonitorStep({
483
+ dataToProcess: workerResponse(input),
484
+ monitorStep: input.monitorStep,
485
+ monitor: monitor,
486
+ probeApiIngestResponse: {
487
+ monitorId: monitor.id!,
488
+ rootCause: null,
489
+ },
490
+ evaluationSummary: {
491
+ criteriaResults: [],
492
+ events: [],
493
+ } as unknown as MonitorEvaluationSummary,
494
+ });
495
+
496
+ return response.rootCause || "";
497
+ }
498
+
499
+ function linesOf(rootCause: string): Array<string> {
500
+ return rootCause.split("\n");
501
+ }
502
+
503
+ // The part of a fixture the "- Metric:" expectations need.
504
+ interface CatalogLookup {
505
+ friendlyName: (metricName: string) => string | undefined;
506
+ }
507
+
508
+ /*
509
+ * The "- Metric:" sub-line for one formula component, with the metric's
510
+ * catalog name looked up rather than hard-coded (catalogs change).
511
+ */
512
+ function componentLine(input: {
513
+ fixture: CatalogLookup;
514
+ alias: string;
515
+ metricName: string;
516
+ }): string {
517
+ const friendlyName: string | undefined = input.fixture.friendlyName(
518
+ input.metricName,
519
+ );
520
+
521
+ const described: string =
522
+ friendlyName && friendlyName !== input.metricName
523
+ ? `${friendlyName} (\`${input.metricName}\`)`
524
+ : `\`${input.metricName}\``;
525
+
526
+ return ` - \`${input.alias}\` = ${described}`;
527
+ }
528
+
529
+ function metricLine(input: {
530
+ fixture: CatalogLookup;
531
+ metricName: string;
532
+ }): string {
533
+ const friendlyName: string | undefined = input.fixture.friendlyName(
534
+ input.metricName,
535
+ );
536
+
537
+ return friendlyName && friendlyName !== input.metricName
538
+ ? `- Metric: ${friendlyName} (\`${input.metricName}\`)`
539
+ : `- Metric: \`${input.metricName}\``;
540
+ }
541
+
542
+ // --- Proxmox ---
543
+
544
+ function pveAttributes(input: {
545
+ id: string;
546
+ type: string;
547
+ scope: string;
548
+ }): JSONObject {
549
+ // pve-exporter's `id` label, split by the agent's OTTL transform.
550
+ return {
551
+ id: input.id,
552
+ "pve.type": input.type,
553
+ "pve.scope": input.scope,
554
+ "pve.id": input.id.split("/").slice(1).join("/"),
555
+ "resource.proxmox.cluster.name": PVE_CLUSTER,
556
+ };
557
+ }
558
+
559
+ const GB: number = 1_000_000_000;
560
+
561
+ /*
562
+ * Three nodes: pve1 at 90–94% of 64 GB, pve3 at 86–88% of 128 GB, pve2
563
+ * comfortably at 50%. The RAM totals are 11- and 12-digit byte counts —
564
+ * exactly what the old denominator scan printed for every node.
565
+ */
566
+ const PVE_NODE_MEMORY: Array<RawSeries> = [
567
+ {
568
+ attributes: pveAttributes({ id: "node/pve1", type: "node", scope: "node" }),
569
+ valuesByAlias: {
570
+ used_mem: [57.6 * GB, 58.24 * GB, 58.88 * GB, 59.52 * GB, 60.16 * GB],
571
+ total_mem: [64 * GB, 64 * GB, 64 * GB, 64 * GB, 64 * GB],
572
+ },
573
+ },
574
+ {
575
+ attributes: pveAttributes({ id: "node/pve2", type: "node", scope: "node" }),
576
+ valuesByAlias: {
577
+ used_mem: [64 * GB, 64 * GB, 64 * GB, 64 * GB, 64 * GB],
578
+ total_mem: [128 * GB, 128 * GB, 128 * GB, 128 * GB, 128 * GB],
579
+ },
580
+ },
581
+ {
582
+ attributes: pveAttributes({ id: "node/pve3", type: "node", scope: "node" }),
583
+ valuesByAlias: {
584
+ used_mem: [110.08 * GB, 110.72 * GB, 111.36 * GB, 112 * GB, 112.64 * GB],
585
+ total_mem: [128 * GB, 128 * GB, 128 * GB, 128 * GB, 128 * GB],
586
+ },
587
+ },
588
+ ];
589
+
590
+ const PVE_STORAGE: Array<RawSeries> = [
591
+ {
592
+ attributes: pveAttributes({
593
+ id: "storage/local-lvm",
594
+ type: "storage",
595
+ scope: "storage",
596
+ }),
597
+ valuesByAlias: {
598
+ used_disk: [900 * GB, 910 * GB, 920 * GB, 930 * GB, 940 * GB],
599
+ total_disk: [1000 * GB, 1000 * GB, 1000 * GB, 1000 * GB, 1000 * GB],
600
+ },
601
+ },
602
+ {
603
+ attributes: pveAttributes({
604
+ id: "storage/local",
605
+ type: "storage",
606
+ scope: "storage",
607
+ }),
608
+ valuesByAlias: {
609
+ used_disk: [20 * GB, 20 * GB, 20 * GB, 20 * GB, 20 * GB],
610
+ total_disk: [100 * GB, 100 * GB, 100 * GB, 100 * GB, 100 * GB],
611
+ },
612
+ },
613
+ {
614
+ attributes: pveAttributes({
615
+ id: "storage/backups",
616
+ type: "storage",
617
+ scope: "storage",
618
+ }),
619
+ valuesByAlias: {
620
+ used_disk: [3480 * GB, 3480 * GB, 3480 * GB, 3480 * GB, 3480 * GB],
621
+ total_disk: [4000 * GB, 4000 * GB, 4000 * GB, 4000 * GB, 4000 * GB],
622
+ },
623
+ },
624
+ ];
625
+
626
+ const PVE_LXC_DISK: Array<RawSeries> = [
627
+ {
628
+ attributes: pveAttributes({ id: "lxc/101", type: "lxc", scope: "guest" }),
629
+ valuesByAlias: {
630
+ used_disk: [7.6 * GB, 7.6 * GB, 7.6 * GB, 7.6 * GB, 7.6 * GB],
631
+ total_disk: [8 * GB, 8 * GB, 8 * GB, 8 * GB, 8 * GB],
632
+ },
633
+ },
634
+ {
635
+ attributes: pveAttributes({ id: "lxc/102", type: "lxc", scope: "guest" }),
636
+ valuesByAlias: {
637
+ used_disk: [12.8 * GB, 12.8 * GB, 12.8 * GB, 12.8 * GB, 12.8 * GB],
638
+ total_disk: [32 * GB, 32 * GB, 32 * GB, 32 * GB, 32 * GB],
639
+ },
640
+ },
641
+ ];
642
+
643
+ describe("Grouped Proxmox ratio templates list only the breaching series, in %", () => {
644
+ test("pve-node-high-memory: only the nodes above 85%, worst first, never RAM bytes", async () => {
645
+ const rootCause: string = await rootCauseOf({
646
+ monitorStep: proxmoxStep("pve-node-high-memory"),
647
+ fixture: PROXMOX,
648
+ series: PVE_NODE_MEMORY,
649
+ });
650
+
651
+ const lines: Array<string> = linesOf(rootCause);
652
+
653
+ expect(lines).toContain("**Affected Resources** (2 total)");
654
+ // Kind-agnostic here — the kind is pinned by the next describe block.
655
+ expect(rootCause).toMatch(
656
+ /^1\. \*\*[^*]+\*\* `node\/pve1` — \*\*94\.00%\*\*$/m,
657
+ );
658
+ expect(rootCause).toMatch(
659
+ /^2\. \*\*[^*]+\*\* `node\/pve3` — \*\*88\.00%\*\*$/m,
660
+ );
661
+
662
+ // The healthy node is not "affected".
663
+ expect(rootCause).not.toContain("node/pve2");
664
+
665
+ // No RAM totals, raw or scaled.
666
+ expect(rootCause).not.toMatch(RAW_NUMBER);
667
+ expect(rootCause).not.toContain("64 GB");
668
+ expect(rootCause).not.toContain("128 GB");
669
+ });
670
+
671
+ test("pve-node-high-memory: '- Metric:' names the formula legend and lists both components", async () => {
672
+ const rootCause: string = await rootCauseOf({
673
+ monitorStep: proxmoxStep("pve-node-high-memory"),
674
+ fixture: PROXMOX,
675
+ series: PVE_NODE_MEMORY,
676
+ });
677
+
678
+ const lines: Array<string> = linesOf(rootCause);
679
+
680
+ expect(lines).toContain("**Proxmox Cluster Details**");
681
+ expect(lines).toContain(`- Cluster: ${PVE_CLUSTER}`);
682
+ expect(lines).toContain("- Metric: Node Memory Utilization (%)");
683
+ expect(lines).toContain("- Formula: `(used_mem / total_mem) * 100`");
684
+ expect(lines).toContain(
685
+ componentLine({
686
+ fixture: PROXMOX,
687
+ alias: "used_mem",
688
+ metricName: "pve_memory_usage_bytes",
689
+ }),
690
+ );
691
+ expect(lines).toContain(
692
+ componentLine({
693
+ fixture: PROXMOX,
694
+ alias: "total_mem",
695
+ metricName: "pve_memory_size_bytes",
696
+ }),
697
+ );
698
+
699
+ // Not the denominator's own metric line.
700
+ expect(lines).not.toContain(
701
+ metricLine({ fixture: PROXMOX, metricName: "pve_memory_size_bytes" }),
702
+ );
703
+ });
704
+
705
+ test("pve-storage-near-full: only the volumes above 85%, in %, with the legend and components", async () => {
706
+ const rootCause: string = await rootCauseOf({
707
+ monitorStep: proxmoxStep("pve-storage-near-full"),
708
+ fixture: PROXMOX,
709
+ series: PVE_STORAGE,
710
+ });
711
+
712
+ const lines: Array<string> = linesOf(rootCause);
713
+
714
+ expect(lines).toContain("**Affected Resources** (2 total)");
715
+ expect(rootCause).toMatch(
716
+ /^1\. \*\*[^*]+\*\* `storage\/local-lvm` — \*\*94\.00%\*\*$/m,
717
+ );
718
+ expect(rootCause).toMatch(
719
+ /^2\. \*\*[^*]+\*\* `storage\/backups` — \*\*87\.00%\*\*$/m,
720
+ );
721
+ expect(rootCause).not.toContain("`storage/local`");
722
+
723
+ expect(lines).toContain("- Metric: Storage Utilization (%)");
724
+ expect(lines).toContain("- Formula: `(used_disk / total_disk) * 100`");
725
+ expect(lines).toContain(
726
+ componentLine({
727
+ fixture: PROXMOX,
728
+ alias: "used_disk",
729
+ metricName: "pve_disk_usage_bytes",
730
+ }),
731
+ );
732
+ expect(lines).toContain(
733
+ componentLine({
734
+ fixture: PROXMOX,
735
+ alias: "total_disk",
736
+ metricName: "pve_disk_size_bytes",
737
+ }),
738
+ );
739
+
740
+ expect(rootCause).not.toMatch(RAW_NUMBER);
741
+ expect(rootCause).not.toContain("4 TB");
742
+ expect(rootCause).not.toContain("1 TB");
743
+ });
744
+
745
+ test("pve-lxc-disk-near-full: only the container above 90%, in %, with the legend and components", async () => {
746
+ const rootCause: string = await rootCauseOf({
747
+ monitorStep: proxmoxStep("pve-lxc-disk-near-full"),
748
+ fixture: PROXMOX,
749
+ series: PVE_LXC_DISK,
750
+ });
751
+
752
+ const lines: Array<string> = linesOf(rootCause);
753
+
754
+ expect(lines).toContain("**Affected Resources** (1 total)");
755
+ expect(rootCause).toMatch(
756
+ /^1\. \*\*[^*]+\*\* `lxc\/101` — \*\*95\.00%\*\*$/m,
757
+ );
758
+ expect(rootCause).not.toContain("lxc/102");
759
+
760
+ expect(lines).toContain("- Metric: Container Root Disk Utilization (%)");
761
+ expect(lines).toContain("- Formula: `(used_disk / total_disk) * 100`");
762
+ expect(lines).toContain(
763
+ componentLine({
764
+ fixture: PROXMOX,
765
+ alias: "used_disk",
766
+ metricName: "pve_disk_usage_bytes",
767
+ }),
768
+ );
769
+
770
+ expect(rootCause).not.toMatch(RAW_NUMBER);
771
+ expect(rootCause).not.toContain("8 GB");
772
+ });
773
+ });
774
+
775
+ /*
776
+ * A grouped ratio template groups by pve-exporter's `id` label alone, and
777
+ * a formula row carries only its group labels — so the only raw
778
+ * attributes the evaluator enriches a formula series from are `{ id }`.
779
+ * The kind (Node / Storage / Container) lives in `pve.type` / `pve.scope`,
780
+ * which the component query rows of the SAME series do carry, and which
781
+ * the raw scan of the same query reads. PlatformResourceIdentity exists so
782
+ * that "a per-series list and a raw-scan list title the same node the same
783
+ * way"; these pin that promise for the formula templates.
784
+ */
785
+ describe("Grouped Proxmox ratio templates title each series by its kind, as the raw scan does", () => {
786
+ test("pve-node-high-memory: the node is a **Node**", async () => {
787
+ const lines: Array<string> = linesOf(
788
+ await rootCauseOf({
789
+ monitorStep: proxmoxStep("pve-node-high-memory"),
790
+ fixture: PROXMOX,
791
+ series: PVE_NODE_MEMORY,
792
+ }),
793
+ );
794
+
795
+ expect(lines).toContain("1. **Node** `node/pve1` — **94.00%**");
796
+ expect(lines).toContain("2. **Node** `node/pve3` — **88.00%**");
797
+ });
798
+
799
+ test("pve-storage-near-full: the volume is a **Storage**", async () => {
800
+ const lines: Array<string> = linesOf(
801
+ await rootCauseOf({
802
+ monitorStep: proxmoxStep("pve-storage-near-full"),
803
+ fixture: PROXMOX,
804
+ series: PVE_STORAGE,
805
+ }),
806
+ );
807
+
808
+ expect(lines).toContain("1. **Storage** `storage/local-lvm` — **94.00%**");
809
+ expect(lines).toContain("2. **Storage** `storage/backups` — **87.00%**");
810
+ });
811
+
812
+ test("pve-lxc-disk-near-full: the guest is a **Container**", async () => {
813
+ const lines: Array<string> = linesOf(
814
+ await rootCauseOf({
815
+ monitorStep: proxmoxStep("pve-lxc-disk-near-full"),
816
+ fixture: PROXMOX,
817
+ series: PVE_LXC_DISK,
818
+ }),
819
+ );
820
+
821
+ expect(lines).toContain("1. **Container** `lxc/101` — **95.00%**");
822
+ });
823
+ });
824
+
825
+ describe("Grouped Proxmox pve-node-offline lists the DOWN node", () => {
826
+ const NODES_UP: Array<RawSeries> = [
827
+ {
828
+ attributes: pveAttributes({
829
+ id: "node/pve1",
830
+ type: "node",
831
+ scope: "node",
832
+ }),
833
+ valuesByAlias: { node_up: [1, 1, 1, 1, 1] },
834
+ },
835
+ {
836
+ attributes: pveAttributes({
837
+ id: "node/pve2",
838
+ type: "node",
839
+ scope: "node",
840
+ }),
841
+ valuesByAlias: { node_up: [0, 0, 0, 0, 0] },
842
+ },
843
+ {
844
+ attributes: pveAttributes({
845
+ id: "node/pve3",
846
+ type: "node",
847
+ scope: "node",
848
+ }),
849
+ valuesByAlias: { node_up: [1, 1, 1, 1, 1] },
850
+ },
851
+ ];
852
+
853
+ test("the node at 0 is listed and titled as a node; the up nodes are not", async () => {
854
+ const rootCause: string = await rootCauseOf({
855
+ monitorStep: proxmoxStep("pve-node-offline"),
856
+ fixture: PROXMOX,
857
+ series: NODES_UP,
858
+ });
859
+
860
+ const lines: Array<string> = linesOf(rootCause);
861
+
862
+ expect(lines).toContain("**Affected Resources** (1 total)");
863
+ expect(lines).toContain("1. **Node** `node/pve2` — **0**");
864
+ expect(rootCause).not.toContain("node/pve1");
865
+ expect(rootCause).not.toContain("node/pve3");
866
+
867
+ expect(lines).toContain(
868
+ metricLine({ fixture: PROXMOX, metricName: "pve_up" }),
869
+ );
870
+ expect(rootCause).not.toMatch(RAW_NUMBER);
871
+ });
872
+ });
873
+
874
+ // --- Ceph ---
875
+
876
+ function cephPoolAttributes(poolId: number): JSONObject {
877
+ // ceph-mgr's pool data series carry pool_id only — no `name` label.
878
+ return {
879
+ pool_id: poolId,
880
+ instance: "10.0.0.11:9283",
881
+ job: "ceph",
882
+ "resource.ceph.cluster.name": CEPH_CLUSTER,
883
+ };
884
+ }
885
+
886
+ describe("Grouped Ceph templates list the series the criteria matched", () => {
887
+ /*
888
+ * pool 3 (rbd) is 93–95% full. pool 0 (.mgr) is nearly empty with 2.4 TB
889
+ * free — the largest max_avail, which is why the old denominator scan,
890
+ * ranked by value, listed it FIRST. pool 5 sits at 40%.
891
+ */
892
+ const POOLS: Array<RawSeries> = [
893
+ {
894
+ attributes: cephPoolAttributes(0),
895
+ valuesByAlias: {
896
+ pool_stored: [1_500_000, 1_500_000, 1_500_000, 1_500_000, 1_500_000],
897
+ pool_max_avail: [2400 * GB, 2400 * GB, 2400 * GB, 2400 * GB, 2400 * GB],
898
+ },
899
+ },
900
+ {
901
+ attributes: cephPoolAttributes(3),
902
+ valuesByAlias: {
903
+ pool_stored: [930 * GB, 935 * GB, 940 * GB, 945 * GB, 950 * GB],
904
+ pool_max_avail: [70 * GB, 65 * GB, 60 * GB, 55 * GB, 50 * GB],
905
+ },
906
+ },
907
+ {
908
+ attributes: cephPoolAttributes(5),
909
+ valuesByAlias: {
910
+ pool_stored: [400 * GB, 400 * GB, 400 * GB, 400 * GB, 400 * GB],
911
+ pool_max_avail: [600 * GB, 600 * GB, 600 * GB, 600 * GB, 600 * GB],
912
+ },
913
+ },
914
+ ];
915
+
916
+ test("ceph-pool-near-full: the full pool at its %, the emptiest pool absent", async () => {
917
+ const rootCause: string = await rootCauseOf({
918
+ monitorStep: cephStep("ceph-pool-near-full"),
919
+ fixture: CEPH,
920
+ series: POOLS,
921
+ });
922
+
923
+ const lines: Array<string> = linesOf(rootCause);
924
+
925
+ expect(lines).toContain("**Ceph Cluster Details**");
926
+ expect(lines).toContain(`- Cluster: ${CEPH_CLUSTER}`);
927
+ expect(lines).toContain("**Affected Resources** (1 total)");
928
+ expect(lines).toContain("1. **Pool** `3` — **95.00%**");
929
+
930
+ // The emptiest pool, previously first by free bytes, is gone.
931
+ expect(rootCause).not.toContain("**Pool** `0`");
932
+ expect(rootCause).not.toContain("**Pool** `5`");
933
+ expect(rootCause).not.toContain("2.4 TB");
934
+ expect(rootCause).not.toMatch(RAW_NUMBER);
935
+ });
936
+
937
+ test("ceph-pool-near-full: '- Metric:' names the formula and each component once", async () => {
938
+ const rootCause: string = await rootCauseOf({
939
+ monitorStep: cephStep("ceph-pool-near-full"),
940
+ fixture: CEPH,
941
+ series: POOLS,
942
+ });
943
+
944
+ const lines: Array<string> = linesOf(rootCause);
945
+
946
+ expect(lines).toContain("- Metric: Pool Capacity Used (%)");
947
+ expect(lines).toContain(
948
+ "- Formula: `(pool_stored / (pool_stored + pool_max_avail)) * 100`",
949
+ );
950
+
951
+ const storedLine: string = componentLine({
952
+ fixture: CEPH,
953
+ alias: "pool_stored",
954
+ metricName: "ceph_pool_stored",
955
+ });
956
+ const maxAvailLine: string = componentLine({
957
+ fixture: CEPH,
958
+ alias: "pool_max_avail",
959
+ metricName: "ceph_pool_max_avail",
960
+ });
961
+
962
+ // pool_stored appears twice in the expression but is one component.
963
+ expect(
964
+ lines.filter((line: string) => {
965
+ return line === storedLine;
966
+ }),
967
+ ).toHaveLength(1);
968
+ expect(lines).toContain(maxAvailLine);
969
+ expect(lines).not.toContain(
970
+ metricLine({ fixture: CEPH, metricName: "ceph_pool_max_avail" }),
971
+ );
972
+ });
973
+
974
+ test("ceph-osd-down grouped by ceph_daemon: the down OSD at **0**, the up OSDs absent", async () => {
975
+ const osd: (daemon: string, values: Array<number>) => RawSeries = (
976
+ daemon: string,
977
+ values: Array<number>,
978
+ ): RawSeries => {
979
+ return {
980
+ attributes: {
981
+ ceph_daemon: daemon,
982
+ instance: "10.0.0.11:9283",
983
+ job: "ceph",
984
+ "resource.ceph.cluster.name": CEPH_CLUSTER,
985
+ },
986
+ valuesByAlias: { osd_up: values },
987
+ };
988
+ };
989
+
990
+ const rootCause: string = await rootCauseOf({
991
+ monitorStep: cephStep("ceph-osd-down"),
992
+ fixture: CEPH,
993
+ series: [
994
+ osd("osd.0", [1, 1, 1, 1, 1]),
995
+ osd("osd.1", [0, 0, 0, 0, 0]),
996
+ osd("osd.2", [1, 1, 1, 1, 1]),
997
+ ],
998
+ });
999
+
1000
+ const lines: Array<string> = linesOf(rootCause);
1001
+
1002
+ expect(lines).toContain("**Affected Resources** (1 total)");
1003
+ expect(lines).toContain("1. **Daemon** `osd.1` — **0**");
1004
+ expect(rootCause).not.toContain("osd.0");
1005
+ expect(rootCause).not.toContain("osd.2");
1006
+ expect(lines).toContain(
1007
+ metricLine({ fixture: CEPH, metricName: "ceph_osd_up" }),
1008
+ );
1009
+ expect(rootCause).not.toMatch(RAW_NUMBER);
1010
+ });
1011
+ });
1012
+
1013
+ // --- VMware ---
1014
+
1015
+ function vmwareAttributes(overrides: JSONObject): JSONObject {
1016
+ return {
1017
+ "resource.vmware.vcenter.name": VCENTER,
1018
+ "resource.vcenter.datacenter.name": "DC1",
1019
+ ...overrides,
1020
+ };
1021
+ }
1022
+
1023
+ describe("Grouped VMware templates name the object the series is about", () => {
1024
+ test("vmware-vm-memory-saturation: the VM, with the Host and Cluster its datapoints carry", async () => {
1025
+ const monitorStep: MonitorStep = vmwareStep("vmware-vm-memory-saturation");
1026
+
1027
+ // The template groups by VM name — the series label names only the VM.
1028
+ expect(MonitorStep.getGroupByAttributeKeys(monitorStep)).toEqual([
1029
+ VMwareGroupByKey.VirtualMachine,
1030
+ ]);
1031
+
1032
+ const rootCause: string = await rootCauseOf({
1033
+ monitorStep: monitorStep,
1034
+ fixture: VMWARE,
1035
+ declaredUnits: { "vcenter.vm.memory.utilization": "%" },
1036
+ series: [
1037
+ {
1038
+ attributes: vmwareAttributes({
1039
+ "resource.vcenter.cluster.name": "prod-cluster",
1040
+ "resource.vcenter.host.name": "esx-01.lab",
1041
+ "resource.vcenter.vm.name": "db-01",
1042
+ "resource.vcenter.vm.id": "5029a1b2-db01",
1043
+ "resource.vcenter.resource_pool.name": "databases",
1044
+ "resource.vcenter.resource_pool.inventory_path":
1045
+ "/DC1/host/prod-cluster/Resources/databases",
1046
+ }),
1047
+ valuesByAlias: {
1048
+ vm_memory_utilization: [92, 93, 94.25, 95, 96.5],
1049
+ },
1050
+ },
1051
+ {
1052
+ attributes: vmwareAttributes({
1053
+ "resource.vcenter.cluster.name": "prod-cluster",
1054
+ "resource.vcenter.host.name": "esx-02.lab",
1055
+ "resource.vcenter.vm.name": "web-01",
1056
+ "resource.vcenter.vm.id": "5029c3d4-web01",
1057
+ }),
1058
+ valuesByAlias: {
1059
+ vm_memory_utilization: [40, 41, 42, 43, 44],
1060
+ },
1061
+ },
1062
+ ],
1063
+ });
1064
+
1065
+ const lines: Array<string> = linesOf(rootCause);
1066
+
1067
+ expect(lines).toContain("**vCenter Details**");
1068
+ expect(lines).toContain(`- vCenter: ${VCENTER}`);
1069
+ expect(lines).toContain(
1070
+ metricLine({
1071
+ fixture: VMWARE,
1072
+ metricName: "vcenter.vm.memory.utilization",
1073
+ }),
1074
+ );
1075
+ expect(lines).toContain("**Affected Resources** (1 total)");
1076
+
1077
+ const head: number = lines.indexOf(
1078
+ "1. **Virtual Machine** `db-01` — **96.50%**",
1079
+ );
1080
+ expect(head).toBeGreaterThanOrEqual(0);
1081
+ expect(lines[head + 1]).toBe(" - Host: `esx-01.lab`");
1082
+ expect(lines[head + 2]).toBe(" - Cluster: `prod-cluster`");
1083
+
1084
+ expect(rootCause).not.toContain("web-01");
1085
+ expect(rootCause).not.toContain("esx-02.lab");
1086
+ expect(rootCause).not.toContain("9650.00%");
1087
+ expect(rootCause).not.toMatch(RAW_NUMBER);
1088
+ });
1089
+
1090
+ test("vmware-resource-pool-memory-swapped: the pool, never a Host borrowed from its datapoint", async () => {
1091
+ const monitorStep: MonitorStep = vmwareStep(
1092
+ "vmware-resource-pool-memory-swapped",
1093
+ );
1094
+
1095
+ expect(MonitorStep.getGroupByAttributeKeys(monitorStep)).toEqual([
1096
+ VMwareGroupByKey.ResourcePool,
1097
+ ]);
1098
+
1099
+ const pool: (
1100
+ name: string,
1101
+ host: string,
1102
+ values: Array<number>,
1103
+ ) => RawSeries = (
1104
+ name: string,
1105
+ host: string,
1106
+ values: Array<number>,
1107
+ ): RawSeries => {
1108
+ return {
1109
+ /*
1110
+ * The pool's datapoint happens to carry a host — but a pool's
1111
+ * VMs can run on different hosts, so the list must not say so.
1112
+ */
1113
+ attributes: vmwareAttributes({
1114
+ "resource.vcenter.cluster.name": "prod-cluster",
1115
+ "resource.vcenter.host.name": host,
1116
+ "resource.vcenter.resource_pool.name": name,
1117
+ "resource.vcenter.resource_pool.inventory_path": `/DC1/host/prod-cluster/Resources/${name}`,
1118
+ }),
1119
+ valuesByAlias: { resource_pool_memory_swapped: values },
1120
+ };
1121
+ };
1122
+
1123
+ const rootCause: string = await rootCauseOf({
1124
+ monitorStep: monitorStep,
1125
+ fixture: VMWARE,
1126
+ declaredUnits: { "vcenter.resource_pool.memory.swapped": "MiBy" },
1127
+ series: [
1128
+ // 512 MiB = 536870912 bytes → "537 MB" on the decimal ladder.
1129
+ pool("batch", "esx-01.lab", [256, 300, 400, 512, 512]),
1130
+ pool("web", "esx-02.lab", [0, 0, 0, 0, 0]),
1131
+ ],
1132
+ });
1133
+
1134
+ const lines: Array<string> = linesOf(rootCause);
1135
+
1136
+ expect(lines).toContain("**Affected Resources** (1 total)");
1137
+
1138
+ /*
1139
+ * Titled by its inventory path (the group-by key); the pool's display
1140
+ * name may or may not precede it.
1141
+ */
1142
+ const poolHead: RegExp =
1143
+ /^1\. \*\*Resource Pool\*\* .*`\/DC1\/host\/prod-cluster\/Resources\/batch`.* — \*\*537 MB\*\*$/;
1144
+
1145
+ const head: number = lines.findIndex((line: string) => {
1146
+ return poolHead.test(line);
1147
+ });
1148
+ expect(head).toBeGreaterThanOrEqual(0);
1149
+ expect(lines[head + 1]).toBe(" - Cluster: `prod-cluster`");
1150
+
1151
+ // Never a Host under a pool.
1152
+ expect(
1153
+ lines.filter((line: string) => {
1154
+ return line.trim().startsWith("- Host:");
1155
+ }),
1156
+ ).toEqual([]);
1157
+ expect(rootCause).not.toContain("esx-01.lab");
1158
+
1159
+ expect(rootCause).not.toContain("Resources/web");
1160
+ expect(rootCause).not.toContain("512 MiB");
1161
+ expect(rootCause).not.toMatch(RAW_NUMBER);
1162
+ });
1163
+ });
1164
+
1165
+ // --- Docker Swarm ---
1166
+
1167
+ function swarmTask(input: {
1168
+ container: string;
1169
+ service: string;
1170
+ node: string;
1171
+ alias: string;
1172
+ values: Array<number>;
1173
+ }): RawSeries {
1174
+ return {
1175
+ attributes: {
1176
+ "resource.container.name": input.container,
1177
+ "resource.container.image.name": `registry.local/${input.service}:1.4.2`,
1178
+ "resource.docker.swarm.cluster.name": SWARM_CLUSTER,
1179
+ "docker.swarm.service.name": input.service,
1180
+ "docker.swarm.node.name": input.node,
1181
+ },
1182
+ valuesByAlias: { [input.alias]: input.values },
1183
+ };
1184
+ }
1185
+
1186
+ describe("Grouped Docker Swarm templates list the affected tasks", () => {
1187
+ test("docker-swarm-task-down: lowest uptime first, as a duration", async () => {
1188
+ const rootCause: string = await rootCauseOf({
1189
+ monitorStep: dockerSwarmStep("docker-swarm-task-down"),
1190
+ fixture: DOCKER_SWARM,
1191
+ declaredUnits: { "container.uptime": "s" },
1192
+ series: [
1193
+ swarmTask({
1194
+ container: "web.1.k3j2h1g0f9e8",
1195
+ service: "web",
1196
+ node: "swarm-worker-1",
1197
+ alias: "container_uptime",
1198
+ values: [35, 65],
1199
+ }),
1200
+ swarmTask({
1201
+ container: "api.1.a1b2c3d4e5f6",
1202
+ service: "api",
1203
+ node: "swarm-worker-2",
1204
+ alias: "container_uptime",
1205
+ values: [7200, 7260],
1206
+ }),
1207
+ swarmTask({
1208
+ container: "web.2.z9y8x7w6v5u4",
1209
+ service: "web",
1210
+ node: "swarm-worker-3",
1211
+ alias: "container_uptime",
1212
+ values: [12.5, 42.5],
1213
+ }),
1214
+ ],
1215
+ });
1216
+
1217
+ const lines: Array<string> = linesOf(rootCause);
1218
+
1219
+ expect(lines).toContain("**Docker Swarm Cluster Details**");
1220
+ expect(lines).toContain(`- Cluster: ${SWARM_CLUSTER}`);
1221
+ expect(lines).toContain("**Affected Tasks** (2 total)");
1222
+ expect(rootCause).not.toContain("**Affected Resources**");
1223
+
1224
+ const first: number = lines.indexOf(
1225
+ "1. **Task** `web.2.z9y8x7w6v5u4` — **12.5 sec**",
1226
+ );
1227
+ expect(first).toBeGreaterThanOrEqual(0);
1228
+ expect(lines[first + 1]).toBe(" - Service: `web`");
1229
+ expect(lines[first + 2]).toBe(" - Node: `swarm-worker-3`");
1230
+
1231
+ expect(lines).toContain("2. **Task** `web.1.k3j2h1g0f9e8` — **35 sec**");
1232
+
1233
+ // The long-running task never breached.
1234
+ expect(rootCause).not.toContain("api.1.a1b2c3d4e5f6");
1235
+ expect(rootCause).not.toMatch(RAW_NUMBER);
1236
+ });
1237
+
1238
+ test('docker-swarm-high-cpu: rendered as %, from the catalog — not the exporter\'s declared "1"', async () => {
1239
+ const rootCause: string = await rootCauseOf({
1240
+ monitorStep: dockerSwarmStep("docker-swarm-high-cpu"),
1241
+ fixture: DOCKER_SWARM,
1242
+ // docker_stats declares "1" but sends 0–100 (100 = one core).
1243
+ declaredUnits: { "container.cpu.utilization": "1" },
1244
+ series: [
1245
+ swarmTask({
1246
+ container: "web.1.k3j2h1g0f9e8",
1247
+ service: "web",
1248
+ node: "swarm-worker-1",
1249
+ alias: "container_cpu",
1250
+ values: [85, 88, 90.25, 92, 95.5],
1251
+ }),
1252
+ swarmTask({
1253
+ container: "api.1.a1b2c3d4e5f6",
1254
+ service: "api",
1255
+ node: "swarm-worker-2",
1256
+ alias: "container_cpu",
1257
+ values: [10, 12, 11, 9, 10],
1258
+ }),
1259
+ swarmTask({
1260
+ container: "worker.1.q1w2e3r4t5y6",
1261
+ service: "worker",
1262
+ node: "swarm-worker-2",
1263
+ alias: "container_cpu",
1264
+ values: [150, 160, 170, 180, 185],
1265
+ }),
1266
+ ],
1267
+ });
1268
+
1269
+ const lines: Array<string> = linesOf(rootCause);
1270
+
1271
+ expect(lines).toContain("**Affected Tasks** (2 total)");
1272
+ expect(lines).toContain(
1273
+ metricLine({
1274
+ fixture: DOCKER_SWARM,
1275
+ metricName: "container.cpu.utilization",
1276
+ }),
1277
+ );
1278
+ expect(lines).toContain(
1279
+ "1. **Task** `worker.1.q1w2e3r4t5y6` — **185.00%**",
1280
+ );
1281
+ expect(lines).toContain("2. **Task** `web.1.k3j2h1g0f9e8` — **95.50%**");
1282
+
1283
+ expect(rootCause).not.toContain("api.1.a1b2c3d4e5f6");
1284
+ expect(rootCause).not.toContain("18500.00%");
1285
+ expect(rootCause).not.toContain("9550.00%");
1286
+ expect(rootCause).not.toMatch(RAW_NUMBER);
1287
+ });
1288
+ });