@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
@@ -1,24 +1,67 @@
1
1
  import { describe, expect, test } from "@jest/globals";
2
2
  import {
3
+ CALLER_CLUSTER_ATTRIBUTE,
4
+ CALLER_NAMESPACE_ATTRIBUTE,
3
5
  ClientSpanDependencyRow,
6
+ DATABASE_ENDPOINT_DESCRIPTIVE_ATTRIBUTE,
7
+ DATABASE_PORT_DESCRIPTIVE_ATTRIBUTE,
8
+ DatabaseCallerContextSql,
9
+ DependencyDatabaseEndpoint,
10
+ DependencyDatabaseServer,
11
+ DependencyDatabaseTarget,
4
12
  DependencyEdgeCollector,
5
13
  DependencyQueryWindow,
6
14
  DependencyTarget,
15
+ MAX_DATABASE_TARGETS_PER_ROW,
16
+ NON_BLANK_TEXT_PATTERN,
17
+ ORDINAL_SUFFIX_PATTERN,
7
18
  PARENT_LOOKBACK_MINUTES,
19
+ PLAIN_HOST_ADDRESS_PATTERN,
20
+ PRIVATE_IP_ADDRESS_PATTERN,
8
21
  SERVICE_GRAPH_REQUEST_FAILED_METRIC,
9
22
  SERVICE_GRAPH_REQUEST_TOTAL_METRIC,
10
23
  buildClientSpanDependencySql,
11
24
  buildServiceGraphMetricSql,
12
25
  buildTraceLinkedDependencySql,
26
+ databaseCallerContextSql,
27
+ databaseInstanceSql,
28
+ describeDependencyDatabaseServer,
13
29
  escapeSql,
30
+ firstNonEmptyAttributeSql,
14
31
  isUuid,
32
+ mergeDependencyEntityDescriptions,
15
33
  mergeDependencySources,
34
+ readDependencyDatabaseTargets,
16
35
  resolveClientSpanTarget,
36
+ resolveDependencyDatabaseEndpoint,
17
37
  resolveServiceGraphPeer,
18
38
  serviceNameCandidatesForHost,
19
39
  toEdgeMetrics,
20
40
  toExtractedDependencyEntity,
21
41
  } from "../../../../Server/Utils/Telemetry/ServiceDependencyDiscovery";
42
+ import {
43
+ CALLER_CLUSTER_ATTRIBUTE as DISCOVERY_CALLER_CLUSTER,
44
+ CALLER_NAMESPACE_ATTRIBUTE as DISCOVERY_CALLER_NAMESPACE,
45
+ NON_BLANK_TEXT_PATTERN as DISCOVERY_NON_BLANK_TEXT_PATTERN,
46
+ ORDINAL_SUFFIX_PATTERN as DISCOVERY_ORDINAL_SUFFIX_PATTERN,
47
+ PLAIN_HOST_ADDRESS_PATTERN as DISCOVERY_PLAIN_HOST_ADDRESS_PATTERN,
48
+ PRIVATE_IP_ADDRESS_PATTERN as DISCOVERY_PRIVATE_IP_ADDRESS_PATTERN,
49
+ buildDatabaseEndpointSql,
50
+ databaseCallerContextSql as discoveryDatabaseCallerContextSql,
51
+ databaseInstanceSql as discoveryDatabaseInstanceSql,
52
+ firstNonEmptyAttributeSql as discoveryFirstNonEmptyAttributeSql,
53
+ getDatabaseEndpointCallerContextNeeds,
54
+ } from "../../../../Server/Utils/Telemetry/DatabaseEndpointDiscovery";
55
+ import {
56
+ DatabaseEndpoint,
57
+ buildDatabaseCallerContext,
58
+ formatDatabaseEndpoint,
59
+ } from "../../../../Types/DatabaseServer/DatabaseEndpoint";
60
+ import {
61
+ DATABASE_ADDRESS_ATTRIBUTES,
62
+ DATABASE_PORT_ATTRIBUTES,
63
+ resolveDatabaseCallTarget,
64
+ } from "../../../../Types/DatabaseServer/DatabaseTelemetryResolver";
22
65
  import EntityRelationshipType from "../../../../Types/Telemetry/EntityRelationshipType";
23
66
  import EntityType from "../../../../Types/Telemetry/EntityType";
24
67
  import { EntityRelationshipEdge } from "../../../../Utils/Telemetry/EntityRelationship";
@@ -523,3 +566,1297 @@ describe("helpers", () => {
523
566
  expect(escapeSql("a'b\\c")).toBe("a\\'b\\\\c");
524
567
  });
525
568
  });
569
+
570
+ /*
571
+ * The database SERVER a Database node's calls reached, for the Service
572
+ * Map's "Open database" link. The node itself stays keyed by what its spans
573
+ * said (engine, host, logical database); the server is described beside
574
+ * it, resolved exactly as ingest keyed the spans.
575
+ */
576
+
577
+ // A stored CLIENT span: its own attributes plus the `resource.`-prefixed ones.
578
+ type StoredAttributes = Record<string, string>;
579
+
580
+ /*
581
+ * The client-span dependency query's per-span columns for ONE stored span,
582
+ * computed the way its inner SELECT computes them (ClickHouse maps answer
583
+ * '' for a missing key, and the caller's placement is kept only where
584
+ * getDatabaseEndpointCallerContextNeeds — the TypeScript twin of
585
+ * databaseCallerContextSql — says it can change the endpoint).
586
+ */
587
+ type SpanColumns = Record<string, string>;
588
+
589
+ function spanColumns(attributes: StoredAttributes): SpanColumns {
590
+ const read: (key: string) => string = (key: string): string => {
591
+ return attributes[key] ?? "";
592
+ };
593
+ const firstNonEmpty: (keys: ReadonlyArray<string>) => string = (
594
+ keys: ReadonlyArray<string>,
595
+ ): string => {
596
+ for (const key of keys) {
597
+ if (read(key) !== "") {
598
+ return read(key);
599
+ }
600
+ }
601
+ return "";
602
+ };
603
+
604
+ const dbSystem: string =
605
+ read("db.system.name") !== "" ? read("db.system.name") : read("db.system");
606
+ const namespace: string = read("db.namespace");
607
+ const instance: string =
608
+ read("db.mssql.instance_name") !== ""
609
+ ? read("db.mssql.instance_name")
610
+ : namespace.includes("|")
611
+ ? namespace.split("|")[0]!
612
+ : "";
613
+ const address: string = firstNonEmpty(DATABASE_ADDRESS_ATTRIBUTES);
614
+ const needs: { namespace: boolean; kubernetes: boolean; cluster: boolean } =
615
+ getDatabaseEndpointCallerContextNeeds(address);
616
+ const callerNamespace: string = read(CALLER_NAMESPACE_ATTRIBUTE);
617
+ const isDatabase: boolean = dbSystem !== "";
618
+
619
+ return {
620
+ dbSystem: dbSystem,
621
+ dbNamespace: namespace !== "" ? namespace : read("db.name"),
622
+ messagingSystem: read("messaging.system"),
623
+ peerService: read("peer.service"),
624
+ rpcSystem: read("rpc.system"),
625
+ rpcService: read("rpc.service"),
626
+ serverAddress: firstNonEmpty([
627
+ "server.address",
628
+ "net.peer.name",
629
+ "http.host",
630
+ ]),
631
+ isHttp:
632
+ read("http.request.method") !== "" || read("http.method") !== ""
633
+ ? "1"
634
+ : "0",
635
+ dbServerAddress: isDatabase ? address : "",
636
+ serverPort: isDatabase ? firstNonEmpty(DATABASE_PORT_ATTRIBUTES) : "",
637
+ dbInstance: isDatabase ? instance : "",
638
+ callerNamespace: isDatabase && needs.namespace ? callerNamespace : "",
639
+ callerInKubernetes:
640
+ isDatabase && needs.kubernetes && callerNamespace.trim() !== ""
641
+ ? "1"
642
+ : "0",
643
+ callerCluster:
644
+ isDatabase && needs.cluster ? read(CALLER_CLUSTER_ATTRIBUTE) : "",
645
+ };
646
+ }
647
+
648
+ // The span's entry of its row's dbTargets, in the query's column order.
649
+ function targetColumns(columns: SpanColumns): Array<string> {
650
+ return [
651
+ columns["dbServerAddress"]!,
652
+ columns["serverPort"]!,
653
+ columns["dbInstance"]!,
654
+ columns["callerNamespace"]!,
655
+ columns["callerInKubernetes"]!,
656
+ columns["callerCluster"]!,
657
+ ];
658
+ }
659
+
660
+ // The dependency row ONE stored span would group into on its own.
661
+ function dependencyRowForSpan(
662
+ attributes: StoredAttributes,
663
+ ): ClientSpanDependencyRow {
664
+ const columns: SpanColumns = spanColumns(attributes);
665
+ return row({
666
+ dbSystem: columns["dbSystem"],
667
+ dbNamespace: columns["dbNamespace"],
668
+ messagingSystem: columns["messagingSystem"],
669
+ peerService: columns["peerService"],
670
+ rpcSystem: columns["rpcSystem"],
671
+ rpcService: columns["rpcService"],
672
+ serverAddress: columns["serverAddress"],
673
+ isHttp: columns["isHttp"],
674
+ dbTargets: columns["dbSystem"] !== "" ? [targetColumns(columns)] : [],
675
+ });
676
+ }
677
+
678
+ // What ingest keys the same span with (DatabaseCallEntityKeys).
679
+ function ingestEndpoint(attributes: StoredAttributes): string | null {
680
+ const resource: Record<string, unknown> = {};
681
+ for (const [key, value] of Object.entries(attributes)) {
682
+ if (key.startsWith("resource.")) {
683
+ resource[key.substring("resource.".length)] = value;
684
+ }
685
+ }
686
+ const target: { endpoint: DatabaseEndpoint } | null =
687
+ resolveDatabaseCallTarget({
688
+ getAttribute: (key: string): unknown => {
689
+ return attributes[key];
690
+ },
691
+ caller: buildDatabaseCallerContext(resource),
692
+ });
693
+ return target ? formatDatabaseEndpoint(target.endpoint) : null;
694
+ }
695
+
696
+ // A database server target, as a row's dbTargets carries it.
697
+ function target(
698
+ overrides: Partial<{
699
+ address: string;
700
+ port: string;
701
+ instance: string;
702
+ namespace: string;
703
+ inKubernetes: string;
704
+ cluster: string;
705
+ }>,
706
+ ): Array<string> {
707
+ return [
708
+ overrides.address ?? "",
709
+ overrides.port ?? "",
710
+ overrides.instance ?? "",
711
+ overrides.namespace ?? "",
712
+ overrides.inKubernetes ?? "0",
713
+ overrides.cluster ?? "",
714
+ ];
715
+ }
716
+
717
+ const CALLERS: Record<string, StoredAttributes> = {
718
+ vm: { "resource.host.name": "vm-7", "resource.os.type": "linux" },
719
+ podProd: {
720
+ "resource.k8s.pod.name": "api-7d9f-x",
721
+ [CALLER_NAMESPACE_ATTRIBUTE]: "shop",
722
+ [CALLER_CLUSTER_ATTRIBUTE]: "prod-eu",
723
+ },
724
+ podOtherNamespace: {
725
+ "resource.k8s.pod.name": "billing-5c-y",
726
+ [CALLER_NAMESPACE_ATTRIBUTE]: "billing",
727
+ [CALLER_CLUSTER_ATTRIBUTE]: "prod-eu",
728
+ },
729
+ podOtherCluster: {
730
+ "resource.k8s.pod.name": "api-7d9f-z",
731
+ [CALLER_NAMESPACE_ATTRIBUTE]: "shop",
732
+ [CALLER_CLUSTER_ATTRIBUTE]: "prod-us",
733
+ },
734
+ podNoCluster: {
735
+ "resource.k8s.pod.name": "api-7d9f-x",
736
+ [CALLER_NAMESPACE_ATTRIBUTE]: "shop",
737
+ },
738
+ };
739
+
740
+ const DATABASE_SPANS: Array<StoredAttributes> = [
741
+ { "db.system.name": "postgresql", "server.address": "orders.example.com" },
742
+ {
743
+ "db.system.name": "postgresql",
744
+ "server.address": "orders.example.com",
745
+ "server.port": "6432",
746
+ },
747
+ {
748
+ "db.system": "postgres",
749
+ "net.peer.name": "db.prod",
750
+ "net.peer.port": "5433",
751
+ },
752
+ {
753
+ "db.system.name": "postgresql",
754
+ "server.address": "Orders.Example.COM.:5439",
755
+ },
756
+ { "db.system.name": "postgresql", "server.address": "postgres" },
757
+ { "db.system.name": "postgresql", "server.address": "postgres.data" },
758
+ { "db.system.name": "mongodb", "server.address": "mongo-0.mongo-headless" },
759
+ {
760
+ "db.system.name": "redis",
761
+ "server.address": "redis-master.cache.svc.cluster.local",
762
+ },
763
+ { "db.system.name": "mysql", "server.address": "10.0.0.5" },
764
+ { "db.system.name": "mysql", "server.address": "db.internal" },
765
+ { "db.system.name": "mysql", "network.peer.address": "10.0.4.7" },
766
+ { "db.system.name": "mysql", "server.address": "[fd00::5]:3307" },
767
+ {
768
+ "db.system.name": "microsoft.sql_server",
769
+ "server.address": "sql1.corp.example.com",
770
+ "db.mssql.instance_name": "INST01",
771
+ },
772
+ {
773
+ "db.system.name": "microsoft.sql_server",
774
+ "server.address": "sql1.corp.example.com",
775
+ "db.namespace": "inst02|orders",
776
+ },
777
+ {
778
+ "db.system.name": "microsoft.sql_server",
779
+ "server.address": "sql1.corp.example.com",
780
+ "server.port": "14330",
781
+ "db.mssql.instance_name": "INST01",
782
+ },
783
+ {
784
+ "db.system.name": "postgresql",
785
+ "server.address": "postgresql://app@postgres/orders",
786
+ },
787
+ { "db.system.name": "postgresql", "server.address": "localhost" },
788
+ { "db.system.name": "redis", "server.address": "host.docker.internal" },
789
+ { "db.system.name": "postgresql", "server.address": "[REDACTED]" },
790
+ { "db.system.name": "cockroachdb", "server.address": "crdb.example.com" },
791
+ ];
792
+
793
+ describe("client span dependency SQL — the database server a node calls", () => {
794
+ const sql: string = collapse(buildClientSpanDependencySql(WINDOW));
795
+ const context: DatabaseCallerContextSql =
796
+ databaseCallerContextSql("dbServerAddress");
797
+
798
+ test("keeps what the ingest resolver reads, with its precedence, for database calls only", () => {
799
+ const only: (expression: string, otherwise?: string) => string = (
800
+ expression: string,
801
+ otherwise: string = "''",
802
+ ): string => {
803
+ return collapse(`if(dbSystem != '', ${expression}, ${otherwise})`);
804
+ };
805
+ expect(sql).toContain(
806
+ `${only(firstNonEmptyAttributeSql(DATABASE_ADDRESS_ATTRIBUTES))} AS dbServerAddress`,
807
+ );
808
+ expect(sql).toContain(
809
+ `${only(firstNonEmptyAttributeSql(DATABASE_PORT_ATTRIBUTES))} AS serverPort`,
810
+ );
811
+ expect(sql).toContain(`${only(databaseInstanceSql())} AS dbInstance`);
812
+ expect(sql).toContain(
813
+ `${only(context.callerNamespace)} AS callerNamespace`,
814
+ );
815
+ expect(sql).toContain(
816
+ `${only(context.callerInKubernetes, "0")} AS callerInKubernetes`,
817
+ );
818
+ expect(sql).toContain(`${only(context.callerCluster)} AS callerCluster`);
819
+ });
820
+
821
+ test("keeps the caller's placement with the very predicates database discovery groups by", () => {
822
+ // The endpoint query's columns are the same builder over its own address.
823
+ const discovery: string = collapse(
824
+ buildDatabaseEndpointSql({ ...WINDOW, maxRows: 10 }),
825
+ );
826
+ const own: DatabaseCallerContextSql =
827
+ databaseCallerContextSql("serverAddress");
828
+ expect(discovery).toContain(
829
+ `${collapse(own.callerNamespace)} AS callerNamespace`,
830
+ );
831
+ expect(discovery).toContain(
832
+ `${collapse(own.callerInKubernetes)} AS callerInKubernetes`,
833
+ );
834
+ expect(discovery).toContain(
835
+ `${collapse(own.callerCluster)} AS callerCluster`,
836
+ );
837
+ // …read here over the database call's own address.
838
+ expect(collapse(context.callerCluster)).toContain(
839
+ `match(lower(dbServerAddress), '${PLAIN_HOST_ADDRESS_PATTERN}')`,
840
+ );
841
+ expect(collapse(context.callerNamespace)).toContain(
842
+ `attributes['${CALLER_NAMESPACE_ATTRIBUTE}'], '' )`,
843
+ );
844
+ expect(collapse(context.callerCluster)).toContain(
845
+ `attributes['${CALLER_CLUSTER_ATTRIBUTE}'], '' )`,
846
+ );
847
+ });
848
+
849
+ test("aggregates the servers a row's calls named instead of grouping on them", () => {
850
+ expect(sql).toContain(
851
+ `groupUniqArrayIf(${MAX_DATABASE_TARGETS_PER_ROW})([dbServerAddress, serverPort, dbInstance, callerNamespace, toString(callerInKubernetes), callerCluster], dbSystem != '') AS dbTargets`,
852
+ );
853
+ // One row per (caller, node): only the columns a node is keyed by.
854
+ expect(sql).toContain(
855
+ "GROUP BY callerServiceId, dbSystem, dbNamespace, messagingSystem, peerService, rpcSystem, rpcService, serverAddress, isHttp ORDER BY callCount DESC LIMIT 77",
856
+ );
857
+ expect(MAX_DATABASE_TARGETS_PER_ROW).toBeGreaterThan(1);
858
+ });
859
+
860
+ test("the columns a node is keyed by are read exactly as before", () => {
861
+ expect(sql).toContain(
862
+ "multiIf(attributes['db.system.name'] != '', attributes['db.system.name'], attributes['db.system']) AS dbSystem",
863
+ );
864
+ expect(sql).toContain(
865
+ "multiIf(attributes['db.namespace'] != '', attributes['db.namespace'], attributes['db.name']) AS dbNamespace",
866
+ );
867
+ expect(sql).toContain(
868
+ "multiIf( attributes['server.address'] != '', attributes['server.address'], attributes['net.peer.name'] != '', attributes['net.peer.name'], attributes['http.host'] != '', attributes['http.host'], attributes['url.full'] != '', domain(attributes['url.full']), attributes['http.url'] != '', domain(attributes['http.url']), '' ) AS serverAddress",
869
+ );
870
+ });
871
+
872
+ test("never reads anything that varies per caller instance", () => {
873
+ for (const perInstance of [
874
+ "k8s.pod.name",
875
+ "k8s.node.name",
876
+ "host.name",
877
+ "container.id",
878
+ "service.instance.id",
879
+ ]) {
880
+ expect(sql).not.toContain(perInstance);
881
+ }
882
+ });
883
+
884
+ test("the shared SQL helpers are the ones DatabaseEndpointDiscovery re-exports", () => {
885
+ expect(firstNonEmptyAttributeSql).toBe(discoveryFirstNonEmptyAttributeSql);
886
+ expect(databaseInstanceSql).toBe(discoveryDatabaseInstanceSql);
887
+ expect(databaseCallerContextSql).toBe(discoveryDatabaseCallerContextSql);
888
+ expect(CALLER_NAMESPACE_ATTRIBUTE).toBe(DISCOVERY_CALLER_NAMESPACE);
889
+ expect(CALLER_CLUSTER_ATTRIBUTE).toBe(DISCOVERY_CALLER_CLUSTER);
890
+ expect(PLAIN_HOST_ADDRESS_PATTERN).toBe(
891
+ DISCOVERY_PLAIN_HOST_ADDRESS_PATTERN,
892
+ );
893
+ expect(PRIVATE_IP_ADDRESS_PATTERN).toBe(
894
+ DISCOVERY_PRIVATE_IP_ADDRESS_PATTERN,
895
+ );
896
+ expect(ORDINAL_SUFFIX_PATTERN).toBe(DISCOVERY_ORDINAL_SUFFIX_PATTERN);
897
+ expect(NON_BLANK_TEXT_PATTERN).toBe(DISCOVERY_NON_BLANK_TEXT_PATTERN);
898
+ });
899
+
900
+ test("keeps a caller's placement only where it can change the server", () => {
901
+ const placements: Array<StoredAttributes> = [
902
+ CALLERS["podProd"]!,
903
+ CALLERS["podOtherNamespace"]!,
904
+ CALLERS["podOtherCluster"]!,
905
+ CALLERS["vm"]!,
906
+ ];
907
+ const distinctTargets: (address: string) => number = (
908
+ address: string,
909
+ ): number => {
910
+ return new Set<string>(
911
+ placements.map((caller: StoredAttributes): string => {
912
+ return JSON.stringify(
913
+ targetColumns(
914
+ spanColumns({
915
+ "db.system.name": "postgresql",
916
+ "server.address": address,
917
+ ...caller,
918
+ }),
919
+ ),
920
+ );
921
+ }),
922
+ ).size;
923
+ };
924
+
925
+ // A global name is one server from anywhere: no placement is kept.
926
+ expect(
927
+ targetColumns(
928
+ spanColumns({
929
+ "db.system.name": "postgresql",
930
+ "server.address": "orders.cluster-x.rds.amazonaws.com",
931
+ ...CALLERS["podProd"],
932
+ }),
933
+ ),
934
+ ).toEqual(target({ address: "orders.cluster-x.rds.amazonaws.com" }));
935
+ expect(distinctTargets("orders.cluster-x.rds.amazonaws.com")).toBe(1);
936
+ // A single-label name: namespace and cluster.
937
+ expect(distinctTargets("postgres")).toBe(4);
938
+ // A StatefulSet member: namespace and cluster.
939
+ expect(distinctTargets("mongo-0.mongo-headless")).toBe(4);
940
+ // A Service name or a private IP: the cluster (or none, from the VM).
941
+ expect(distinctTargets("pg.data.svc.cluster.local")).toBe(3);
942
+ expect(distinctTargets("10.0.0.5")).toBe(3);
943
+ // A two-label name: the cluster, and whether the caller runs in one.
944
+ expect(
945
+ targetColumns(
946
+ spanColumns({
947
+ "db.system.name": "postgresql",
948
+ "server.address": "postgres.data",
949
+ ...CALLERS["podProd"],
950
+ }),
951
+ ),
952
+ ).toEqual(
953
+ target({
954
+ address: "postgres.data",
955
+ inKubernetes: "1",
956
+ cluster: "prod-eu",
957
+ }),
958
+ );
959
+ });
960
+ });
961
+
962
+ describe("readDependencyDatabaseTargets", () => {
963
+ test("reads the query's positional targets", () => {
964
+ expect(
965
+ readDependencyDatabaseTargets(
966
+ row({
967
+ dbSystem: "postgresql",
968
+ dbTargets: [
969
+ target({
970
+ address: "postgres.data",
971
+ port: "5432",
972
+ inKubernetes: "1",
973
+ cluster: "prod-eu",
974
+ }),
975
+ ["sql1", "", "INST01", "shop", "0", ""],
976
+ ],
977
+ }),
978
+ ),
979
+ ).toEqual([
980
+ {
981
+ address: "postgres.data",
982
+ port: "5432",
983
+ instance: "",
984
+ callerNamespace: "",
985
+ callerInKubernetes: true,
986
+ callerCluster: "prod-eu",
987
+ },
988
+ {
989
+ address: "sql1",
990
+ port: "",
991
+ instance: "INST01",
992
+ callerNamespace: "shop",
993
+ callerInKubernetes: false,
994
+ callerCluster: "",
995
+ },
996
+ ] as Array<DependencyDatabaseTarget>);
997
+ });
998
+
999
+ test("skips what is not a target, and reads a missing column as none", () => {
1000
+ expect(
1001
+ readDependencyDatabaseTargets(
1002
+ row({
1003
+ dbSystem: "postgresql",
1004
+ dbTargets: [
1005
+ null,
1006
+ "db.example.com",
1007
+ { address: "db.example.com" },
1008
+ ["db.example.com", 5432],
1009
+ ] as unknown as Array<Array<string>>,
1010
+ }),
1011
+ ),
1012
+ ).toEqual([
1013
+ {
1014
+ address: "db.example.com",
1015
+ port: "5432",
1016
+ instance: "",
1017
+ callerNamespace: "",
1018
+ callerInKubernetes: false,
1019
+ callerCluster: "",
1020
+ },
1021
+ ]);
1022
+ for (const dbTargets of [undefined, null, "[]", {}]) {
1023
+ expect(
1024
+ readDependencyDatabaseTargets(
1025
+ row({
1026
+ dbSystem: "postgresql",
1027
+ dbTargets: dbTargets as unknown as Array<Array<string>>,
1028
+ }),
1029
+ ),
1030
+ ).toEqual([]);
1031
+ }
1032
+ });
1033
+ });
1034
+
1035
+ describe("resolveDependencyDatabaseEndpoint", () => {
1036
+ const resolveSpan: (
1037
+ attributes: StoredAttributes,
1038
+ ) => DependencyDatabaseEndpoint | null = (
1039
+ attributes: StoredAttributes,
1040
+ ): DependencyDatabaseEndpoint | null => {
1041
+ const spanRow: ClientSpanDependencyRow = dependencyRowForSpan(attributes);
1042
+ const targets: Array<DependencyDatabaseTarget> =
1043
+ readDependencyDatabaseTargets(spanRow);
1044
+ expect(targets.length).toBeLessThanOrEqual(1);
1045
+ return targets[0]
1046
+ ? resolveDependencyDatabaseEndpoint({
1047
+ dbSystem: spanRow.dbSystem,
1048
+ target: targets[0],
1049
+ })
1050
+ : null;
1051
+ };
1052
+
1053
+ for (const [callerName, caller] of Object.entries(CALLERS)) {
1054
+ for (const span of DATABASE_SPANS) {
1055
+ const attributes: StoredAttributes = { ...span, ...caller };
1056
+ test(`is the endpoint ingest keyed the span with: ${callerName} → ${JSON.stringify(span)}`, () => {
1057
+ expect(resolveSpan(attributes)?.endpoint ?? null).toBe(
1058
+ ingestEndpoint(attributes),
1059
+ );
1060
+ // …and what the span's row describes its node with.
1061
+ expect(
1062
+ describeDependencyDatabaseServer(dependencyRowForSpan(attributes))
1063
+ ?.endpoint ?? null,
1064
+ ).toBe(ingestEndpoint(attributes));
1065
+ });
1066
+ }
1067
+ }
1068
+
1069
+ test("a Kubernetes short name is the caller's own Service, qualified with its cluster", () => {
1070
+ const shop: DependencyDatabaseEndpoint | null = resolveSpan({
1071
+ "db.system.name": "postgresql",
1072
+ "server.address": "postgres",
1073
+ ...CALLERS["podProd"],
1074
+ });
1075
+ const billing: DependencyDatabaseEndpoint | null = resolveSpan({
1076
+ "db.system.name": "postgresql",
1077
+ "server.address": "postgres",
1078
+ ...CALLERS["podOtherNamespace"],
1079
+ });
1080
+ expect(shop).toEqual({
1081
+ endpoint: "postgres.shop.svc.cluster.local:5432@prod-eu",
1082
+ port: null,
1083
+ });
1084
+ expect(billing?.endpoint).toBe(
1085
+ "postgres.billing.svc.cluster.local:5432@prod-eu",
1086
+ );
1087
+ });
1088
+
1089
+ test("a two-label name from a pod is the Service it names, with only the 'runs in Kubernetes' flag kept", () => {
1090
+ const columns: SpanColumns = spanColumns({
1091
+ "db.system.name": "postgresql",
1092
+ "server.address": "postgres.data",
1093
+ ...CALLERS["podProd"],
1094
+ });
1095
+ expect(columns["callerNamespace"]).toBe("");
1096
+ expect(
1097
+ resolveSpan({
1098
+ "db.system.name": "postgresql",
1099
+ "server.address": "postgres.data",
1100
+ ...CALLERS["podProd"],
1101
+ })?.endpoint,
1102
+ ).toBe("postgres.data.svc.cluster.local:5432@prod-eu");
1103
+ // From a VM the same name is an ordinary DNS name.
1104
+ expect(
1105
+ resolveSpan({
1106
+ "db.system.name": "postgresql",
1107
+ "server.address": "postgres.data",
1108
+ ...CALLERS["vm"],
1109
+ })?.endpoint,
1110
+ ).toBe("postgres.data:5432");
1111
+ });
1112
+
1113
+ test("the port the calls named: the port attribute, else the address's, else none", () => {
1114
+ const resolve: (span: StoredAttributes) => number | null | undefined = (
1115
+ span: StoredAttributes,
1116
+ ): number | null | undefined => {
1117
+ return resolveSpan(span)?.port;
1118
+ };
1119
+ expect(
1120
+ resolve({
1121
+ "db.system.name": "postgresql",
1122
+ "server.address": "db.example.com:6000",
1123
+ "server.port": "6432",
1124
+ }),
1125
+ ).toBe(6432);
1126
+ expect(
1127
+ resolve({
1128
+ "db.system.name": "postgresql",
1129
+ "server.address": "db.example.com:6000",
1130
+ }),
1131
+ ).toBe(6000);
1132
+ expect(
1133
+ resolve({
1134
+ "db.system.name": "postgresql",
1135
+ "server.address": "db.example.com",
1136
+ }),
1137
+ ).toBeNull();
1138
+ // An unusable port attribute is ignored, exactly as the resolver ignores it.
1139
+ expect(
1140
+ resolve({
1141
+ "db.system.name": "postgresql",
1142
+ "server.address": "db.example.com",
1143
+ "server.port": "70000",
1144
+ }),
1145
+ ).toBeNull();
1146
+ expect(
1147
+ resolveDependencyDatabaseEndpoint({
1148
+ dbSystem: "postgresql",
1149
+ target: {
1150
+ address: "db.example.com",
1151
+ port: "6432",
1152
+ instance: "",
1153
+ callerNamespace: "",
1154
+ callerInKubernetes: false,
1155
+ callerCluster: "",
1156
+ },
1157
+ }),
1158
+ ).toEqual({ endpoint: "db.example.com:6432", port: 6432 });
1159
+ });
1160
+
1161
+ test("a SQL Server named instance reached without a port is that instance, never port 1433", () => {
1162
+ expect(
1163
+ resolveSpan({
1164
+ "db.system.name": "microsoft.sql_server",
1165
+ "server.address": "sql1.corp.example.com",
1166
+ "db.mssql.instance_name": "INST01",
1167
+ }),
1168
+ ).toEqual({ endpoint: "sql1.corp.example.com\\inst01", port: null });
1169
+ });
1170
+
1171
+ test("names no server for a call that has none", () => {
1172
+ const none: DependencyDatabaseTarget = {
1173
+ address: "",
1174
+ port: "",
1175
+ instance: "",
1176
+ callerNamespace: "",
1177
+ callerInKubernetes: false,
1178
+ callerCluster: "",
1179
+ };
1180
+ for (const [dbSystem, address] of [
1181
+ ["postgresql", "localhost:5432"],
1182
+ ["redis", "host.docker.internal"],
1183
+ ["postgresql", "[REDACTED]"],
1184
+ ["postgresql", ""],
1185
+ ["", "db.example.com"],
1186
+ [undefined, "db.example.com"],
1187
+ ] as Array<[string | undefined, string]>) {
1188
+ expect(
1189
+ resolveDependencyDatabaseEndpoint({
1190
+ dbSystem: dbSystem,
1191
+ target: { ...none, address: address },
1192
+ }),
1193
+ ).toBeNull();
1194
+ }
1195
+ expect(
1196
+ resolveDependencyDatabaseEndpoint(
1197
+ null as unknown as {
1198
+ dbSystem: string;
1199
+ target: DependencyDatabaseTarget;
1200
+ },
1201
+ ),
1202
+ ).toBeNull();
1203
+ });
1204
+ });
1205
+
1206
+ describe("describeDependencyDatabaseServer", () => {
1207
+ const RDS: string = "orders.cluster-x.rds.amazonaws.com";
1208
+
1209
+ test("one server, however many placements called it", () => {
1210
+ expect(
1211
+ describeDependencyDatabaseServer(
1212
+ row({
1213
+ dbSystem: "postgresql",
1214
+ dbTargets: [target({ address: RDS, port: "5432" })],
1215
+ }),
1216
+ ),
1217
+ ).toEqual({ endpoint: `${RDS}:5432`, port: "5432" });
1218
+ // Targets that differ only in what cannot change the server agree.
1219
+ expect(
1220
+ describeDependencyDatabaseServer(
1221
+ row({
1222
+ dbSystem: "postgresql",
1223
+ dbTargets: [
1224
+ target({ address: RDS, port: "5432" }),
1225
+ target({ address: RDS, port: "5432", namespace: "a" }),
1226
+ target({ address: RDS, port: "5432", cluster: "c2" }),
1227
+ ],
1228
+ }),
1229
+ ),
1230
+ ).toEqual({ endpoint: `${RDS}:5432`, port: "5432" });
1231
+ });
1232
+
1233
+ test("targets that reach different servers leave the row ambiguous ('')", () => {
1234
+ expect(
1235
+ describeDependencyDatabaseServer(
1236
+ row({
1237
+ dbSystem: "postgresql",
1238
+ dbTargets: [
1239
+ target({ address: "pg.data.svc.cluster.local", cluster: "eu" }),
1240
+ target({ address: "pg.data.svc.cluster.local", cluster: "us" }),
1241
+ ],
1242
+ }),
1243
+ ),
1244
+ ).toEqual({ endpoint: "", port: null });
1245
+ // A named port and no named port disagree.
1246
+ expect(
1247
+ describeDependencyDatabaseServer(
1248
+ row({
1249
+ dbSystem: "postgresql",
1250
+ dbTargets: [
1251
+ target({ address: RDS, port: "5432" }),
1252
+ target({ address: RDS }),
1253
+ ],
1254
+ }),
1255
+ ),
1256
+ ).toEqual({ endpoint: `${RDS}:5432`, port: "" });
1257
+ });
1258
+
1259
+ test("a target that names no server contradicts nothing", () => {
1260
+ expect(
1261
+ describeDependencyDatabaseServer(
1262
+ row({
1263
+ dbSystem: "redis",
1264
+ dbTargets: [
1265
+ target({ address: "localhost" }),
1266
+ target({ address: "10.0.0.9", port: "6380", cluster: "prod-eu" }),
1267
+ ],
1268
+ }),
1269
+ ),
1270
+ ).toEqual({ endpoint: "10.0.0.9:6380@prod-eu", port: "6380" });
1271
+ expect(
1272
+ describeDependencyDatabaseServer(
1273
+ row({
1274
+ dbSystem: "redis",
1275
+ dbTargets: [target({ address: "localhost" })],
1276
+ }),
1277
+ ),
1278
+ ).toBeNull();
1279
+ });
1280
+
1281
+ test("a full target list may have been cut short, so it never names one server", () => {
1282
+ const agreeing: Array<Array<string>> = [];
1283
+ for (let index: number = 0; index < MAX_DATABASE_TARGETS_PER_ROW; index++) {
1284
+ agreeing.push(target({ address: RDS, namespace: `ns-${index}` }));
1285
+ }
1286
+ expect(
1287
+ describeDependencyDatabaseServer(
1288
+ row({ dbSystem: "postgresql", dbTargets: agreeing }),
1289
+ ),
1290
+ ).toEqual({ endpoint: "", port: "" });
1291
+ expect(
1292
+ describeDependencyDatabaseServer(
1293
+ row({ dbSystem: "postgresql", dbTargets: agreeing.slice(1) }),
1294
+ ),
1295
+ ).toEqual({ endpoint: `${RDS}:5432`, port: null });
1296
+ });
1297
+
1298
+ test("describes nothing for a row that is not a database call, or has no targets", () => {
1299
+ for (const overrides of [
1300
+ { dbSystem: "", dbTargets: [target({ address: RDS })] },
1301
+ { peerService: "stripe", serverAddress: "api.stripe.com" },
1302
+ { dbSystem: "postgresql", dbTargets: [] },
1303
+ { dbSystem: "postgresql" },
1304
+ ] as Array<Partial<ClientSpanDependencyRow>>) {
1305
+ expect(describeDependencyDatabaseServer(row(overrides))).toBeNull();
1306
+ }
1307
+ expect(
1308
+ describeDependencyDatabaseServer(
1309
+ null as unknown as ClientSpanDependencyRow,
1310
+ ),
1311
+ ).toBeNull();
1312
+ });
1313
+ });
1314
+
1315
+ describe("resolveClientSpanTarget — the database server a node calls", () => {
1316
+ const keyOf: (target: DependencyTarget | null) => string = (
1317
+ target: DependencyTarget | null,
1318
+ ): string => {
1319
+ if (!target || target.kind !== "dependency") {
1320
+ throw new Error("expected a dependency");
1321
+ }
1322
+ return toExtractedDependencyEntity({
1323
+ projectId: PROJECT_ID,
1324
+ entity: target.entity,
1325
+ }).entityKey;
1326
+ };
1327
+
1328
+ const describedEndpoint: (
1329
+ target: DependencyTarget | null,
1330
+ ) => string | undefined = (
1331
+ target: DependencyTarget | null,
1332
+ ): string | undefined => {
1333
+ return target?.kind === "dependency"
1334
+ ? target.entity.descriptiveAttributes[
1335
+ DATABASE_ENDPOINT_DESCRIPTIVE_ATTRIBUTE
1336
+ ]
1337
+ : undefined;
1338
+ };
1339
+
1340
+ test("a database node describes the server its calls reached, beside its unchanged identity", () => {
1341
+ const withServer: DependencyTarget | null = resolveClientSpanTarget(
1342
+ row({
1343
+ dbSystem: "PostgreSQL",
1344
+ dbNamespace: "Orders",
1345
+ serverAddress: "postgres.data",
1346
+ dbTargets: [
1347
+ target({
1348
+ address: "postgres.data",
1349
+ port: "5432",
1350
+ inKubernetes: "1",
1351
+ cluster: "Prod-EU",
1352
+ }),
1353
+ ],
1354
+ }),
1355
+ KNOWN,
1356
+ );
1357
+ expect(withServer).toEqual({
1358
+ kind: "dependency",
1359
+ entity: {
1360
+ entityType: EntityType.Database,
1361
+ identifyingAttributes: {
1362
+ "db.system.name": "postgresql",
1363
+ "server.address": "postgres.data",
1364
+ "db.namespace": "orders",
1365
+ },
1366
+ descriptiveAttributes: {
1367
+ "db.system.name": "postgresql",
1368
+ [DATABASE_ENDPOINT_DESCRIPTIVE_ATTRIBUTE]:
1369
+ "postgres.data.svc.cluster.local:5432@prod-eu",
1370
+ [DATABASE_PORT_DESCRIPTIVE_ATTRIBUTE]: "5432",
1371
+ },
1372
+ },
1373
+ });
1374
+
1375
+ // The same calls without the server column: the same node.
1376
+ const withoutServer: DependencyTarget | null = resolveClientSpanTarget(
1377
+ row({
1378
+ dbSystem: "PostgreSQL",
1379
+ dbNamespace: "Orders",
1380
+ serverAddress: "postgres.data",
1381
+ }),
1382
+ KNOWN,
1383
+ );
1384
+ expect(keyOf(withServer)).toBe(keyOf(withoutServer));
1385
+ });
1386
+
1387
+ test("the attributes are the ones the Service Map's database link reads", () => {
1388
+ expect(DATABASE_ENDPOINT_DESCRIPTIVE_ATTRIBUTE).toBe(
1389
+ "oneuptime.database.endpoint",
1390
+ );
1391
+ expect(DATABASE_PORT_DESCRIPTIVE_ATTRIBUTE).toBe("server.port");
1392
+ });
1393
+
1394
+ test("callers in two clusters reach two servers through ONE node", () => {
1395
+ const fromCluster: (cluster: string) => DependencyTarget | null = (
1396
+ cluster: string,
1397
+ ): DependencyTarget | null => {
1398
+ return resolveClientSpanTarget(
1399
+ dependencyRowForSpan({
1400
+ "db.system.name": "postgresql",
1401
+ "server.address": "postgres.data",
1402
+ [CALLER_NAMESPACE_ATTRIBUTE]: "shop",
1403
+ [CALLER_CLUSTER_ATTRIBUTE]: cluster,
1404
+ }),
1405
+ KNOWN,
1406
+ );
1407
+ };
1408
+ const eu: DependencyTarget | null = fromCluster("prod-eu");
1409
+ const us: DependencyTarget | null = fromCluster("prod-us");
1410
+ expect(keyOf(eu)).toBe(keyOf(us));
1411
+ expect(describedEndpoint(eu)).toBe(
1412
+ "postgres.data.svc.cluster.local:5432@prod-eu",
1413
+ );
1414
+ expect(describedEndpoint(us)).toBe(
1415
+ "postgres.data.svc.cluster.local:5432@prod-us",
1416
+ );
1417
+
1418
+ // One caller in both clusters: one row, two targets — ambiguous.
1419
+ const both: DependencyTarget | null = resolveClientSpanTarget(
1420
+ row({
1421
+ dbSystem: "postgresql",
1422
+ serverAddress: "postgres.data",
1423
+ dbTargets: [
1424
+ target({
1425
+ address: "postgres.data",
1426
+ inKubernetes: "1",
1427
+ cluster: "prod-eu",
1428
+ }),
1429
+ target({
1430
+ address: "postgres.data",
1431
+ inKubernetes: "1",
1432
+ cluster: "prod-us",
1433
+ }),
1434
+ ],
1435
+ }),
1436
+ KNOWN,
1437
+ );
1438
+ expect(keyOf(both)).toBe(keyOf(eu));
1439
+ expect(describedEndpoint(both)).toBe("");
1440
+ });
1441
+
1442
+ test("a call whose server cannot be named carries no description of one", () => {
1443
+ const unnamed: DependencyTarget | null = resolveClientSpanTarget(
1444
+ row({
1445
+ dbSystem: "redis",
1446
+ serverAddress: "host.docker.internal:6379",
1447
+ dbTargets: [target({ address: "host.docker.internal:6379" })],
1448
+ }),
1449
+ KNOWN,
1450
+ );
1451
+ expect(unnamed).toEqual({
1452
+ kind: "dependency",
1453
+ entity: {
1454
+ entityType: EntityType.Database,
1455
+ identifyingAttributes: {
1456
+ "db.system.name": "redis",
1457
+ "server.address": "host.docker.internal",
1458
+ },
1459
+ descriptiveAttributes: { "db.system.name": "redis" },
1460
+ },
1461
+ });
1462
+ });
1463
+
1464
+ test("only database nodes ever describe a database server", () => {
1465
+ for (const overrides of [
1466
+ { peerService: "stripe", serverAddress: "api.stripe.com" },
1467
+ { messagingSystem: "kafka", serverAddress: "kafka-0.kafka:9092" },
1468
+ { serverAddress: "api.stripe.com", isHttp: 1 },
1469
+ { rpcSystem: "grpc", rpcService: "acme.ledger.v1.Ledger" },
1470
+ ] as Array<Partial<ClientSpanDependencyRow>>) {
1471
+ const resolved: DependencyTarget | null = resolveClientSpanTarget(
1472
+ row({
1473
+ ...overrides,
1474
+ dbTargets: [target({ address: "x.example", port: "5432" })],
1475
+ }),
1476
+ KNOWN,
1477
+ );
1478
+ expect(resolved?.kind).toBe("dependency");
1479
+ if (resolved?.kind === "dependency") {
1480
+ const attributes: Record<string, string> =
1481
+ resolved.entity.descriptiveAttributes;
1482
+ expect(DATABASE_ENDPOINT_DESCRIPTIVE_ATTRIBUTE in attributes).toBe(
1483
+ false,
1484
+ );
1485
+ expect(DATABASE_PORT_DESCRIPTIVE_ATTRIBUTE in attributes).toBe(false);
1486
+ }
1487
+ }
1488
+ });
1489
+ });
1490
+
1491
+ /*
1492
+ * The client-span query returns its busiest rows up to a cap. A database
1493
+ * row must stay ONE row per (caller, node) however many placements, pods,
1494
+ * IPs or ports its calls came from — otherwise a busy database's split rows
1495
+ * fill the cap and the calls to everything else (HTTP APIs, gRPC peers,
1496
+ * brokers) silently fall off the Service Map. This simulates the query
1497
+ * over a large estate: its GROUP BY columns are read from the SQL itself,
1498
+ * its dbTargets aggregation is groupUniqArrayIf with the same cap.
1499
+ */
1500
+ describe("client span dependency rows are bounded by the nodes, not the estate", () => {
1501
+ interface SimulatedCalls {
1502
+ service: string;
1503
+ attributes: StoredAttributes;
1504
+ calls: number;
1505
+ }
1506
+
1507
+ interface SimulatedRow {
1508
+ columns: SpanColumns;
1509
+ targets: Array<string>;
1510
+ callCount: number;
1511
+ }
1512
+
1513
+ function runQuery(
1514
+ calls: Array<SimulatedCalls>,
1515
+ maxRows: number,
1516
+ ): Array<ClientSpanDependencyRow> {
1517
+ const sql: string = collapse(
1518
+ buildClientSpanDependencySql({ ...WINDOW, maxRows: maxRows }),
1519
+ );
1520
+ const groupBy: RegExpMatchArray | null = sql.match(
1521
+ /GROUP BY ([A-Za-z, ]+) ORDER BY callCount DESC/,
1522
+ );
1523
+ expect(groupBy).not.toBeNull();
1524
+ const groupColumns: Array<string> = groupBy![1]!.split(", ");
1525
+
1526
+ const groups: Map<string, SimulatedRow> = new Map<string, SimulatedRow>();
1527
+ for (const call of calls) {
1528
+ const columns: SpanColumns = {
1529
+ callerServiceId: call.service,
1530
+ ...spanColumns(call.attributes),
1531
+ };
1532
+ const key: string = JSON.stringify(
1533
+ groupColumns.map((column: string): string => {
1534
+ expect(column in columns).toBe(true);
1535
+ return columns[column]!;
1536
+ }),
1537
+ );
1538
+ const group: SimulatedRow = groups.get(key) || {
1539
+ columns: columns,
1540
+ targets: [],
1541
+ callCount: 0,
1542
+ };
1543
+ const entry: string = JSON.stringify(targetColumns(columns));
1544
+ if (
1545
+ columns["dbSystem"] !== "" &&
1546
+ !group.targets.includes(entry) &&
1547
+ group.targets.length < MAX_DATABASE_TARGETS_PER_ROW
1548
+ ) {
1549
+ group.targets.push(entry);
1550
+ }
1551
+ group.callCount += call.calls;
1552
+ groups.set(key, group);
1553
+ }
1554
+
1555
+ return Array.from(groups.values())
1556
+ .sort((a: SimulatedRow, b: SimulatedRow): number => {
1557
+ return b.callCount - a.callCount;
1558
+ })
1559
+ .slice(0, maxRows)
1560
+ .map((group: SimulatedRow): ClientSpanDependencyRow => {
1561
+ return {
1562
+ callerServiceId: group.columns["callerServiceId"]!,
1563
+ dbSystem: group.columns["dbSystem"],
1564
+ dbNamespace: group.columns["dbNamespace"],
1565
+ messagingSystem: group.columns["messagingSystem"],
1566
+ peerService: group.columns["peerService"],
1567
+ rpcSystem: group.columns["rpcSystem"],
1568
+ rpcService: group.columns["rpcService"],
1569
+ serverAddress: group.columns["serverAddress"],
1570
+ isHttp: group.columns["isHttp"],
1571
+ dbTargets: group.targets.map((entry: string): Array<string> => {
1572
+ return JSON.parse(entry) as Array<string>;
1573
+ }),
1574
+ callCount: group.callCount,
1575
+ errorCount: 0,
1576
+ avgDurationNano: 1000000,
1577
+ };
1578
+ });
1579
+ }
1580
+
1581
+ const SERVICES: number = 10;
1582
+ const NAMESPACES: number = 100;
1583
+ const CLUSTERS: number = 3;
1584
+ const RDS: string = "orders.cluster-x.rds.amazonaws.com";
1585
+
1586
+ function service(index: number): string {
1587
+ return `00000000-0000-4000-8000-${String(index).padStart(12, "0")}`;
1588
+ }
1589
+
1590
+ /*
1591
+ * Namespace-per-tenant: every service runs in 100 namespaces across 3
1592
+ * clusters and calls, from each, a managed Postgres, the namespace's own
1593
+ * `redis` and a Postgres pod IP — busy database calls — while once in a
1594
+ * while calling Stripe over HTTP and a ledger over gRPC.
1595
+ */
1596
+ function estate(): Array<SimulatedCalls> {
1597
+ const calls: Array<SimulatedCalls> = [];
1598
+ for (let s: number = 0; s < SERVICES; s++) {
1599
+ for (let n: number = 0; n < NAMESPACES; n++) {
1600
+ const placement: StoredAttributes = {
1601
+ "resource.k8s.pod.name": `svc-${s}-${n}-pod`,
1602
+ [CALLER_NAMESPACE_ATTRIBUTE]: `tenant-${n}`,
1603
+ [CALLER_CLUSTER_ATTRIBUTE]: `cluster-${n % CLUSTERS}`,
1604
+ };
1605
+ calls.push(
1606
+ {
1607
+ service: service(s),
1608
+ attributes: {
1609
+ "db.system.name": "postgresql",
1610
+ "db.namespace": "orders",
1611
+ "server.address": RDS,
1612
+ "server.port": "5432",
1613
+ ...placement,
1614
+ },
1615
+ calls: 50,
1616
+ },
1617
+ {
1618
+ service: service(s),
1619
+ attributes: {
1620
+ "db.system.name": "redis",
1621
+ "server.address": "redis",
1622
+ ...placement,
1623
+ },
1624
+ calls: 40,
1625
+ },
1626
+ {
1627
+ service: service(s),
1628
+ attributes: {
1629
+ "db.system.name": "postgresql",
1630
+ "db.namespace": "tenant",
1631
+ "network.peer.address": `10.0.${s}.${n}`,
1632
+ "network.peer.port": "5432",
1633
+ ...placement,
1634
+ },
1635
+ calls: 30,
1636
+ },
1637
+ );
1638
+ }
1639
+ calls.push(
1640
+ {
1641
+ service: service(s),
1642
+ attributes: {
1643
+ "server.address": "api.stripe.com",
1644
+ "http.request.method": "POST",
1645
+ },
1646
+ calls: 2,
1647
+ },
1648
+ {
1649
+ service: service(s),
1650
+ attributes: {
1651
+ "rpc.system": "grpc",
1652
+ "rpc.service": "acme.ledger.v1.Ledger",
1653
+ },
1654
+ calls: 1,
1655
+ },
1656
+ );
1657
+ }
1658
+ return calls;
1659
+ }
1660
+
1661
+ test("HTTP and RPC peers survive however numerous the database calls' placements", () => {
1662
+ const rows: Array<ClientSpanDependencyRow> = runQuery(estate(), 1000);
1663
+
1664
+ for (let s: number = 0; s < SERVICES; s++) {
1665
+ const own: Array<ClientSpanDependencyRow> = rows.filter(
1666
+ (candidate: ClientSpanDependencyRow): boolean => {
1667
+ return candidate.callerServiceId === service(s);
1668
+ },
1669
+ );
1670
+ const targets: Array<DependencyTarget | null> = own.map(
1671
+ (candidate: ClientSpanDependencyRow): DependencyTarget | null => {
1672
+ return resolveClientSpanTarget(candidate, KNOWN);
1673
+ },
1674
+ );
1675
+ expect(
1676
+ targets.some((resolved: DependencyTarget | null): boolean => {
1677
+ return (
1678
+ resolved?.kind === "dependency" &&
1679
+ resolved.entity.identifyingAttributes["server.address"] ===
1680
+ "api.stripe.com"
1681
+ );
1682
+ }),
1683
+ ).toBe(true);
1684
+ expect(
1685
+ targets.some((resolved: DependencyTarget | null): boolean => {
1686
+ return (
1687
+ resolved?.kind === "dependency" &&
1688
+ resolved.entity.identifyingAttributes["rpc.service"] ===
1689
+ "acme.ledger.v1.ledger"
1690
+ );
1691
+ }),
1692
+ ).toBe(true);
1693
+ // One row per (caller, node): three databases, three rows.
1694
+ expect(
1695
+ own.filter((candidate: ClientSpanDependencyRow): boolean => {
1696
+ return Boolean(candidate.dbSystem);
1697
+ }),
1698
+ ).toHaveLength(3);
1699
+ }
1700
+ expect(rows).toHaveLength(SERVICES * 5);
1701
+ });
1702
+
1703
+ test("each database row still describes its server — or says it reached several", () => {
1704
+ const rows: Array<ClientSpanDependencyRow> = runQuery(estate(), 1000);
1705
+ const described: Map<string, DependencyDatabaseServer | null> = new Map<
1706
+ string,
1707
+ DependencyDatabaseServer | null
1708
+ >();
1709
+ for (const candidate of rows) {
1710
+ if (candidate.callerServiceId === service(0) && candidate.dbSystem) {
1711
+ described.set(
1712
+ `${candidate.dbSystem} ${candidate.serverAddress}`,
1713
+ describeDependencyDatabaseServer(candidate),
1714
+ );
1715
+ }
1716
+ }
1717
+ expect(Object.fromEntries(described)).toEqual({
1718
+ // Every placement reaches the same managed server.
1719
+ [`postgresql ${RDS}`]: { endpoint: `${RDS}:5432`, port: "5432" },
1720
+ // Each namespace's own `redis`, and a pod IP per tenant: many servers.
1721
+ "redis redis": { endpoint: "", port: "" },
1722
+ "postgresql ": { endpoint: "", port: "" },
1723
+ });
1724
+ });
1725
+
1726
+ test("with a tight cap, the busiest nodes win — never one node's split rows", () => {
1727
+ const rows: Array<ClientSpanDependencyRow> = runQuery(estate(), 30);
1728
+ expect(rows).toHaveLength(30);
1729
+ const nodes: Set<string> = new Set<string>(
1730
+ rows.map((candidate: ClientSpanDependencyRow): string => {
1731
+ return `${candidate.callerServiceId} ${candidate.dbSystem} ${candidate.serverAddress} ${candidate.dbNamespace}`;
1732
+ }),
1733
+ );
1734
+ expect(nodes.size).toBe(30);
1735
+ });
1736
+ });
1737
+
1738
+ describe("mergeDependencyEntityDescriptions", () => {
1739
+ const ENDPOINT: string = DATABASE_ENDPOINT_DESCRIPTIVE_ATTRIBUTE;
1740
+ const PORT: string = DATABASE_PORT_DESCRIPTIVE_ATTRIBUTE;
1741
+
1742
+ function sighting(descriptive?: Record<string, string>): ExtractedEntity {
1743
+ const entity: ExtractedEntity = {
1744
+ entityType: EntityType.Database,
1745
+ entityKey: "0123456789abcdef",
1746
+ identifyingAttributes: {
1747
+ "db.system.name": "postgresql",
1748
+ "server.address": "db.example.com",
1749
+ },
1750
+ };
1751
+ if (descriptive) {
1752
+ entity.descriptiveAttributes = descriptive;
1753
+ }
1754
+ return entity;
1755
+ }
1756
+
1757
+ function merged(
1758
+ ...sightings: Array<ExtractedEntity>
1759
+ ): Record<string, string> | undefined {
1760
+ return sightings.reduce(mergeDependencyEntityDescriptions)
1761
+ .descriptiveAttributes;
1762
+ }
1763
+
1764
+ test("sightings that name the same server keep it", () => {
1765
+ const same: ExtractedEntity = sighting({
1766
+ "db.system.name": "postgresql",
1767
+ [ENDPOINT]: "db.example.com:6432",
1768
+ [PORT]: "6432",
1769
+ });
1770
+ expect(merged(same, same, same)).toEqual({
1771
+ "db.system.name": "postgresql",
1772
+ [ENDPOINT]: "db.example.com:6432",
1773
+ [PORT]: "6432",
1774
+ });
1775
+ });
1776
+
1777
+ test("sightings that reach different servers leave the node ambiguous ('')", () => {
1778
+ expect(
1779
+ merged(
1780
+ sighting({ [ENDPOINT]: "db.example.com:5432" }),
1781
+ sighting({ [ENDPOINT]: "db.example.com:6432", [PORT]: "6432" }),
1782
+ ),
1783
+ ).toEqual({ [ENDPOINT]: "", [PORT]: "" });
1784
+ // Same port, two clusters: the endpoint is ambiguous, the port is not.
1785
+ expect(
1786
+ merged(
1787
+ sighting({
1788
+ [ENDPOINT]: "pg.data.svc.cluster.local:5432@eu",
1789
+ [PORT]: "5432",
1790
+ }),
1791
+ sighting({
1792
+ [ENDPOINT]: "pg.data.svc.cluster.local:5432@us",
1793
+ [PORT]: "5432",
1794
+ }),
1795
+ ),
1796
+ ).toEqual({ [ENDPOINT]: "", [PORT]: "5432" });
1797
+ });
1798
+
1799
+ test("a named port and no named port disagree", () => {
1800
+ expect(
1801
+ merged(
1802
+ sighting({ [ENDPOINT]: "db.example.com:5432", [PORT]: "5432" }),
1803
+ sighting({ [ENDPOINT]: "db.example.com:5432" }),
1804
+ ),
1805
+ ).toEqual({ [ENDPOINT]: "db.example.com:5432", [PORT]: "" });
1806
+ });
1807
+
1808
+ test("once ambiguous, always ambiguous, in any order", () => {
1809
+ const a: ExtractedEntity = sighting({ [ENDPOINT]: "a.example.com:5432" });
1810
+ const b: ExtractedEntity = sighting({ [ENDPOINT]: "b.example.com:5432" });
1811
+ for (const order of [
1812
+ [a, b, a],
1813
+ [a, a, b],
1814
+ [b, a, a],
1815
+ [a, b, b, a],
1816
+ ]) {
1817
+ expect(merged(...order)?.[ENDPOINT]).toBe("");
1818
+ }
1819
+ });
1820
+
1821
+ test("a sighting that names no server contradicts nothing", () => {
1822
+ const named: ExtractedEntity = sighting({
1823
+ "db.system.name": "postgresql",
1824
+ [ENDPOINT]: "db.example.com:5432",
1825
+ });
1826
+ const unnamed: ExtractedEntity = sighting({
1827
+ "db.system.name": "postgresql",
1828
+ });
1829
+ expect(merged(named, unnamed)?.[ENDPOINT]).toBe("db.example.com:5432");
1830
+ expect(merged(unnamed, named)?.[ENDPOINT]).toBe("db.example.com:5432");
1831
+ expect(merged(unnamed, sighting(), unnamed)).toEqual({
1832
+ "db.system.name": "postgresql",
1833
+ });
1834
+ expect(merged(sighting(), sighting())).toBeUndefined();
1835
+ });
1836
+
1837
+ test("every other attribute: the later sighting wins, and the identity is untouched", () => {
1838
+ const earlier: ExtractedEntity = sighting({
1839
+ "network.protocol.name": "tcp",
1840
+ "db.system.name": "postgres",
1841
+ });
1842
+ const later: ExtractedEntity = sighting({ "db.system.name": "postgresql" });
1843
+ const result: ExtractedEntity = mergeDependencyEntityDescriptions(
1844
+ earlier,
1845
+ later,
1846
+ );
1847
+ expect(result.descriptiveAttributes).toEqual({
1848
+ "network.protocol.name": "tcp",
1849
+ "db.system.name": "postgresql",
1850
+ });
1851
+ expect(result.entityKey).toBe(later.entityKey);
1852
+ expect(result.identifyingAttributes).toEqual(later.identifyingAttributes);
1853
+ // Neither input is modified.
1854
+ expect(earlier.descriptiveAttributes).toEqual({
1855
+ "network.protocol.name": "tcp",
1856
+ "db.system.name": "postgres",
1857
+ });
1858
+ expect(later.descriptiveAttributes).toEqual({
1859
+ "db.system.name": "postgresql",
1860
+ });
1861
+ });
1862
+ });