@oneuptime/common 12.0.5 → 12.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 (528) hide show
  1. package/Models/DatabaseModels/AIRun.ts +33 -0
  2. package/Models/DatabaseModels/DataSource.ts +744 -0
  3. package/Models/DatabaseModels/Index.ts +3 -0
  4. package/Models/DatabaseModels/ProjectCallSMSConfig.ts +10 -2
  5. package/Models/DatabaseModels/User.ts +2 -0
  6. package/Models/DatabaseModels/UserOnCallLog.ts +2 -0
  7. package/Models/DatabaseModels/UserOnCallLogTimeline.ts +2 -0
  8. package/Models/DatabaseModels/UserSession.ts +2 -0
  9. package/Models/DatabaseModels/UserTotpAuth.ts +6 -4
  10. package/Models/DatabaseModels/UserWebAuthn.ts +5 -3
  11. package/Server/API/AIInsightAPI.ts +47 -4
  12. package/Server/API/AIInvestigationAPI.ts +25 -0
  13. package/Server/API/DashboardAPI.ts +176 -492
  14. package/Server/API/DataSourceAPI.ts +268 -0
  15. package/Server/API/StatusPageAPI.ts +11 -1
  16. package/Server/API/UserAPI.ts +119 -0
  17. package/Server/Infrastructure/ClickhouseConfig.ts +59 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.ts +39 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.ts +24 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.ts +76 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
  22. package/Server/Infrastructure/Queue.ts +26 -0
  23. package/Server/Infrastructure/QueueWorker.ts +17 -0
  24. package/Server/Middleware/TelemetryIngest.ts +8 -3
  25. package/Server/Services/AIInsightService.ts +132 -12
  26. package/Server/Services/AIRunService.ts +34 -0
  27. package/Server/Services/AIService.ts +17 -2
  28. package/Server/Services/AlertService.ts +38 -32
  29. package/Server/Services/AnalyticsDatabaseService.ts +8 -4
  30. package/Server/Services/DataSourceService.ts +200 -0
  31. package/Server/Services/HostService.ts +92 -0
  32. package/Server/Services/IncidentService.ts +45 -0
  33. package/Server/Services/Index.ts +2 -0
  34. package/Server/Services/MonitorService.ts +49 -0
  35. package/Server/Services/MonitorStatusService.ts +166 -0
  36. package/Server/Services/OpenTelemetryIngestService.ts +255 -184
  37. package/Server/Services/RumSessionPinService.ts +7 -21
  38. package/Server/Services/StatusPageSubscriberService.ts +13 -0
  39. package/Server/Services/UserService.ts +280 -9
  40. package/Server/Static/Vendor/README.md +62 -0
  41. package/Server/Static/Vendor/fonts/InterVariable.woff2 +0 -0
  42. package/Server/Static/Vendor/highlight/highlight.min.js +1244 -0
  43. package/Server/Static/Vendor/highlight/languages/apache.min.js +15 -0
  44. package/Server/Static/Vendor/highlight/languages/bash.min.js +21 -0
  45. package/Server/Static/Vendor/highlight/languages/c.min.js +41 -0
  46. package/Server/Static/Vendor/highlight/languages/cpp.min.js +47 -0
  47. package/Server/Static/Vendor/highlight/languages/csharp.min.js +49 -0
  48. package/Server/Static/Vendor/highlight/languages/css.min.js +31 -0
  49. package/Server/Static/Vendor/highlight/languages/diff.min.js +9 -0
  50. package/Server/Static/Vendor/highlight/languages/dns.min.js +11 -0
  51. package/Server/Static/Vendor/highlight/languages/dockerfile.min.js +8 -0
  52. package/Server/Static/Vendor/highlight/languages/go.min.js +20 -0
  53. package/Server/Static/Vendor/highlight/languages/graphql.min.js +12 -0
  54. package/Server/Static/Vendor/highlight/languages/http.min.js +14 -0
  55. package/Server/Static/Vendor/highlight/languages/ini.min.js +15 -0
  56. package/Server/Static/Vendor/highlight/languages/java.min.js +38 -0
  57. package/Server/Static/Vendor/highlight/languages/javascript.min.js +81 -0
  58. package/Server/Static/Vendor/highlight/languages/json.min.js +8 -0
  59. package/Server/Static/Vendor/highlight/languages/kotlin.min.js +46 -0
  60. package/Server/Static/Vendor/highlight/languages/lua.min.js +15 -0
  61. package/Server/Static/Vendor/highlight/languages/makefile.min.js +14 -0
  62. package/Server/Static/Vendor/highlight/languages/nginx.min.js +21 -0
  63. package/Server/Static/Vendor/highlight/languages/perl.min.js +41 -0
  64. package/Server/Static/Vendor/highlight/languages/php.min.js +59 -0
  65. package/Server/Static/Vendor/highlight/languages/powershell.min.js +39 -0
  66. package/Server/Static/Vendor/highlight/languages/python.min.js +42 -0
  67. package/Server/Static/Vendor/highlight/languages/r.min.js +26 -0
  68. package/Server/Static/Vendor/highlight/languages/ruby.min.js +54 -0
  69. package/Server/Static/Vendor/highlight/languages/rust.min.js +27 -0
  70. package/Server/Static/Vendor/highlight/languages/scala.min.js +28 -0
  71. package/Server/Static/Vendor/highlight/languages/sql.min.js +21 -0
  72. package/Server/Static/Vendor/highlight/languages/swift.min.js +68 -0
  73. package/Server/Static/Vendor/highlight/languages/typescript.min.js +99 -0
  74. package/Server/Static/Vendor/highlight/languages/xml.min.js +29 -0
  75. package/Server/Static/Vendor/highlight/languages/yaml.min.js +28 -0
  76. package/Server/Static/Vendor/highlight/styles/github-dark.min.css +10 -0
  77. package/Server/Static/Vendor/highlight/styles/vs2015.min.css +1 -0
  78. package/Server/Static/Vendor/tailwind/tailwind-3.4.5.js +21028 -0
  79. package/Server/Types/Database/Permissions/BasePermission.ts +7 -0
  80. package/Server/Types/Database/Permissions/DeletePermission.ts +2 -0
  81. package/Server/Types/Database/Permissions/ReadPermission.ts +3 -0
  82. package/Server/Types/Database/Permissions/TenantPermission.ts +101 -16
  83. package/Server/Utils/AI/CodeFix/FixRunBudget.ts +2 -2
  84. package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +90 -11
  85. package/Server/Utils/AI/SRE/AlertInvestigationRunner.ts +1 -0
  86. package/Server/Utils/AI/SRE/IncidentInvestigationRunner.ts +1 -0
  87. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.ts +125 -0
  88. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +25 -15
  89. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +256 -57
  90. package/Server/Utils/AI/SRE/Insights/InsightStore.ts +76 -18
  91. package/Server/Utils/AI/SRE/InvestigationTldr.ts +352 -0
  92. package/Server/Utils/AnalyticsDatabase/InsertDedupContext.ts +7 -2
  93. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +1793 -0
  94. package/Server/Utils/Dashboard/PublicDashboardViewConfig.ts +65 -0
  95. package/Server/Utils/DataSource/ConnectorRegistry.ts +53 -0
  96. package/Server/Utils/DataSource/Connectors/ClickHouseConnector.ts +338 -0
  97. package/Server/Utils/DataSource/Connectors/ElasticsearchConnector.ts +717 -0
  98. package/Server/Utils/DataSource/Connectors/LokiConnector.ts +606 -0
  99. package/Server/Utils/DataSource/Connectors/MySqlConnector.ts +307 -0
  100. package/Server/Utils/DataSource/Connectors/PostgresConnector.ts +260 -0
  101. package/Server/Utils/DataSource/Connectors/PrometheusConnector.ts +555 -0
  102. package/Server/Utils/DataSource/Connectors/RestApiConnector.ts +597 -0
  103. package/Server/Utils/DataSource/Connectors/SqlServerConnector.ts +318 -0
  104. package/Server/Utils/DataSource/EgressGuard.ts +446 -0
  105. package/Server/Utils/DataSource/HttpFetch.ts +221 -0
  106. package/Server/Utils/DataSource/SqlQueryGuard.ts +383 -0
  107. package/Server/Utils/DataSource/SqlResultShaper.ts +372 -0
  108. package/Server/Utils/DataSource/Types.ts +93 -0
  109. package/Server/Utils/Database/PostgresErrorTranslator.ts +172 -17
  110. package/Server/Utils/InProcessMemo.ts +141 -0
  111. package/Server/Utils/InstanceHealth/TelemetryIngestion.ts +628 -0
  112. package/Server/Utils/Monitor/MonitorMetricUtil.ts +58 -0
  113. package/Server/Utils/Monitor/MonitorResource.ts +14 -0
  114. package/Server/Utils/Response.ts +12 -1
  115. package/Server/Utils/SSRFProtection.ts +237 -0
  116. package/Server/Utils/StartServer.ts +8 -0
  117. package/Server/Utils/StatusPageSubscriberWebhook.ts +30 -0
  118. package/Server/Utils/Telemetry/CaptureSpan.ts +30 -18
  119. package/Server/Utils/Telemetry/IngestionTimestamp.ts +75 -0
  120. package/Server/Utils/Telemetry/ResourceHeartbeat.ts +99 -7
  121. package/Server/Utils/Telemetry/Telemetry.ts +112 -1
  122. package/Server/Utils/Telemetry.ts +104 -3
  123. package/Server/Utils/VendorAssets.ts +165 -0
  124. package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +28 -5
  125. package/Server/Utils/Workspace/Slack/Slack.ts +5 -0
  126. package/Server/Views/Partials/Head.ejs +21 -7
  127. package/Tests/App/AdminDashboard/AdminProjectsIdFilter.test.tsx +207 -0
  128. package/Tests/App/AdminDashboard/AdminUsersIdFilter.test.tsx +216 -0
  129. package/Tests/App/Dashboard/AIInsightViewActions.test.tsx +422 -0
  130. package/Tests/App/Dashboard/AddWidgetModal.test.tsx +10 -3
  131. package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +387 -0
  132. package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +199 -0
  133. package/Tests/App/Dashboard/DataSourceChartSyntheticConfig.test.tsx +736 -0
  134. package/Tests/App/Dashboard/DataSourceQueryEditor.test.tsx +868 -0
  135. package/Tests/App/Dashboard/DataSourceWidgetCatalog.test.ts +440 -0
  136. package/Tests/App/Dashboard/DataSourceWidgetFetching.test.tsx +2109 -0
  137. package/Tests/App/Dashboard/DataSourceWidgetViews.test.tsx +726 -0
  138. package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +447 -0
  139. package/Tests/App/Dashboard/InvestigationPanel.test.tsx +514 -3
  140. package/Tests/App/Dashboard/ProductMenuBreadcrumbs.test.tsx +391 -0
  141. package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +326 -0
  142. package/Tests/App/Dashboard/SideMenuHarness.tsx +187 -0
  143. package/Tests/App/Dashboard/WidgetCatalog.test.ts +6 -1
  144. package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +1858 -0
  145. package/Tests/Models/DatabaseModels/DataSourceModelSecrets.test.ts +70 -0
  146. package/Tests/Server/API/AIInvestigationAPI.test.ts +187 -0
  147. package/Tests/Server/API/DashboardPublicDataSourceStrip.test.ts +686 -0
  148. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +585 -823
  149. package/Tests/Server/API/DataSourceAPIRoutes.test.ts +486 -0
  150. package/Tests/Server/API/StatusPageSubscriberOwnership.test.ts +291 -0
  151. package/Tests/Server/API/UserAuthenticationAPI.test.ts +641 -0
  152. package/Tests/Server/Infrastructure/ClickhouseKeepAliveOptions.test.ts +441 -0
  153. package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +140 -0
  154. package/Tests/Server/Services/AIInsightService.test.ts +266 -11
  155. package/Tests/Server/Services/AIRunAnalysisTldrPersistence.test.ts +160 -0
  156. package/Tests/Server/Services/AIServiceDailyBudget.test.ts +4 -0
  157. package/Tests/Server/Services/AlertMetricResourceAttributes.test.ts +352 -0
  158. package/Tests/Server/Services/DataSourceServiceSettings.test.ts +244 -0
  159. package/Tests/Server/Services/HostServiceFindOrCreateMemo.test.ts +397 -0
  160. package/Tests/Server/Services/HostServiceUpdateLastSeen.test.ts +41 -2
  161. package/Tests/Server/Services/IncidentMetricResourceAttributes.test.ts +408 -0
  162. package/Tests/Server/Services/MonitorStatusServiceDeleteReferenceCleanup.test.ts +351 -0
  163. package/Tests/Server/Services/MonitorStatusServiceValidation.test.ts +171 -0
  164. package/Tests/Server/Services/OpenTelemetryResourceRetentionMemo.test.ts +398 -0
  165. package/Tests/Server/Services/OpenTelemetryServiceResolutionCache.test.ts +19 -0
  166. package/Tests/Server/Services/ProjectCallSMSConfigCredentialUniqueness.test.ts +305 -0
  167. package/Tests/Server/Services/ResourceUpdateLastSeenLivenessFallback.test.ts +36 -1
  168. package/Tests/Server/Services/RumSessionPinUniqueViolationRecovery.test.ts +163 -0
  169. package/Tests/Server/Services/UserAuthenticationService.test.ts +973 -0
  170. package/Tests/Server/Types/Database/ObjectIdColumnQuery.test.ts +262 -0
  171. package/Tests/Server/Types/Database/Permissions/TenantPermission.test.ts +358 -0
  172. package/Tests/Server/Utils/AI/FixRunBudget.test.ts +2 -2
  173. package/Tests/Server/Utils/AI/Insights/ExceptionSpikeDetector.test.ts +6 -1
  174. package/Tests/Server/Utils/AI/Insights/InsightStore.test.ts +121 -4
  175. package/Tests/Server/Utils/AI/Insights/NewExceptionDetector.test.ts +361 -10
  176. package/Tests/Server/Utils/AI/InvestigationSettlement.test.ts +292 -0
  177. package/Tests/Server/Utils/AI/InvestigationTldr.test.ts +623 -0
  178. package/Tests/Server/Utils/AI/InvestigationTldrWiring.test.ts +98 -0
  179. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +857 -0
  180. package/Tests/Server/Utils/Dashboard/PublicDashboardViewConfig.test.ts +480 -0
  181. package/Tests/Server/Utils/DataSource/ClickHouseConnector.test.ts +759 -0
  182. package/Tests/Server/Utils/DataSource/ConnectorRegistry.test.ts +37 -0
  183. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +645 -0
  184. package/Tests/Server/Utils/DataSource/ElasticsearchConnector.test.ts +1206 -0
  185. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +616 -0
  186. package/Tests/Server/Utils/DataSource/LokiConnector.test.ts +970 -0
  187. package/Tests/Server/Utils/DataSource/MySqlConnector.test.ts +935 -0
  188. package/Tests/Server/Utils/DataSource/PostgresConnector.test.ts +854 -0
  189. package/Tests/Server/Utils/DataSource/PrometheusConnector.test.ts +881 -0
  190. package/Tests/Server/Utils/DataSource/RestApiConnector.test.ts +1020 -0
  191. package/Tests/Server/Utils/DataSource/SqlQueryGuard.test.ts +425 -0
  192. package/Tests/Server/Utils/DataSource/SqlResultShaper.test.ts +555 -0
  193. package/Tests/Server/Utils/DataSource/SqlServerConnector.test.ts +934 -0
  194. package/Tests/Server/Utils/Database/PostgresErrorTranslator.test.ts +228 -7
  195. package/Tests/Server/Utils/InProcessMemo.test.ts +292 -0
  196. package/Tests/Server/Utils/InstanceHealth/TelemetryIngestion.test.ts +960 -0
  197. package/Tests/Server/Utils/MicrosoftTeamsWebhookUrlValidation.test.ts +103 -0
  198. package/Tests/Server/Utils/Monitor/MonitorMetricResourceAttributes.test.ts +336 -0
  199. package/Tests/Server/Utils/OfflineAssetHygiene.test.ts +521 -0
  200. package/Tests/Server/Utils/ResponseRenderAnalytics.test.ts +143 -0
  201. package/Tests/Server/Utils/SSRFProtection.test.ts +193 -0
  202. package/Tests/Server/Utils/StatusPageSubscriberWebhook.test.ts +101 -0
  203. package/Tests/Server/Utils/TailwindBuildCopy.test.ts +225 -0
  204. package/Tests/Server/Utils/Telemetry/ResourceHeartbeatL1Memo.test.ts +355 -0
  205. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriter.test.ts +128 -0
  206. package/Tests/Server/Utils/Telemetry/TelemetryUtilAttributeKeys.test.ts +381 -0
  207. package/Tests/Server/Utils/Telemetry.test.ts +87 -0
  208. package/Tests/Server/Utils/VendorAssetIntegrity.test.ts +343 -0
  209. package/Tests/Server/Utils/VendorAssets.test.ts +671 -0
  210. package/Tests/Server/Views/OfflineRender.test.ts +297 -0
  211. package/Tests/Types/Dashboard/DataSourceComponentTypes.test.ts +618 -0
  212. package/Tests/Types/DateClickhouseFormat.test.ts +192 -0
  213. package/Tests/Types/Password.test.ts +363 -0
  214. package/Tests/UI/Components/ConfirmModal.test.tsx +7 -0
  215. package/Tests/UI/Components/ErrorBoundary.test.tsx +53 -0
  216. package/Tests/UI/Components/Modal.test.tsx +281 -1
  217. package/Tests/UI/Components/ObjectIdFilterPipeline.test.tsx +396 -0
  218. package/Tests/UI/Components/StatusPage/ResourceGroupNavigator.test.tsx +1147 -0
  219. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +116 -1
  220. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +2 -0
  221. package/Tests/Utils/Analytics.test.ts +23 -0
  222. package/Tests/Utils/Dashboard/Components/DashboardDataSourceComponents.test.ts +640 -0
  223. package/Tests/Utils/DataSource/DataSourceQueryText.test.ts +138 -0
  224. package/Tests/Utils/DataSource/DataSourceValueReducer.test.ts +526 -0
  225. package/Tests/Utils/Metrics/MetricResourceAttributeUtil.test.ts +691 -0
  226. package/Tests/Utils/StatusPage/GroupHierarchyView.test.ts +1452 -0
  227. package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +931 -0
  228. package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +288 -0
  229. package/Tests/Utils/Telemetry/IngestionTimestamp.test.ts +170 -0
  230. package/Types/AI/AIInsightEvidence.ts +6 -0
  231. package/Types/Analytics/RevenueEvent.ts +31 -0
  232. package/Types/Dashboard/DashboardComponentType.ts +31 -0
  233. package/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.ts +27 -0
  234. package/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.ts +32 -0
  235. package/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.ts +30 -0
  236. package/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.ts +55 -0
  237. package/Types/DataSource/DataSourceLimits.ts +62 -0
  238. package/Types/DataSource/DataSourceQueryConfig.ts +41 -0
  239. package/Types/DataSource/DataSourceTableResult.ts +38 -0
  240. package/Types/DataSource/DataSourceType.ts +213 -0
  241. package/Types/Date.ts +43 -3
  242. package/Types/Password.ts +64 -0
  243. package/Types/Permission.ts +46 -0
  244. package/Types/UserAuthenticationStatus.ts +34 -0
  245. package/UI/Components/Button/Button.tsx +1 -1
  246. package/UI/Components/ErrorBoundary.tsx +459 -108
  247. package/UI/Components/Icon/Icon.tsx +10 -9
  248. package/UI/Components/Modal/ConfirmModal.tsx +6 -1
  249. package/UI/Components/Modal/Modal.tsx +267 -35
  250. package/UI/Components/Modal/ModalFooter.tsx +6 -1
  251. package/UI/Components/MoreMenu/MoreMenu.tsx +19 -4
  252. package/UI/Components/MoreMenu/MoreMenuItem.tsx +6 -1
  253. package/UI/Components/StatusPage/ResourceGrid.tsx +314 -0
  254. package/UI/Components/StatusPage/ResourceGroupNavigator.tsx +808 -0
  255. package/UI/Components/StatusPage/ResourceList.tsx +570 -0
  256. package/UI/Styles/Theme.css +18 -0
  257. package/UI/Utils/API/RequestOptions.ts +7 -0
  258. package/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.ts +1 -0
  259. package/UI/Utils/ModelAPI/ModelAPI.ts +1 -0
  260. package/UI/Utils/TestDataSource.ts +61 -0
  261. package/UI/esbuild-config.js +45 -0
  262. package/Utils/Analytics.ts +15 -0
  263. package/Utils/Dashboard/Components/DashboardDataSourceChartComponent.ts +88 -0
  264. package/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.ts +123 -0
  265. package/Utils/Dashboard/Components/DashboardDataSourceShared.ts +16 -0
  266. package/Utils/Dashboard/Components/DashboardDataSourceTableComponent.ts +72 -0
  267. package/Utils/Dashboard/Components/DashboardDataSourceValueComponent.ts +126 -0
  268. package/Utils/Dashboard/Components/Index.ts +28 -0
  269. package/Utils/DataSource/DataSourceQueryText.ts +94 -0
  270. package/Utils/DataSource/DataSourceValueReducer.ts +156 -0
  271. package/Utils/Metrics/MetricResourceAttributeUtil.ts +380 -0
  272. package/Utils/NetworkDiscovery/ScanTargetUtil.ts +8 -6
  273. package/Utils/StatusPage/GroupHierarchyView.ts +931 -0
  274. package/Utils/StatusPage/ResourceExplorer.ts +853 -0
  275. package/build/dist/Models/DatabaseModels/AIRun.js +34 -0
  276. package/build/dist/Models/DatabaseModels/AIRun.js.map +1 -1
  277. package/build/dist/Models/DatabaseModels/DataSource.js +774 -0
  278. package/build/dist/Models/DatabaseModels/DataSource.js.map +1 -0
  279. package/build/dist/Models/DatabaseModels/Index.js +2 -0
  280. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  281. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js +14 -4
  282. package/build/dist/Models/DatabaseModels/ProjectCallSMSConfig.js.map +1 -1
  283. package/build/dist/Models/DatabaseModels/User.js +2 -0
  284. package/build/dist/Models/DatabaseModels/User.js.map +1 -1
  285. package/build/dist/Models/DatabaseModels/UserOnCallLog.js +2 -0
  286. package/build/dist/Models/DatabaseModels/UserOnCallLog.js.map +1 -1
  287. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js +2 -0
  288. package/build/dist/Models/DatabaseModels/UserOnCallLogTimeline.js.map +1 -1
  289. package/build/dist/Models/DatabaseModels/UserSession.js +2 -0
  290. package/build/dist/Models/DatabaseModels/UserSession.js.map +1 -1
  291. package/build/dist/Models/DatabaseModels/UserTotpAuth.js +6 -4
  292. package/build/dist/Models/DatabaseModels/UserTotpAuth.js.map +1 -1
  293. package/build/dist/Models/DatabaseModels/UserWebAuthn.js +5 -3
  294. package/build/dist/Models/DatabaseModels/UserWebAuthn.js.map +1 -1
  295. package/build/dist/Server/API/AIInsightAPI.js +34 -4
  296. package/build/dist/Server/API/AIInsightAPI.js.map +1 -1
  297. package/build/dist/Server/API/AIInvestigationAPI.js +23 -0
  298. package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
  299. package/build/dist/Server/API/DashboardAPI.js +119 -433
  300. package/build/dist/Server/API/DashboardAPI.js.map +1 -1
  301. package/build/dist/Server/API/DataSourceAPI.js +173 -0
  302. package/build/dist/Server/API/DataSourceAPI.js.map +1 -0
  303. package/build/dist/Server/API/StatusPageAPI.js +11 -1
  304. package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
  305. package/build/dist/Server/API/UserAPI.js +94 -2
  306. package/build/dist/Server/API/UserAPI.js.map +1 -1
  307. package/build/dist/Server/Infrastructure/ClickhouseConfig.js +49 -0
  308. package/build/dist/Server/Infrastructure/ClickhouseConfig.js.map +1 -1
  309. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js +20 -0
  310. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786303472848-AddDataSourceTable.js.map +1 -0
  311. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js +19 -0
  312. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786600000000-AddInvestigationAnalysisTldr.js.map +1 -0
  313. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js +64 -0
  314. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1786700000000-DropProjectCallSMSConfigCredentialUniques.js.map +1 -0
  315. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
  316. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  317. package/build/dist/Server/Infrastructure/Queue.js +27 -0
  318. package/build/dist/Server/Infrastructure/Queue.js.map +1 -1
  319. package/build/dist/Server/Infrastructure/QueueWorker.js +13 -0
  320. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  321. package/build/dist/Server/Middleware/TelemetryIngest.js +8 -3
  322. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  323. package/build/dist/Server/Services/AIInsightService.js +110 -10
  324. package/build/dist/Server/Services/AIInsightService.js.map +1 -1
  325. package/build/dist/Server/Services/AIRunService.js +34 -0
  326. package/build/dist/Server/Services/AIRunService.js.map +1 -1
  327. package/build/dist/Server/Services/AIService.js +16 -2
  328. package/build/dist/Server/Services/AIService.js.map +1 -1
  329. package/build/dist/Server/Services/AlertService.js +28 -35
  330. package/build/dist/Server/Services/AlertService.js.map +1 -1
  331. package/build/dist/Server/Services/AnalyticsDatabaseService.js +8 -4
  332. package/build/dist/Server/Services/AnalyticsDatabaseService.js.map +1 -1
  333. package/build/dist/Server/Services/DataSourceService.js +159 -0
  334. package/build/dist/Server/Services/DataSourceService.js.map +1 -0
  335. package/build/dist/Server/Services/HostService.js +74 -0
  336. package/build/dist/Server/Services/HostService.js.map +1 -1
  337. package/build/dist/Server/Services/IncidentService.js +35 -24
  338. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  339. package/build/dist/Server/Services/Index.js +2 -0
  340. package/build/dist/Server/Services/Index.js.map +1 -1
  341. package/build/dist/Server/Services/MonitorService.js +45 -0
  342. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  343. package/build/dist/Server/Services/MonitorStatusService.js +129 -0
  344. package/build/dist/Server/Services/MonitorStatusService.js.map +1 -1
  345. package/build/dist/Server/Services/OpenTelemetryIngestService.js +222 -159
  346. package/build/dist/Server/Services/OpenTelemetryIngestService.js.map +1 -1
  347. package/build/dist/Server/Services/RumSessionPinService.js +7 -15
  348. package/build/dist/Server/Services/RumSessionPinService.js.map +1 -1
  349. package/build/dist/Server/Services/StatusPageSubscriberService.js +10 -0
  350. package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
  351. package/build/dist/Server/Services/UserService.js +241 -10
  352. package/build/dist/Server/Services/UserService.js.map +1 -1
  353. package/build/dist/Server/Types/Database/Permissions/BasePermission.js +6 -0
  354. package/build/dist/Server/Types/Database/Permissions/BasePermission.js.map +1 -1
  355. package/build/dist/Server/Types/Database/Permissions/DeletePermission.js +1 -0
  356. package/build/dist/Server/Types/Database/Permissions/DeletePermission.js.map +1 -1
  357. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +2 -0
  358. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
  359. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +68 -12
  360. package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
  361. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js +2 -2
  362. package/build/dist/Server/Utils/AI/CodeFix/FixRunBudget.js.map +1 -1
  363. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +68 -8
  364. package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
  365. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js +1 -0
  366. package/build/dist/Server/Utils/AI/SRE/AlertInvestigationRunner.js.map +1 -1
  367. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js +1 -0
  368. package/build/dist/Server/Utils/AI/SRE/IncidentInvestigationRunner.js.map +1 -1
  369. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js +97 -0
  370. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionIdentity.js.map +1 -0
  371. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +17 -15
  372. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  373. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +178 -54
  374. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  375. package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js +65 -16
  376. package/build/dist/Server/Utils/AI/SRE/Insights/InsightStore.js.map +1 -1
  377. package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js +303 -0
  378. package/build/dist/Server/Utils/AI/SRE/InvestigationTldr.js.map +1 -0
  379. package/build/dist/Server/Utils/AnalyticsDatabase/InsertDedupContext.js.map +1 -1
  380. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +1261 -0
  381. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -0
  382. package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js +47 -0
  383. package/build/dist/Server/Utils/Dashboard/PublicDashboardViewConfig.js.map +1 -0
  384. package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js +42 -0
  385. package/build/dist/Server/Utils/DataSource/ConnectorRegistry.js.map +1 -0
  386. package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js +187 -0
  387. package/build/dist/Server/Utils/DataSource/Connectors/ClickHouseConnector.js.map +1 -0
  388. package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js +493 -0
  389. package/build/dist/Server/Utils/DataSource/Connectors/ElasticsearchConnector.js.map +1 -0
  390. package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js +399 -0
  391. package/build/dist/Server/Utils/DataSource/Connectors/LokiConnector.js.map +1 -0
  392. package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js +195 -0
  393. package/build/dist/Server/Utils/DataSource/Connectors/MySqlConnector.js.map +1 -0
  394. package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js +149 -0
  395. package/build/dist/Server/Utils/DataSource/Connectors/PostgresConnector.js.map +1 -0
  396. package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js +362 -0
  397. package/build/dist/Server/Utils/DataSource/Connectors/PrometheusConnector.js.map +1 -0
  398. package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js +342 -0
  399. package/build/dist/Server/Utils/DataSource/Connectors/RestApiConnector.js.map +1 -0
  400. package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js +186 -0
  401. package/build/dist/Server/Utils/DataSource/Connectors/SqlServerConnector.js.map +1 -0
  402. package/build/dist/Server/Utils/DataSource/EgressGuard.js +305 -0
  403. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -0
  404. package/build/dist/Server/Utils/DataSource/HttpFetch.js +134 -0
  405. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -0
  406. package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js +331 -0
  407. package/build/dist/Server/Utils/DataSource/SqlQueryGuard.js.map +1 -0
  408. package/build/dist/Server/Utils/DataSource/SqlResultShaper.js +280 -0
  409. package/build/dist/Server/Utils/DataSource/SqlResultShaper.js.map +1 -0
  410. package/build/dist/Server/Utils/DataSource/Types.js +2 -0
  411. package/build/dist/Server/Utils/DataSource/Types.js.map +1 -0
  412. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js +103 -9
  413. package/build/dist/Server/Utils/Database/PostgresErrorTranslator.js.map +1 -1
  414. package/build/dist/Server/Utils/InProcessMemo.js +113 -0
  415. package/build/dist/Server/Utils/InProcessMemo.js.map +1 -0
  416. package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js +406 -0
  417. package/build/dist/Server/Utils/InstanceHealth/TelemetryIngestion.js.map +1 -0
  418. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +34 -0
  419. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  420. package/build/dist/Server/Utils/Monitor/MonitorResource.js +14 -0
  421. package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
  422. package/build/dist/Server/Utils/Response.js +9 -1
  423. package/build/dist/Server/Utils/Response.js.map +1 -1
  424. package/build/dist/Server/Utils/SSRFProtection.js +192 -0
  425. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -0
  426. package/build/dist/Server/Utils/StartServer.js +7 -0
  427. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  428. package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js +24 -0
  429. package/build/dist/Server/Utils/StatusPageSubscriberWebhook.js.map +1 -1
  430. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +25 -11
  431. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
  432. package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js +32 -0
  433. package/build/dist/Server/Utils/Telemetry/IngestionTimestamp.js.map +1 -0
  434. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js +92 -5
  435. package/build/dist/Server/Utils/Telemetry/ResourceHeartbeat.js.map +1 -1
  436. package/build/dist/Server/Utils/Telemetry/Telemetry.js +92 -6
  437. package/build/dist/Server/Utils/Telemetry/Telemetry.js.map +1 -1
  438. package/build/dist/Server/Utils/Telemetry.js +93 -6
  439. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  440. package/build/dist/Server/Utils/VendorAssets.js +121 -0
  441. package/build/dist/Server/Utils/VendorAssets.js.map +1 -0
  442. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +25 -4
  443. package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
  444. package/build/dist/Server/Utils/Workspace/Slack/Slack.js +5 -0
  445. package/build/dist/Server/Utils/Workspace/Slack/Slack.js.map +1 -1
  446. package/build/dist/Types/Analytics/RevenueEvent.js +26 -0
  447. package/build/dist/Types/Analytics/RevenueEvent.js.map +1 -0
  448. package/build/dist/Types/Dashboard/DashboardComponentType.js +27 -0
  449. package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
  450. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js +2 -0
  451. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceChartComponent.js.map +1 -0
  452. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js +2 -0
  453. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceGaugeComponent.js.map +1 -0
  454. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js +2 -0
  455. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceTableComponent.js.map +1 -0
  456. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js +16 -0
  457. package/build/dist/Types/Dashboard/DashboardComponents/DashboardDataSourceValueComponent.js.map +1 -0
  458. package/build/dist/Types/DataSource/DataSourceLimits.js +47 -0
  459. package/build/dist/Types/DataSource/DataSourceLimits.js.map +1 -0
  460. package/build/dist/Types/DataSource/DataSourceQueryConfig.js +10 -0
  461. package/build/dist/Types/DataSource/DataSourceQueryConfig.js.map +1 -0
  462. package/build/dist/Types/DataSource/DataSourceTableResult.js +14 -0
  463. package/build/dist/Types/DataSource/DataSourceTableResult.js.map +1 -0
  464. package/build/dist/Types/DataSource/DataSourceType.js +183 -0
  465. package/build/dist/Types/DataSource/DataSourceType.js.map +1 -0
  466. package/build/dist/Types/Date.js +38 -3
  467. package/build/dist/Types/Date.js.map +1 -1
  468. package/build/dist/Types/Password.js +56 -0
  469. package/build/dist/Types/Password.js.map +1 -0
  470. package/build/dist/Types/Permission.js +42 -0
  471. package/build/dist/Types/Permission.js.map +1 -1
  472. package/build/dist/Types/UserAuthenticationStatus.js +2 -0
  473. package/build/dist/Types/UserAuthenticationStatus.js.map +1 -0
  474. package/build/dist/UI/Components/ErrorBoundary.js +388 -72
  475. package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
  476. package/build/dist/UI/Components/Icon/Icon.js +10 -4
  477. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  478. package/build/dist/UI/Components/Modal/ConfirmModal.js +1 -1
  479. package/build/dist/UI/Components/Modal/ConfirmModal.js.map +1 -1
  480. package/build/dist/UI/Components/Modal/Modal.js +178 -19
  481. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  482. package/build/dist/UI/Components/Modal/ModalFooter.js +3 -1
  483. package/build/dist/UI/Components/Modal/ModalFooter.js.map +1 -1
  484. package/build/dist/UI/Components/MoreMenu/MoreMenu.js +9 -6
  485. package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
  486. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +7 -1
  487. package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
  488. package/build/dist/UI/Components/StatusPage/ResourceGrid.js +119 -0
  489. package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -0
  490. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js +354 -0
  491. package/build/dist/UI/Components/StatusPage/ResourceGroupNavigator.js.map +1 -0
  492. package/build/dist/UI/Components/StatusPage/ResourceList.js +203 -0
  493. package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -0
  494. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js +1 -6
  495. package/build/dist/UI/Utils/AnalyticsModelAPI/AnalyticsModelAPI.js.map +1 -1
  496. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js +3 -8
  497. package/build/dist/UI/Utils/ModelAPI/ModelAPI.js.map +1 -1
  498. package/build/dist/UI/Utils/TestDataSource.js +39 -0
  499. package/build/dist/UI/Utils/TestDataSource.js.map +1 -0
  500. package/build/dist/Utils/Analytics.js +4 -0
  501. package/build/dist/Utils/Analytics.js.map +1 -1
  502. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js +79 -0
  503. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceChartComponent.js.map +1 -0
  504. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js +111 -0
  505. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceGaugeComponent.js.map +1 -0
  506. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js +15 -0
  507. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceShared.js.map +1 -0
  508. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js +62 -0
  509. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceTableComponent.js.map +1 -0
  510. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js +112 -0
  511. package/build/dist/Utils/Dashboard/Components/DashboardDataSourceValueComponent.js.map +1 -0
  512. package/build/dist/Utils/Dashboard/Components/Index.js +16 -0
  513. package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
  514. package/build/dist/Utils/DataSource/DataSourceQueryText.js +64 -0
  515. package/build/dist/Utils/DataSource/DataSourceQueryText.js.map +1 -0
  516. package/build/dist/Utils/DataSource/DataSourceValueReducer.js +124 -0
  517. package/build/dist/Utils/DataSource/DataSourceValueReducer.js.map +1 -0
  518. package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js +281 -0
  519. package/build/dist/Utils/Metrics/MetricResourceAttributeUtil.js.map +1 -0
  520. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +8 -6
  521. package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
  522. package/build/dist/Utils/StatusPage/GroupHierarchyView.js +560 -0
  523. package/build/dist/Utils/StatusPage/GroupHierarchyView.js.map +1 -0
  524. package/build/dist/Utils/StatusPage/ResourceExplorer.js +519 -0
  525. package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -0
  526. package/jest.config.json +1 -0
  527. package/package.json +5 -1
  528. package/tsconfig.json +12 -10
@@ -0,0 +1,935 @@
1
+ import { afterEach, describe, expect, test } from "@jest/globals";
2
+ import { ConnectionOptions } from "mysql2/promise";
3
+ import MySqlConnector, {
4
+ MySqlConnectionFactory,
5
+ MySqlConnectionLike,
6
+ } from "../../../../Server/Utils/DataSource/Connectors/MySqlConnector";
7
+ import DataSourceEgressGuard from "../../../../Server/Utils/DataSource/EgressGuard";
8
+ import {
9
+ DataSourceConnectionSettings,
10
+ DataSourceQueryWindow,
11
+ } from "../../../../Server/Utils/DataSource/Types";
12
+ import AggregatedResult from "../../../../Types/BaseDatabase/AggregatedResult";
13
+ import {
14
+ DATA_SOURCE_CONNECT_TIMEOUT_IN_MS,
15
+ DATA_SOURCE_MAX_TABLE_ROWS,
16
+ DATA_SOURCE_MAX_TIME_SERIES_ROWS,
17
+ DATA_SOURCE_QUERY_TIMEOUT_IN_MS,
18
+ } from "../../../../Types/DataSource/DataSourceLimits";
19
+ import DataSourceTableResult from "../../../../Types/DataSource/DataSourceTableResult";
20
+ import DataSourceType from "../../../../Types/DataSource/DataSourceType";
21
+ import BadDataException from "../../../../Types/Exception/BadDataException";
22
+
23
+ /*
24
+ * Fake mysql2 connection harness. Records every SQL string in order,
25
+ * counts end()/destroy() calls, and lets individual statements be scripted
26
+ * to fail so teardown paths can be exercised.
27
+ */
28
+ interface FakeConnectionScript {
29
+ /* Result tuple element [0] returned for every query (default []). */
30
+ result?: unknown;
31
+ /* Return an Error for a given SQL string to make that statement reject. */
32
+ failOn?: (sql: string) => Error | null;
33
+ endError?: Error;
34
+ destroyError?: Error;
35
+ }
36
+
37
+ interface FakeConnectionHarness {
38
+ connection: MySqlConnectionLike;
39
+ executedQueries: Array<string>;
40
+ state: { endCalls: number; destroyCalls: number };
41
+ }
42
+
43
+ type CreateFakeConnectionFunction = (
44
+ script?: FakeConnectionScript,
45
+ ) => FakeConnectionHarness;
46
+
47
+ const createFakeConnection: CreateFakeConnectionFunction = (
48
+ script?: FakeConnectionScript,
49
+ ): FakeConnectionHarness => {
50
+ const executedQueries: Array<string> = [];
51
+ const state: { endCalls: number; destroyCalls: number } = {
52
+ endCalls: 0,
53
+ destroyCalls: 0,
54
+ };
55
+
56
+ const connection: MySqlConnectionLike = {
57
+ query: (sql: string): Promise<[unknown, unknown]> => {
58
+ executedQueries.push(sql);
59
+ const error: Error | null = script?.failOn ? script.failOn(sql) : null;
60
+ if (error) {
61
+ return Promise.reject(error);
62
+ }
63
+ return Promise.resolve([script?.result ?? [], []] as [unknown, unknown]);
64
+ },
65
+ end: (): Promise<void> => {
66
+ state.endCalls += 1;
67
+ if (script?.endError) {
68
+ return Promise.reject(script.endError);
69
+ }
70
+ return Promise.resolve();
71
+ },
72
+ destroy: (): void => {
73
+ state.destroyCalls += 1;
74
+ if (script?.destroyError) {
75
+ throw script.destroyError;
76
+ }
77
+ },
78
+ };
79
+
80
+ return {
81
+ connection: connection,
82
+ executedQueries: executedQueries,
83
+ state: state,
84
+ };
85
+ };
86
+
87
+ interface FakeFactoryHarness {
88
+ factory: MySqlConnectionFactory;
89
+ calls: Array<ConnectionOptions>;
90
+ }
91
+
92
+ type CreateFactoryFunction = (
93
+ harness: FakeConnectionHarness,
94
+ events?: Array<string>,
95
+ ) => FakeFactoryHarness;
96
+
97
+ const createFactory: CreateFactoryFunction = (
98
+ harness: FakeConnectionHarness,
99
+ events?: Array<string>,
100
+ ): FakeFactoryHarness => {
101
+ const calls: Array<ConnectionOptions> = [];
102
+ const factory: MySqlConnectionFactory = (
103
+ options: ConnectionOptions,
104
+ ): Promise<MySqlConnectionLike> => {
105
+ calls.push(options);
106
+ if (events) {
107
+ events.push("connect");
108
+ }
109
+ return Promise.resolve(harness.connection);
110
+ };
111
+ return { factory: factory, calls: calls };
112
+ };
113
+
114
+ const PASSWORD: string = "sup3r-s3cret-pw";
115
+
116
+ type BuildSettingsFunction = (
117
+ overrides?: Partial<DataSourceConnectionSettings>,
118
+ ) => DataSourceConnectionSettings;
119
+
120
+ const buildSettings: BuildSettingsFunction = (
121
+ overrides?: Partial<DataSourceConnectionSettings>,
122
+ ): DataSourceConnectionSettings => {
123
+ return {
124
+ dataSourceType: DataSourceType.MySQL,
125
+ databaseHost: "db.example.com",
126
+ databasePort: 3307,
127
+ databaseName: "metrics",
128
+ username: "reader",
129
+ password: PASSWORD,
130
+ ...overrides,
131
+ };
132
+ };
133
+
134
+ const WINDOW: DataSourceQueryWindow = {
135
+ startDate: new Date("2025-01-01T00:00:00.000Z"),
136
+ endDate: new Date("2025-01-02T00:00:00.000Z"),
137
+ };
138
+
139
+ type MockEgressAllowedFunction = (events?: Array<string>) => jest.SpyInstance;
140
+
141
+ const mockEgressAllowed: MockEgressAllowedFunction = (
142
+ events?: Array<string>,
143
+ ): jest.SpyInstance => {
144
+ const spy: jest.SpyInstance = jest.spyOn(
145
+ DataSourceEgressGuard,
146
+ "assertHostnameAllowed",
147
+ );
148
+ spy.mockImplementation(() => {
149
+ if (events) {
150
+ events.push("egress");
151
+ }
152
+ return Promise.resolve([{ address: "203.0.113.10", family: 4 }]);
153
+ });
154
+ return spy;
155
+ };
156
+
157
+ afterEach(() => {
158
+ jest.restoreAllMocks();
159
+ });
160
+
161
+ describe("MySqlConnector", () => {
162
+ describe("testConnection", () => {
163
+ test("connects, runs SELECT 1 and closes the connection", async () => {
164
+ const egressSpy: jest.SpyInstance = mockEgressAllowed();
165
+ const harness: FakeConnectionHarness = createFakeConnection();
166
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
167
+ const connector: MySqlConnector = new MySqlConnector(
168
+ factoryHarness.factory,
169
+ );
170
+
171
+ await connector.testConnection(buildSettings());
172
+
173
+ expect(egressSpy).toHaveBeenCalledWith("db.example.com");
174
+ expect(harness.executedQueries).toEqual(["SELECT 1", "ROLLBACK"]);
175
+ expect(harness.state.endCalls).toBe(1);
176
+ expect(harness.state.destroyCalls).toBe(0);
177
+ });
178
+
179
+ test("validates egress before opening a connection", async () => {
180
+ const events: Array<string> = [];
181
+ mockEgressAllowed(events);
182
+ const harness: FakeConnectionHarness = createFakeConnection();
183
+ const factoryHarness: FakeFactoryHarness = createFactory(harness, events);
184
+ const connector: MySqlConnector = new MySqlConnector(
185
+ factoryHarness.factory,
186
+ );
187
+
188
+ await connector.testConnection(buildSettings());
189
+
190
+ expect(events).toEqual(["egress", "connect"]);
191
+ });
192
+
193
+ test("throws without connecting when databaseHost is missing", async () => {
194
+ const egressSpy: jest.SpyInstance = mockEgressAllowed();
195
+ const harness: FakeConnectionHarness = createFakeConnection();
196
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
197
+ const connector: MySqlConnector = new MySqlConnector(
198
+ factoryHarness.factory,
199
+ );
200
+
201
+ await expect(
202
+ connector.testConnection(buildSettings({ databaseHost: undefined })),
203
+ ).rejects.toThrow(BadDataException);
204
+
205
+ expect(egressSpy).not.toHaveBeenCalled();
206
+ expect(factoryHarness.calls).toHaveLength(0);
207
+ });
208
+
209
+ test("propagates an egress guard rejection without connecting", async () => {
210
+ const egressSpy: jest.SpyInstance = jest.spyOn(
211
+ DataSourceEgressGuard,
212
+ "assertHostnameAllowed",
213
+ );
214
+ egressSpy.mockRejectedValue(
215
+ new BadDataException(
216
+ "Data source host 169.254.169.254 is not allowed: link-local address (cloud metadata range).",
217
+ ),
218
+ );
219
+ const harness: FakeConnectionHarness = createFakeConnection();
220
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
221
+ const connector: MySqlConnector = new MySqlConnector(
222
+ factoryHarness.factory,
223
+ );
224
+
225
+ await expect(connector.testConnection(buildSettings())).rejects.toThrow(
226
+ "not allowed",
227
+ );
228
+
229
+ expect(factoryHarness.calls).toHaveLength(0);
230
+ expect(harness.executedQueries).toHaveLength(0);
231
+ });
232
+
233
+ test("sanitizes connection failures so secrets never leak", async () => {
234
+ mockEgressAllowed();
235
+ const factory: MySqlConnectionFactory =
236
+ (): Promise<MySqlConnectionLike> => {
237
+ return Promise.reject(
238
+ new Error(
239
+ `Access denied for user 'reader'@'10.0.0.5' (using password: ${PASSWORD})`,
240
+ ),
241
+ );
242
+ };
243
+ const connector: MySqlConnector = new MySqlConnector(factory);
244
+
245
+ let thrown: Error | null = null;
246
+ try {
247
+ await connector.testConnection(buildSettings());
248
+ } catch (error) {
249
+ thrown = error as Error;
250
+ }
251
+
252
+ expect(thrown).toBeInstanceOf(BadDataException);
253
+ expect(thrown!.message).not.toContain(PASSWORD);
254
+ expect(thrown!.message).toContain("***");
255
+ });
256
+
257
+ test("sanitizes SELECT 1 failures and still closes the connection", async () => {
258
+ mockEgressAllowed();
259
+ const harness: FakeConnectionHarness = createFakeConnection({
260
+ failOn: (sql: string): Error | null => {
261
+ if (sql === "SELECT 1") {
262
+ return new Error(`server exploded for reader:${PASSWORD}`);
263
+ }
264
+ return null;
265
+ },
266
+ });
267
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
268
+ const connector: MySqlConnector = new MySqlConnector(
269
+ factoryHarness.factory,
270
+ );
271
+
272
+ let thrown: Error | null = null;
273
+ try {
274
+ await connector.testConnection(buildSettings());
275
+ } catch (error) {
276
+ thrown = error as Error;
277
+ }
278
+
279
+ expect(thrown).toBeInstanceOf(BadDataException);
280
+ expect(thrown!.message).not.toContain(PASSWORD);
281
+ expect(harness.state.endCalls).toBe(1);
282
+ });
283
+ });
284
+
285
+ describe("connection options", () => {
286
+ test("passes host, port, credentials, timeout and multipleStatements:false to the driver", async () => {
287
+ mockEgressAllowed();
288
+ const harness: FakeConnectionHarness = createFakeConnection();
289
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
290
+ const connector: MySqlConnector = new MySqlConnector(
291
+ factoryHarness.factory,
292
+ );
293
+
294
+ await connector.testConnection(buildSettings());
295
+
296
+ expect(factoryHarness.calls).toHaveLength(1);
297
+ const options: ConnectionOptions = factoryHarness.calls[0]!;
298
+ expect(options.host).toBe("db.example.com");
299
+ expect(options.port).toBe(3307);
300
+ expect(options.database).toBe("metrics");
301
+ expect(options.user).toBe("reader");
302
+ expect(options.password).toBe(PASSWORD);
303
+ expect(options.connectTimeout).toBe(DATA_SOURCE_CONNECT_TIMEOUT_IN_MS);
304
+ expect(options.multipleStatements).toBe(false);
305
+ expect(options.ssl).toBeUndefined();
306
+ });
307
+
308
+ test("defaults the port to 3306 when none is configured", async () => {
309
+ mockEgressAllowed();
310
+ const harness: FakeConnectionHarness = createFakeConnection();
311
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
312
+ const connector: MySqlConnector = new MySqlConnector(
313
+ factoryHarness.factory,
314
+ );
315
+
316
+ await connector.testConnection(
317
+ buildSettings({ databasePort: undefined }),
318
+ );
319
+
320
+ expect(factoryHarness.calls[0]!.port).toBe(3306);
321
+ });
322
+
323
+ test("enables TLS with certificate verification on by default", async () => {
324
+ mockEgressAllowed();
325
+ const harness: FakeConnectionHarness = createFakeConnection();
326
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
327
+ const connector: MySqlConnector = new MySqlConnector(
328
+ factoryHarness.factory,
329
+ );
330
+
331
+ await connector.testConnection(
332
+ buildSettings({ additionalOptions: { sslEnabled: true } }),
333
+ );
334
+
335
+ expect(factoryHarness.calls[0]!.ssl).toEqual({
336
+ rejectUnauthorized: true,
337
+ });
338
+ });
339
+
340
+ test("disables certificate verification only on explicit opt-out", async () => {
341
+ mockEgressAllowed();
342
+ const harness: FakeConnectionHarness = createFakeConnection();
343
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
344
+ const connector: MySqlConnector = new MySqlConnector(
345
+ factoryHarness.factory,
346
+ );
347
+
348
+ await connector.testConnection(
349
+ buildSettings({
350
+ additionalOptions: {
351
+ sslEnabled: true,
352
+ sslRejectUnauthorized: false,
353
+ },
354
+ }),
355
+ );
356
+
357
+ expect(factoryHarness.calls[0]!.ssl).toEqual({
358
+ rejectUnauthorized: false,
359
+ });
360
+ });
361
+ });
362
+
363
+ describe("queryTimeSeries", () => {
364
+ test("runs the session setup, read-only transaction and query in order", async () => {
365
+ mockEgressAllowed();
366
+ const harness: FakeConnectionHarness = createFakeConnection({
367
+ result: [{ time: new Date("2025-01-01T10:00:00.000Z"), value: 5 }],
368
+ });
369
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
370
+ const connector: MySqlConnector = new MySqlConnector(
371
+ factoryHarness.factory,
372
+ );
373
+
374
+ await connector.queryTimeSeries(
375
+ buildSettings(),
376
+ "SELECT created_at AS time, COUNT(*) AS value FROM orders GROUP BY 1",
377
+ WINDOW,
378
+ );
379
+
380
+ expect(harness.executedQueries).toEqual([
381
+ `SET SESSION MAX_EXECUTION_TIME = ${DATA_SOURCE_QUERY_TIMEOUT_IN_MS}`,
382
+ `SET SESSION SQL_SELECT_LIMIT = ${DATA_SOURCE_MAX_TIME_SERIES_ROWS + 1}`,
383
+ "START TRANSACTION READ ONLY",
384
+ "SELECT created_at AS time, COUNT(*) AS value FROM orders GROUP BY 1",
385
+ "ROLLBACK",
386
+ ]);
387
+ expect(harness.state.endCalls).toBe(1);
388
+ expect(harness.state.destroyCalls).toBe(0);
389
+ });
390
+
391
+ test("validates egress before connecting for queries", async () => {
392
+ const events: Array<string> = [];
393
+ mockEgressAllowed(events);
394
+ const harness: FakeConnectionHarness = createFakeConnection({
395
+ result: [{ time: new Date("2025-01-01T10:00:00.000Z"), value: 1 }],
396
+ });
397
+ const factoryHarness: FakeFactoryHarness = createFactory(harness, events);
398
+ const connector: MySqlConnector = new MySqlConnector(
399
+ factoryHarness.factory,
400
+ );
401
+
402
+ await connector.queryTimeSeries(
403
+ buildSettings(),
404
+ "SELECT 1 AS time, 2 AS value",
405
+ WINDOW,
406
+ );
407
+
408
+ expect(events).toEqual(["egress", "connect"]);
409
+ });
410
+
411
+ test("applies time macros and strips a trailing semicolon before executing", async () => {
412
+ mockEgressAllowed();
413
+ const harness: FakeConnectionHarness = createFakeConnection({
414
+ result: [],
415
+ });
416
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
417
+ const connector: MySqlConnector = new MySqlConnector(
418
+ factoryHarness.factory,
419
+ );
420
+
421
+ await connector.queryTimeSeries(
422
+ buildSettings(),
423
+ "SELECT t AS time, v AS value FROM m WHERE t BETWEEN $__startTime AND $__endTime AND r = $__rangeSeconds ;",
424
+ WINDOW,
425
+ );
426
+
427
+ const executedUserQuery: string = harness.executedQueries[3]!;
428
+ expect(executedUserQuery).toBe(
429
+ "SELECT t AS time, v AS value FROM m WHERE t BETWEEN '2025-01-01 00:00:00' AND '2025-01-02 00:00:00' AND r = 86400 ",
430
+ );
431
+ });
432
+
433
+ test("rejects write queries before any connection is opened", async () => {
434
+ const egressSpy: jest.SpyInstance = mockEgressAllowed();
435
+ const harness: FakeConnectionHarness = createFakeConnection();
436
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
437
+ const connector: MySqlConnector = new MySqlConnector(
438
+ factoryHarness.factory,
439
+ );
440
+
441
+ await expect(
442
+ connector.queryTimeSeries(
443
+ buildSettings(),
444
+ "DELETE FROM orders",
445
+ WINDOW,
446
+ ),
447
+ ).rejects.toThrow(BadDataException);
448
+
449
+ expect(egressSpy).not.toHaveBeenCalled();
450
+ expect(factoryHarness.calls).toHaveLength(0);
451
+ expect(harness.executedQueries).toHaveLength(0);
452
+ });
453
+
454
+ test("rejects stacked statements before any connection is opened", async () => {
455
+ mockEgressAllowed();
456
+ const harness: FakeConnectionHarness = createFakeConnection();
457
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
458
+ const connector: MySqlConnector = new MySqlConnector(
459
+ factoryHarness.factory,
460
+ );
461
+
462
+ await expect(
463
+ connector.queryTimeSeries(
464
+ buildSettings(),
465
+ "SELECT 1; SELECT 2",
466
+ WINDOW,
467
+ ),
468
+ ).rejects.toThrow("single SQL statement");
469
+
470
+ expect(factoryHarness.calls).toHaveLength(0);
471
+ });
472
+
473
+ test("shapes rows into AggregatedResult with labels under attributes", async () => {
474
+ mockEgressAllowed();
475
+ const harness: FakeConnectionHarness = createFakeConnection({
476
+ result: [
477
+ {
478
+ time: new Date("2025-01-01T10:00:00.000Z"),
479
+ value: 5,
480
+ host: "web-1",
481
+ },
482
+ {
483
+ time: new Date("2025-01-01T10:01:00.000Z"),
484
+ value: 7,
485
+ host: "web-2",
486
+ },
487
+ ],
488
+ });
489
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
490
+ const connector: MySqlConnector = new MySqlConnector(
491
+ factoryHarness.factory,
492
+ );
493
+
494
+ const result: AggregatedResult = await connector.queryTimeSeries(
495
+ buildSettings(),
496
+ "SELECT t AS time, v AS value, host FROM m",
497
+ WINDOW,
498
+ );
499
+
500
+ expect(result.data).toHaveLength(2);
501
+ expect(result.truncated).toBe(false);
502
+ expect(result.data[0]!.timestamp).toEqual(
503
+ new Date("2025-01-01T10:00:00.000Z"),
504
+ );
505
+ expect(result.data[0]!.value).toBe(5);
506
+ expect(result.data[0]!["attributes"]).toEqual({ host: "web-1" });
507
+ expect(result.data[1]!["attributes"]).toEqual({ host: "web-2" });
508
+ });
509
+
510
+ test("omits attributes when the query returns only time and value", async () => {
511
+ mockEgressAllowed();
512
+ const harness: FakeConnectionHarness = createFakeConnection({
513
+ result: [{ time: new Date("2025-01-01T10:00:00.000Z"), value: 5 }],
514
+ });
515
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
516
+ const connector: MySqlConnector = new MySqlConnector(
517
+ factoryHarness.factory,
518
+ );
519
+
520
+ const result: AggregatedResult = await connector.queryTimeSeries(
521
+ buildSettings(),
522
+ "SELECT t AS time, v AS value FROM m",
523
+ WINDOW,
524
+ );
525
+
526
+ expect(result.data).toHaveLength(1);
527
+ expect(result.data[0]!["attributes"]).toBeUndefined();
528
+ });
529
+
530
+ test("returns an empty result for zero rows", async () => {
531
+ mockEgressAllowed();
532
+ const harness: FakeConnectionHarness = createFakeConnection({
533
+ result: [],
534
+ });
535
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
536
+ const connector: MySqlConnector = new MySqlConnector(
537
+ factoryHarness.factory,
538
+ );
539
+
540
+ const result: AggregatedResult = await connector.queryTimeSeries(
541
+ buildSettings(),
542
+ "SELECT t AS time, v AS value FROM m",
543
+ WINDOW,
544
+ );
545
+
546
+ expect(result).toEqual({ data: [], truncated: false });
547
+ });
548
+
549
+ test("treats a non-array driver result as zero rows", async () => {
550
+ mockEgressAllowed();
551
+ const harness: FakeConnectionHarness = createFakeConnection({
552
+ result: { affectedRows: 0 },
553
+ });
554
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
555
+ const connector: MySqlConnector = new MySqlConnector(
556
+ factoryHarness.factory,
557
+ );
558
+
559
+ const result: AggregatedResult = await connector.queryTimeSeries(
560
+ buildSettings(),
561
+ "SELECT t AS time, v AS value FROM m",
562
+ WINDOW,
563
+ );
564
+
565
+ expect(result).toEqual({ data: [], truncated: false });
566
+ });
567
+
568
+ test("throws an actionable error when no time column is present", async () => {
569
+ mockEgressAllowed();
570
+ const harness: FakeConnectionHarness = createFakeConnection({
571
+ result: [{ foo: 1, value: 2 }],
572
+ });
573
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
574
+ const connector: MySqlConnector = new MySqlConnector(
575
+ factoryHarness.factory,
576
+ );
577
+
578
+ await expect(
579
+ connector.queryTimeSeries(
580
+ buildSettings(),
581
+ "SELECT foo, v AS value FROM m",
582
+ WINDOW,
583
+ ),
584
+ ).rejects.toThrow("time column");
585
+ });
586
+
587
+ test("skips rows with unparseable timestamps instead of failing", async () => {
588
+ mockEgressAllowed();
589
+ const harness: FakeConnectionHarness = createFakeConnection({
590
+ result: [
591
+ { time: "not-a-date", value: 1 },
592
+ { time: new Date("2025-01-01T10:00:00.000Z"), value: 2 },
593
+ ],
594
+ });
595
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
596
+ const connector: MySqlConnector = new MySqlConnector(
597
+ factoryHarness.factory,
598
+ );
599
+
600
+ const result: AggregatedResult = await connector.queryTimeSeries(
601
+ buildSettings(),
602
+ "SELECT t AS time, v AS value FROM m",
603
+ WINDOW,
604
+ );
605
+
606
+ expect(result.data).toHaveLength(1);
607
+ expect(result.data[0]!.value).toBe(2);
608
+ });
609
+
610
+ test("clamps at the time-series cap and flags truncation", async () => {
611
+ mockEgressAllowed();
612
+ const rows: Array<Record<string, unknown>> = [];
613
+ for (
614
+ let index: number = 0;
615
+ index < DATA_SOURCE_MAX_TIME_SERIES_ROWS + 1;
616
+ index++
617
+ ) {
618
+ rows.push({
619
+ time: new Date(WINDOW.startDate.getTime() + index * 1000),
620
+ value: index,
621
+ });
622
+ }
623
+ const harness: FakeConnectionHarness = createFakeConnection({
624
+ result: rows,
625
+ });
626
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
627
+ const connector: MySqlConnector = new MySqlConnector(
628
+ factoryHarness.factory,
629
+ );
630
+
631
+ const result: AggregatedResult = await connector.queryTimeSeries(
632
+ buildSettings(),
633
+ "SELECT t AS time, v AS value FROM m",
634
+ WINDOW,
635
+ );
636
+
637
+ expect(result.data).toHaveLength(DATA_SOURCE_MAX_TIME_SERIES_ROWS);
638
+ expect(result.truncated).toBe(true);
639
+ });
640
+
641
+ test("continues when SET SESSION MAX_EXECUTION_TIME is unsupported (MariaDB)", async () => {
642
+ mockEgressAllowed();
643
+ const harness: FakeConnectionHarness = createFakeConnection({
644
+ result: [{ time: new Date("2025-01-01T10:00:00.000Z"), value: 1 }],
645
+ failOn: (sql: string): Error | null => {
646
+ if (sql.startsWith("SET SESSION MAX_EXECUTION_TIME")) {
647
+ return new Error("Unknown system variable 'MAX_EXECUTION_TIME'");
648
+ }
649
+ return null;
650
+ },
651
+ });
652
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
653
+ const connector: MySqlConnector = new MySqlConnector(
654
+ factoryHarness.factory,
655
+ );
656
+
657
+ const result: AggregatedResult = await connector.queryTimeSeries(
658
+ buildSettings(),
659
+ "SELECT t AS time, v AS value FROM m",
660
+ WINDOW,
661
+ );
662
+
663
+ expect(result.data).toHaveLength(1);
664
+ expect(harness.executedQueries).toContain(
665
+ `SET SESSION SQL_SELECT_LIMIT = ${DATA_SOURCE_MAX_TIME_SERIES_ROWS + 1}`,
666
+ );
667
+ expect(harness.executedQueries).toContain("START TRANSACTION READ ONLY");
668
+ });
669
+
670
+ test("wraps a failing START TRANSACTION READ ONLY and still tears down", async () => {
671
+ mockEgressAllowed();
672
+ const harness: FakeConnectionHarness = createFakeConnection({
673
+ failOn: (sql: string): Error | null => {
674
+ if (sql === "START TRANSACTION READ ONLY") {
675
+ return new Error(`txn refused for ${PASSWORD}`);
676
+ }
677
+ return null;
678
+ },
679
+ });
680
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
681
+ const connector: MySqlConnector = new MySqlConnector(
682
+ factoryHarness.factory,
683
+ );
684
+
685
+ let thrown: Error | null = null;
686
+ try {
687
+ await connector.queryTimeSeries(
688
+ buildSettings(),
689
+ "SELECT t AS time, v AS value FROM m",
690
+ WINDOW,
691
+ );
692
+ } catch (error) {
693
+ thrown = error as Error;
694
+ }
695
+
696
+ expect(thrown).toBeInstanceOf(BadDataException);
697
+ expect(thrown!.message).not.toContain(PASSWORD);
698
+ expect(harness.executedQueries).toContain("ROLLBACK");
699
+ expect(harness.state.endCalls).toBe(1);
700
+ });
701
+
702
+ test("sanitizes user-query driver errors and rolls back", async () => {
703
+ mockEgressAllowed();
704
+ const harness: FakeConnectionHarness = createFakeConnection({
705
+ failOn: (sql: string): Error | null => {
706
+ if (sql.startsWith("SELECT t AS time")) {
707
+ return new Error(
708
+ `You have an error near 'reader'@'db.example.com', password ${PASSWORD}`,
709
+ );
710
+ }
711
+ return null;
712
+ },
713
+ });
714
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
715
+ const connector: MySqlConnector = new MySqlConnector(
716
+ factoryHarness.factory,
717
+ );
718
+
719
+ let thrown: Error | null = null;
720
+ try {
721
+ await connector.queryTimeSeries(
722
+ buildSettings(),
723
+ "SELECT t AS time, v AS value FROM m",
724
+ WINDOW,
725
+ );
726
+ } catch (error) {
727
+ thrown = error as Error;
728
+ }
729
+
730
+ expect(thrown).toBeInstanceOf(BadDataException);
731
+ expect(thrown!.message).not.toContain(PASSWORD);
732
+ expect(thrown!.message).not.toContain("db.example.com");
733
+ expect(thrown!.message).not.toContain("reader");
734
+ expect(harness.executedQueries).toContain("ROLLBACK");
735
+ expect(harness.state.endCalls).toBe(1);
736
+ });
737
+
738
+ test("falls back to destroy() when end() fails", async () => {
739
+ mockEgressAllowed();
740
+ const harness: FakeConnectionHarness = createFakeConnection({
741
+ result: [{ time: new Date("2025-01-01T10:00:00.000Z"), value: 1 }],
742
+ endError: new Error("socket already half-closed"),
743
+ });
744
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
745
+ const connector: MySqlConnector = new MySqlConnector(
746
+ factoryHarness.factory,
747
+ );
748
+
749
+ const result: AggregatedResult = await connector.queryTimeSeries(
750
+ buildSettings(),
751
+ "SELECT t AS time, v AS value FROM m",
752
+ WINDOW,
753
+ );
754
+
755
+ expect(result.data).toHaveLength(1);
756
+ expect(harness.state.endCalls).toBe(1);
757
+ expect(harness.state.destroyCalls).toBe(1);
758
+ });
759
+
760
+ test("swallows a destroy() failure after a failed end()", async () => {
761
+ mockEgressAllowed();
762
+ const harness: FakeConnectionHarness = createFakeConnection({
763
+ result: [{ time: new Date("2025-01-01T10:00:00.000Z"), value: 1 }],
764
+ endError: new Error("end failed"),
765
+ destroyError: new Error("destroy failed"),
766
+ });
767
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
768
+ const connector: MySqlConnector = new MySqlConnector(
769
+ factoryHarness.factory,
770
+ );
771
+
772
+ const result: AggregatedResult = await connector.queryTimeSeries(
773
+ buildSettings(),
774
+ "SELECT t AS time, v AS value FROM m",
775
+ WINDOW,
776
+ );
777
+
778
+ expect(result.data).toHaveLength(1);
779
+ expect(harness.state.destroyCalls).toBe(1);
780
+ });
781
+
782
+ test("swallows a ROLLBACK failure and still closes the connection", async () => {
783
+ mockEgressAllowed();
784
+ const harness: FakeConnectionHarness = createFakeConnection({
785
+ result: [{ time: new Date("2025-01-01T10:00:00.000Z"), value: 1 }],
786
+ failOn: (sql: string): Error | null => {
787
+ if (sql === "ROLLBACK") {
788
+ return new Error("rollback failed");
789
+ }
790
+ return null;
791
+ },
792
+ });
793
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
794
+ const connector: MySqlConnector = new MySqlConnector(
795
+ factoryHarness.factory,
796
+ );
797
+
798
+ const result: AggregatedResult = await connector.queryTimeSeries(
799
+ buildSettings(),
800
+ "SELECT t AS time, v AS value FROM m",
801
+ WINDOW,
802
+ );
803
+
804
+ expect(result.data).toHaveLength(1);
805
+ expect(harness.state.endCalls).toBe(1);
806
+ });
807
+ });
808
+
809
+ describe("queryTable", () => {
810
+ test("uses the table row cap (+1) in SQL_SELECT_LIMIT", async () => {
811
+ mockEgressAllowed();
812
+ const harness: FakeConnectionHarness = createFakeConnection({
813
+ result: [{ id: 1, name: "a" }],
814
+ });
815
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
816
+ const connector: MySqlConnector = new MySqlConnector(
817
+ factoryHarness.factory,
818
+ );
819
+
820
+ await connector.queryTable(
821
+ buildSettings(),
822
+ "SELECT id, name FROM users",
823
+ WINDOW,
824
+ );
825
+
826
+ expect(harness.executedQueries).toContain(
827
+ `SET SESSION SQL_SELECT_LIMIT = ${DATA_SOURCE_MAX_TABLE_ROWS + 1}`,
828
+ );
829
+ });
830
+
831
+ test("shapes rows into columns and JSON-safe cells", async () => {
832
+ mockEgressAllowed();
833
+ const harness: FakeConnectionHarness = createFakeConnection({
834
+ result: [
835
+ {
836
+ id: 1,
837
+ name: "alpha",
838
+ created_at: new Date("2025-01-01T10:00:00.000Z"),
839
+ active: true,
840
+ blob: Buffer.from([1, 2, 3]),
841
+ },
842
+ ],
843
+ });
844
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
845
+ const connector: MySqlConnector = new MySqlConnector(
846
+ factoryHarness.factory,
847
+ );
848
+
849
+ const result: DataSourceTableResult = await connector.queryTable(
850
+ buildSettings(),
851
+ "SELECT id, name, created_at, active, blob FROM users",
852
+ WINDOW,
853
+ );
854
+
855
+ expect(
856
+ result.columns.map((column: { key: string }) => {
857
+ return column.key;
858
+ }),
859
+ ).toEqual(["id", "name", "created_at", "active", "blob"]);
860
+ expect(result.rows).toEqual([
861
+ {
862
+ id: 1,
863
+ name: "alpha",
864
+ created_at: "2025-01-01T10:00:00.000Z",
865
+ active: true,
866
+ blob: "[binary]",
867
+ },
868
+ ]);
869
+ expect(result.truncated).toBe(false);
870
+ });
871
+
872
+ test("returns an empty table for zero rows", async () => {
873
+ mockEgressAllowed();
874
+ const harness: FakeConnectionHarness = createFakeConnection({
875
+ result: [],
876
+ });
877
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
878
+ const connector: MySqlConnector = new MySqlConnector(
879
+ factoryHarness.factory,
880
+ );
881
+
882
+ const result: DataSourceTableResult = await connector.queryTable(
883
+ buildSettings(),
884
+ "SELECT id FROM users",
885
+ WINDOW,
886
+ );
887
+
888
+ expect(result).toEqual({ columns: [], rows: [], truncated: false });
889
+ });
890
+
891
+ test("clamps at the table cap and flags truncation", async () => {
892
+ mockEgressAllowed();
893
+ const rows: Array<Record<string, unknown>> = [];
894
+ for (
895
+ let index: number = 0;
896
+ index < DATA_SOURCE_MAX_TABLE_ROWS + 1;
897
+ index++
898
+ ) {
899
+ rows.push({ id: index });
900
+ }
901
+ const harness: FakeConnectionHarness = createFakeConnection({
902
+ result: rows,
903
+ });
904
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
905
+ const connector: MySqlConnector = new MySqlConnector(
906
+ factoryHarness.factory,
907
+ );
908
+
909
+ const result: DataSourceTableResult = await connector.queryTable(
910
+ buildSettings(),
911
+ "SELECT id FROM users",
912
+ WINDOW,
913
+ );
914
+
915
+ expect(result.rows).toHaveLength(DATA_SOURCE_MAX_TABLE_ROWS);
916
+ expect(result.truncated).toBe(true);
917
+ });
918
+
919
+ test("rejects write queries without connecting", async () => {
920
+ mockEgressAllowed();
921
+ const harness: FakeConnectionHarness = createFakeConnection();
922
+ const factoryHarness: FakeFactoryHarness = createFactory(harness);
923
+ const connector: MySqlConnector = new MySqlConnector(
924
+ factoryHarness.factory,
925
+ );
926
+
927
+ await expect(
928
+ connector.queryTable(buildSettings(), "DROP TABLE users", WINDOW),
929
+ ).rejects.toThrow(BadDataException);
930
+
931
+ expect(factoryHarness.calls).toHaveLength(0);
932
+ expect(harness.executedQueries).toHaveLength(0);
933
+ });
934
+ });
935
+ });