@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,934 @@
1
+ import { afterEach, describe, expect, test } from "@jest/globals";
2
+ import * as mssql from "mssql";
3
+ import SqlServerConnector, {
4
+ SqlServerPoolFactory,
5
+ SqlServerPoolLike,
6
+ SqlServerRequestLike,
7
+ SqlServerTransactionLike,
8
+ } from "../../../../Server/Utils/DataSource/Connectors/SqlServerConnector";
9
+ import DataSourceEgressGuard from "../../../../Server/Utils/DataSource/EgressGuard";
10
+ import {
11
+ DataSourceConnectionSettings,
12
+ DataSourceQueryWindow,
13
+ } from "../../../../Server/Utils/DataSource/Types";
14
+ import AggregatedResult from "../../../../Types/BaseDatabase/AggregatedResult";
15
+ import {
16
+ DATA_SOURCE_CONNECT_TIMEOUT_IN_MS,
17
+ DATA_SOURCE_MAX_TABLE_ROWS,
18
+ DATA_SOURCE_MAX_TIME_SERIES_ROWS,
19
+ DATA_SOURCE_QUERY_TIMEOUT_IN_MS,
20
+ } from "../../../../Types/DataSource/DataSourceLimits";
21
+ import DataSourceTableResult, {
22
+ DataSourceTableColumnType,
23
+ } from "../../../../Types/DataSource/DataSourceTableResult";
24
+ import DataSourceType from "../../../../Types/DataSource/DataSourceType";
25
+ import BadDataException from "../../../../Types/Exception/BadDataException";
26
+
27
+ /*
28
+ * Fake mssql pool harness. Records every lifecycle step and SQL string in
29
+ * order ("connect", "begin", "batch:<sql>", "query:<sql>", "rollback",
30
+ * "close") plus every pool config the factory received, and lets individual
31
+ * steps be scripted to fail so teardown paths can be exercised. No real
32
+ * network or database is ever touched.
33
+ */
34
+ interface HarnessScript {
35
+ /* Rows returned by the recordset of every query (default []). */
36
+ rows?: Array<Record<string, unknown>>;
37
+ /* Return {} (no recordset key at all) from query. */
38
+ omitRecordset?: boolean;
39
+ connectError?: Error;
40
+ beginError?: Error;
41
+ batchError?: Error;
42
+ queryError?: Error;
43
+ rollbackError?: Error;
44
+ closeError?: Error;
45
+ }
46
+
47
+ interface Harness {
48
+ factory: SqlServerPoolFactory;
49
+ calls: Array<string>;
50
+ configs: Array<mssql.config>;
51
+ }
52
+
53
+ type CreateHarnessFunction = (script?: HarnessScript) => Harness;
54
+
55
+ const createHarness: CreateHarnessFunction = (
56
+ script?: HarnessScript,
57
+ ): Harness => {
58
+ const calls: Array<string> = [];
59
+ const configs: Array<mssql.config> = [];
60
+
61
+ const factory: SqlServerPoolFactory = (
62
+ config: mssql.config,
63
+ ): SqlServerPoolLike => {
64
+ configs.push(config);
65
+
66
+ const transaction: SqlServerTransactionLike = {
67
+ begin: async (): Promise<void> => {
68
+ calls.push("begin");
69
+ if (script?.beginError) {
70
+ throw script.beginError;
71
+ }
72
+ },
73
+ rollback: async (): Promise<void> => {
74
+ calls.push("rollback");
75
+ if (script?.rollbackError) {
76
+ throw script.rollbackError;
77
+ }
78
+ },
79
+ request: (): SqlServerRequestLike => {
80
+ return {
81
+ batch: async (sql: string): Promise<unknown> => {
82
+ calls.push(`batch:${sql}`);
83
+ if (script?.batchError) {
84
+ throw script.batchError;
85
+ }
86
+ return undefined;
87
+ },
88
+ query: async (
89
+ sql: string,
90
+ ): Promise<{
91
+ recordset?: Array<Record<string, unknown>> | undefined;
92
+ }> => {
93
+ calls.push(`query:${sql}`);
94
+ if (script?.queryError) {
95
+ throw script.queryError;
96
+ }
97
+ if (script?.omitRecordset) {
98
+ return {};
99
+ }
100
+ return { recordset: script?.rows || [] };
101
+ },
102
+ };
103
+ },
104
+ };
105
+
106
+ return {
107
+ connect: async (): Promise<void> => {
108
+ calls.push("connect");
109
+ if (script?.connectError) {
110
+ throw script.connectError;
111
+ }
112
+ },
113
+ close: async (): Promise<void> => {
114
+ calls.push("close");
115
+ if (script?.closeError) {
116
+ throw script.closeError;
117
+ }
118
+ },
119
+ transaction: (): SqlServerTransactionLike => {
120
+ return transaction;
121
+ },
122
+ };
123
+ };
124
+
125
+ return { factory: factory, calls: calls, configs: configs };
126
+ };
127
+
128
+ const PASSWORD: string = "sup3r-s3cret-pw";
129
+ const HOST: string = "mssql.internal.example.com";
130
+
131
+ type BuildSettingsFunction = (
132
+ overrides?: Partial<DataSourceConnectionSettings>,
133
+ ) => DataSourceConnectionSettings;
134
+
135
+ const buildSettings: BuildSettingsFunction = (
136
+ overrides?: Partial<DataSourceConnectionSettings>,
137
+ ): DataSourceConnectionSettings => {
138
+ return {
139
+ dataSourceType: DataSourceType.MicrosoftSqlServer,
140
+ databaseHost: HOST,
141
+ databasePort: 1433,
142
+ databaseName: "metrics",
143
+ username: "readonly_login",
144
+ password: PASSWORD,
145
+ ...overrides,
146
+ };
147
+ };
148
+
149
+ const WINDOW: DataSourceQueryWindow = {
150
+ startDate: new Date("2026-01-01T00:00:00.000Z"),
151
+ endDate: new Date("2026-01-01T01:00:00.000Z"),
152
+ };
153
+
154
+ type MockEgressAllowedFunction = (events?: Array<string>) => jest.SpyInstance;
155
+
156
+ const mockEgressAllowed: MockEgressAllowedFunction = (
157
+ events?: Array<string>,
158
+ ): jest.SpyInstance => {
159
+ const spy: jest.SpyInstance = jest.spyOn(
160
+ DataSourceEgressGuard,
161
+ "assertHostnameAllowed",
162
+ );
163
+ spy.mockImplementation(() => {
164
+ if (events) {
165
+ events.push("egress");
166
+ }
167
+ return Promise.resolve([{ address: "203.0.113.10", family: 4 }]);
168
+ });
169
+ return spy;
170
+ };
171
+
172
+ type FindExecutedQueryFunction = (harness: Harness) => string | undefined;
173
+
174
+ const findExecutedQuery: FindExecutedQueryFunction = (
175
+ harness: Harness,
176
+ ): string | undefined => {
177
+ const call: string | undefined = harness.calls.find((entry: string) => {
178
+ return entry.startsWith("query:");
179
+ });
180
+ return call ? call.substring("query:".length) : undefined;
181
+ };
182
+
183
+ afterEach(() => {
184
+ jest.restoreAllMocks();
185
+ });
186
+
187
+ describe("SqlServerConnector", () => {
188
+ test("exposes the Microsoft SQL Server data source type", () => {
189
+ const connector: SqlServerConnector = new SqlServerConnector();
190
+ expect(connector.dataSourceType).toBe(DataSourceType.MicrosoftSqlServer);
191
+ });
192
+
193
+ describe("queryTimeSeries", () => {
194
+ test("shapes time/value rows into AggregatedResult without attributes", async () => {
195
+ mockEgressAllowed();
196
+ const harness: Harness = createHarness({
197
+ rows: [
198
+ { time: "2026-01-01 00:00:00", value: 5 },
199
+ { time: new Date("2026-01-01T00:10:00.000Z"), value: "7.5" },
200
+ ],
201
+ });
202
+ const connector: SqlServerConnector = new SqlServerConnector(
203
+ harness.factory,
204
+ );
205
+
206
+ const result: AggregatedResult = await connector.queryTimeSeries(
207
+ buildSettings(),
208
+ "SELECT time, value FROM metrics_table",
209
+ WINDOW,
210
+ );
211
+
212
+ expect(result.truncated).toBe(false);
213
+ expect(result.data).toHaveLength(2);
214
+ expect(result.data[0]!.timestamp).toEqual(
215
+ new Date("2026-01-01T00:00:00.000Z"),
216
+ );
217
+ expect(result.data[0]!.value).toBe(5);
218
+ expect(result.data[0]!["attributes"]).toBeUndefined();
219
+ expect(result.data[1]!.timestamp).toEqual(
220
+ new Date("2026-01-01T00:10:00.000Z"),
221
+ );
222
+ expect(result.data[1]!.value).toBe(7.5);
223
+ });
224
+
225
+ test("places series label columns under attributes on each point", async () => {
226
+ mockEgressAllowed();
227
+ const harness: Harness = createHarness({
228
+ rows: [
229
+ { time: "2026-01-01 00:00:00", value: 1, host: "api-1" },
230
+ { time: "2026-01-01 00:00:00", value: 2, host: "api-2" },
231
+ ],
232
+ });
233
+ const connector: SqlServerConnector = new SqlServerConnector(
234
+ harness.factory,
235
+ );
236
+
237
+ const result: AggregatedResult = await connector.queryTimeSeries(
238
+ buildSettings(),
239
+ "SELECT time, value, host FROM metrics_table",
240
+ WINDOW,
241
+ );
242
+
243
+ expect(result.data).toHaveLength(2);
244
+ expect(result.data[0]!["attributes"]).toEqual({ host: "api-1" });
245
+ expect(result.data[1]!["attributes"]).toEqual({ host: "api-2" });
246
+ });
247
+
248
+ test("returns an empty result for an empty recordset", async () => {
249
+ mockEgressAllowed();
250
+ const harness: Harness = createHarness({ rows: [] });
251
+ const connector: SqlServerConnector = new SqlServerConnector(
252
+ harness.factory,
253
+ );
254
+
255
+ const result: AggregatedResult = await connector.queryTimeSeries(
256
+ buildSettings(),
257
+ "SELECT time, value FROM empty_table",
258
+ WINDOW,
259
+ );
260
+
261
+ expect(result).toEqual({ data: [], truncated: false });
262
+ });
263
+
264
+ test("treats a missing recordset (non-row batch response) as empty", async () => {
265
+ mockEgressAllowed();
266
+ const harness: Harness = createHarness({ omitRecordset: true });
267
+ const connector: SqlServerConnector = new SqlServerConnector(
268
+ harness.factory,
269
+ );
270
+
271
+ const result: AggregatedResult = await connector.queryTimeSeries(
272
+ buildSettings(),
273
+ "SELECT time, value FROM metrics_table",
274
+ WINDOW,
275
+ );
276
+
277
+ expect(result.data).toEqual([]);
278
+ });
279
+
280
+ test("clamps to the time-series row cap and flags truncation", async () => {
281
+ mockEgressAllowed();
282
+ const rows: Array<Record<string, unknown>> = [];
283
+ for (let i: number = 0; i < DATA_SOURCE_MAX_TIME_SERIES_ROWS + 1; i++) {
284
+ rows.push({ time: 1700000000 + i, value: i });
285
+ }
286
+ const harness: Harness = createHarness({ rows: rows });
287
+ const connector: SqlServerConnector = new SqlServerConnector(
288
+ harness.factory,
289
+ );
290
+
291
+ const result: AggregatedResult = await connector.queryTimeSeries(
292
+ buildSettings(),
293
+ "SELECT time, value FROM big_table",
294
+ WINDOW,
295
+ );
296
+
297
+ expect(result.truncated).toBe(true);
298
+ expect(result.data).toHaveLength(DATA_SOURCE_MAX_TIME_SERIES_ROWS);
299
+ });
300
+
301
+ test("runs SET ROWCOUNT cap+1 before the query and always rolls back and closes", async () => {
302
+ mockEgressAllowed();
303
+ const harness: Harness = createHarness({
304
+ rows: [{ time: 1700000000, value: 1 }],
305
+ });
306
+ const connector: SqlServerConnector = new SqlServerConnector(
307
+ harness.factory,
308
+ );
309
+ const sql: string = "SELECT time, value FROM metrics_table";
310
+
311
+ await connector.queryTimeSeries(buildSettings(), sql, WINDOW);
312
+
313
+ expect(harness.calls).toEqual([
314
+ "connect",
315
+ "begin",
316
+ `batch:SET ROWCOUNT ${DATA_SOURCE_MAX_TIME_SERIES_ROWS + 1}`,
317
+ `query:${sql}`,
318
+ "rollback",
319
+ "close",
320
+ ]);
321
+ });
322
+
323
+ test("applies time macros and executes exactly the interpolated SQL", async () => {
324
+ mockEgressAllowed();
325
+ const harness: Harness = createHarness({ rows: [] });
326
+ const connector: SqlServerConnector = new SqlServerConnector(
327
+ harness.factory,
328
+ );
329
+
330
+ await connector.queryTimeSeries(
331
+ buildSettings(),
332
+ "SELECT time, value FROM m WHERE t BETWEEN $__startTime AND $__endTime AND ms >= $__startTimeMs AND ms < $__endTimeMs AND r = $__rangeSeconds",
333
+ WINDOW,
334
+ );
335
+
336
+ const expected: string = `SELECT time, value FROM m WHERE t BETWEEN '2026-01-01 00:00:00' AND '2026-01-01 01:00:00' AND ms >= ${WINDOW.startDate.getTime()} AND ms < ${WINDOW.endDate.getTime()} AND r = 3600`;
337
+ expect(findExecutedQuery(harness)).toBe(expected);
338
+ });
339
+
340
+ test("executes exactly the final SQL including a trailing semicolon", async () => {
341
+ mockEgressAllowed();
342
+ const harness: Harness = createHarness({ rows: [] });
343
+ const connector: SqlServerConnector = new SqlServerConnector(
344
+ harness.factory,
345
+ );
346
+ const sql: string = "SELECT time, value FROM metrics_table;";
347
+
348
+ await connector.queryTimeSeries(buildSettings(), sql, WINDOW);
349
+
350
+ expect(findExecutedQuery(harness)).toBe(sql);
351
+ });
352
+
353
+ test("surfaces the shaper error when no time column is returned", async () => {
354
+ mockEgressAllowed();
355
+ const harness: Harness = createHarness({
356
+ rows: [{ foo: "bar", value: 2 }],
357
+ });
358
+ const connector: SqlServerConnector = new SqlServerConnector(
359
+ harness.factory,
360
+ );
361
+
362
+ await expect(
363
+ connector.queryTimeSeries(
364
+ buildSettings(),
365
+ "SELECT foo, value FROM metrics_table",
366
+ WINDOW,
367
+ ),
368
+ ).rejects.toThrow(/time column/);
369
+ });
370
+ });
371
+
372
+ describe("queryTable", () => {
373
+ test("shapes rows into columns with sniffed types and coerced cells", async () => {
374
+ mockEgressAllowed();
375
+ const harness: Harness = createHarness({
376
+ rows: [
377
+ {
378
+ name: "api",
379
+ count: 3,
380
+ at: new Date("2026-01-01T00:00:00.000Z"),
381
+ ok: true,
382
+ },
383
+ ],
384
+ });
385
+ const connector: SqlServerConnector = new SqlServerConnector(
386
+ harness.factory,
387
+ );
388
+
389
+ const result: DataSourceTableResult = await connector.queryTable(
390
+ buildSettings(),
391
+ "SELECT name, count, at, ok FROM services",
392
+ WINDOW,
393
+ );
394
+
395
+ expect(result.truncated).toBe(false);
396
+ expect(result.columns).toEqual([
397
+ { key: "name", title: "name", type: DataSourceTableColumnType.Text },
398
+ {
399
+ key: "count",
400
+ title: "count",
401
+ type: DataSourceTableColumnType.Number,
402
+ },
403
+ { key: "at", title: "at", type: DataSourceTableColumnType.Date },
404
+ { key: "ok", title: "ok", type: DataSourceTableColumnType.Boolean },
405
+ ]);
406
+ expect(result.rows).toEqual([
407
+ {
408
+ name: "api",
409
+ count: 3,
410
+ at: "2026-01-01T00:00:00.000Z",
411
+ ok: true,
412
+ },
413
+ ]);
414
+ });
415
+
416
+ test("uses the table row cap for SET ROWCOUNT and flags truncation", async () => {
417
+ mockEgressAllowed();
418
+ const rows: Array<Record<string, unknown>> = [];
419
+ for (let i: number = 0; i < DATA_SOURCE_MAX_TABLE_ROWS + 1; i++) {
420
+ rows.push({ id: i });
421
+ }
422
+ const harness: Harness = createHarness({ rows: rows });
423
+ const connector: SqlServerConnector = new SqlServerConnector(
424
+ harness.factory,
425
+ );
426
+
427
+ const result: DataSourceTableResult = await connector.queryTable(
428
+ buildSettings(),
429
+ "SELECT id FROM big_table",
430
+ WINDOW,
431
+ );
432
+
433
+ expect(result.truncated).toBe(true);
434
+ expect(result.rows).toHaveLength(DATA_SOURCE_MAX_TABLE_ROWS);
435
+ expect(harness.calls).toContain(
436
+ `batch:SET ROWCOUNT ${DATA_SOURCE_MAX_TABLE_ROWS + 1}`,
437
+ );
438
+ });
439
+
440
+ test("returns an empty table for an empty recordset", async () => {
441
+ mockEgressAllowed();
442
+ const harness: Harness = createHarness({ rows: [] });
443
+ const connector: SqlServerConnector = new SqlServerConnector(
444
+ harness.factory,
445
+ );
446
+
447
+ const result: DataSourceTableResult = await connector.queryTable(
448
+ buildSettings(),
449
+ "SELECT id FROM empty_table",
450
+ WINDOW,
451
+ );
452
+
453
+ expect(result).toEqual({ columns: [], rows: [], truncated: false });
454
+ });
455
+ });
456
+
457
+ describe("read-only guard", () => {
458
+ const forbiddenQueries: Array<{ title: string; sql: string }> = [
459
+ { title: "xp_cmdshell execution", sql: "EXEC xp_cmdshell 'dir'" },
460
+ {
461
+ title: "WAITFOR smuggled after a SELECT",
462
+ sql: "SELECT 1 WAITFOR DELAY '00:00:10'",
463
+ },
464
+ {
465
+ title: "sp_configure system procedure",
466
+ sql: "SELECT 1 sp_configure 'show advanced options'",
467
+ },
468
+ {
469
+ title: "SELECT INTO table creation",
470
+ sql: "SELECT * INTO stolen_data FROM users",
471
+ },
472
+ {
473
+ title: "semicolon-less batch smuggling",
474
+ sql: "SELECT 1 DROP TABLE x",
475
+ },
476
+ {
477
+ title: "stacked statement with a semicolon",
478
+ sql: "SELECT 1; DELETE FROM users",
479
+ },
480
+ {
481
+ title: "UPDATE statement",
482
+ sql: "UPDATE users SET name = 'x'",
483
+ },
484
+ ];
485
+
486
+ for (const forbidden of forbiddenQueries) {
487
+ test(`rejects ${forbidden.title} without creating a pool`, async () => {
488
+ const egressSpy: jest.SpyInstance = mockEgressAllowed();
489
+ const harness: Harness = createHarness();
490
+ const connector: SqlServerConnector = new SqlServerConnector(
491
+ harness.factory,
492
+ );
493
+
494
+ await expect(
495
+ connector.queryTimeSeries(buildSettings(), forbidden.sql, WINDOW),
496
+ ).rejects.toThrow(BadDataException);
497
+
498
+ expect(harness.configs).toHaveLength(0);
499
+ expect(harness.calls).toHaveLength(0);
500
+ expect(egressSpy).not.toHaveBeenCalled();
501
+ });
502
+ }
503
+
504
+ test("rejects an empty query", async () => {
505
+ mockEgressAllowed();
506
+ const harness: Harness = createHarness();
507
+ const connector: SqlServerConnector = new SqlServerConnector(
508
+ harness.factory,
509
+ );
510
+
511
+ await expect(
512
+ connector.queryTimeSeries(buildSettings(), " ", WINDOW),
513
+ ).rejects.toThrow(BadDataException);
514
+
515
+ expect(harness.configs).toHaveLength(0);
516
+ });
517
+
518
+ test("rejects backslash-escaped quotes", async () => {
519
+ mockEgressAllowed();
520
+ const harness: Harness = createHarness();
521
+ const connector: SqlServerConnector = new SqlServerConnector(
522
+ harness.factory,
523
+ );
524
+
525
+ await expect(
526
+ connector.queryTimeSeries(
527
+ buildSettings(),
528
+ "SELECT 'it\\'s' AS value, 1700000000 AS time",
529
+ WINDOW,
530
+ ),
531
+ ).rejects.toThrow(/Backslash-escaped/);
532
+
533
+ expect(harness.configs).toHaveLength(0);
534
+ });
535
+
536
+ test("allows forbidden words inside string literals", async () => {
537
+ mockEgressAllowed();
538
+ const harness: Harness = createHarness({ rows: [] });
539
+ const connector: SqlServerConnector = new SqlServerConnector(
540
+ harness.factory,
541
+ );
542
+ const sql: string =
543
+ "SELECT 'DROP TABLE x' AS label, 1 AS value, 1700000000 AS time";
544
+
545
+ await connector.queryTimeSeries(buildSettings(), sql, WINDOW);
546
+
547
+ expect(findExecutedQuery(harness)).toBe(sql);
548
+ });
549
+
550
+ test("allows forbidden words inside bracket-quoted identifiers", async () => {
551
+ mockEgressAllowed();
552
+ const harness: Harness = createHarness({ rows: [] });
553
+ const connector: SqlServerConnector = new SqlServerConnector(
554
+ harness.factory,
555
+ );
556
+ const sql: string =
557
+ "SELECT [delete] AS value, 1700000000 AS time FROM [orders]";
558
+
559
+ await connector.queryTimeSeries(buildSettings(), sql, WINDOW);
560
+
561
+ expect(findExecutedQuery(harness)).toBe(sql);
562
+ });
563
+ });
564
+
565
+ describe("egress guard", () => {
566
+ test("validates the database host before connecting", async () => {
567
+ const harness: Harness = createHarness({ rows: [] });
568
+ const egressSpy: jest.SpyInstance = mockEgressAllowed(harness.calls);
569
+ const connector: SqlServerConnector = new SqlServerConnector(
570
+ harness.factory,
571
+ );
572
+
573
+ await connector.queryTimeSeries(
574
+ buildSettings(),
575
+ "SELECT time, value FROM metrics_table",
576
+ WINDOW,
577
+ );
578
+
579
+ expect(egressSpy).toHaveBeenCalledWith(HOST);
580
+ expect(harness.calls[0]).toBe("egress");
581
+ expect(harness.calls[1]).toBe("connect");
582
+ });
583
+
584
+ test("propagates an egress guard rejection without creating a pool", async () => {
585
+ const egressSpy: jest.SpyInstance = jest.spyOn(
586
+ DataSourceEgressGuard,
587
+ "assertHostnameAllowed",
588
+ );
589
+ egressSpy.mockRejectedValue(
590
+ new BadDataException(
591
+ "Data source host 169.254.169.254 is not allowed: link-local address (cloud metadata range).",
592
+ ),
593
+ );
594
+ const harness: Harness = createHarness();
595
+ const connector: SqlServerConnector = new SqlServerConnector(
596
+ harness.factory,
597
+ );
598
+
599
+ await expect(
600
+ connector.queryTimeSeries(
601
+ buildSettings(),
602
+ "SELECT time, value FROM metrics_table",
603
+ WINDOW,
604
+ ),
605
+ ).rejects.toThrow("not allowed");
606
+
607
+ expect(harness.configs).toHaveLength(0);
608
+ expect(harness.calls).toHaveLength(0);
609
+ });
610
+
611
+ test("throws without touching the guard when databaseHost is missing", async () => {
612
+ const egressSpy: jest.SpyInstance = mockEgressAllowed();
613
+ const harness: Harness = createHarness();
614
+ const connector: SqlServerConnector = new SqlServerConnector(
615
+ harness.factory,
616
+ );
617
+
618
+ await expect(
619
+ connector.queryTimeSeries(
620
+ buildSettings({ databaseHost: undefined }),
621
+ "SELECT time, value FROM metrics_table",
622
+ WINDOW,
623
+ ),
624
+ ).rejects.toThrow(/database host/i);
625
+
626
+ expect(egressSpy).not.toHaveBeenCalled();
627
+ expect(harness.configs).toHaveLength(0);
628
+ });
629
+ });
630
+
631
+ describe("pool configuration", () => {
632
+ test("maps settings and limits onto the mssql config", async () => {
633
+ mockEgressAllowed();
634
+ const harness: Harness = createHarness({ rows: [] });
635
+ const connector: SqlServerConnector = new SqlServerConnector(
636
+ harness.factory,
637
+ );
638
+
639
+ await connector.queryTable(
640
+ buildSettings({ databasePort: 14330 }),
641
+ "SELECT id FROM services",
642
+ WINDOW,
643
+ );
644
+
645
+ const config: mssql.config = harness.configs[0]!;
646
+ expect(config.server).toBe(HOST);
647
+ expect(config.port).toBe(14330);
648
+ expect(config.database).toBe("metrics");
649
+ expect(config.user).toBe("readonly_login");
650
+ expect(config.password).toBe(PASSWORD);
651
+ expect(config.connectionTimeout).toBe(DATA_SOURCE_CONNECT_TIMEOUT_IN_MS);
652
+ expect(config.requestTimeout).toBe(DATA_SOURCE_QUERY_TIMEOUT_IN_MS);
653
+ expect(config.pool).toEqual({ max: 1, min: 0, idleTimeoutMillis: 30000 });
654
+ expect(config.options?.appName).toBe("OneUptime-DataSource");
655
+ });
656
+
657
+ test("defaults the port to 1433 when none is configured", async () => {
658
+ mockEgressAllowed();
659
+ const harness: Harness = createHarness({ rows: [] });
660
+ const connector: SqlServerConnector = new SqlServerConnector(
661
+ harness.factory,
662
+ );
663
+
664
+ await connector.queryTable(
665
+ buildSettings({ databasePort: undefined }),
666
+ "SELECT id FROM services",
667
+ WINDOW,
668
+ );
669
+
670
+ expect(harness.configs[0]!.port).toBe(1433);
671
+ });
672
+
673
+ test("disables encryption and trusts the certificate when TLS is off", async () => {
674
+ mockEgressAllowed();
675
+ const harness: Harness = createHarness({ rows: [] });
676
+ const connector: SqlServerConnector = new SqlServerConnector(
677
+ harness.factory,
678
+ );
679
+
680
+ await connector.queryTable(
681
+ buildSettings(),
682
+ "SELECT id FROM services",
683
+ WINDOW,
684
+ );
685
+
686
+ const config: mssql.config = harness.configs[0]!;
687
+ expect(config.options?.encrypt).toBe(false);
688
+ expect(config.options?.trustServerCertificate).toBe(true);
689
+ });
690
+
691
+ test("enables encryption with certificate verification ON by default", async () => {
692
+ mockEgressAllowed();
693
+ const harness: Harness = createHarness({ rows: [] });
694
+ const connector: SqlServerConnector = new SqlServerConnector(
695
+ harness.factory,
696
+ );
697
+
698
+ await connector.queryTable(
699
+ buildSettings({ additionalOptions: { sslEnabled: true } }),
700
+ "SELECT id FROM services",
701
+ WINDOW,
702
+ );
703
+
704
+ const config: mssql.config = harness.configs[0]!;
705
+ expect(config.options?.encrypt).toBe(true);
706
+ expect(config.options?.trustServerCertificate).toBe(false);
707
+ });
708
+
709
+ test("trusts the server certificate only on explicit opt-out", async () => {
710
+ mockEgressAllowed();
711
+ const harness: Harness = createHarness({ rows: [] });
712
+ const connector: SqlServerConnector = new SqlServerConnector(
713
+ harness.factory,
714
+ );
715
+
716
+ await connector.queryTable(
717
+ buildSettings({
718
+ additionalOptions: { sslEnabled: true, sslRejectUnauthorized: false },
719
+ }),
720
+ "SELECT id FROM services",
721
+ WINDOW,
722
+ );
723
+
724
+ const config: mssql.config = harness.configs[0]!;
725
+ expect(config.options?.encrypt).toBe(true);
726
+ expect(config.options?.trustServerCertificate).toBe(true);
727
+ });
728
+ });
729
+
730
+ describe("error handling", () => {
731
+ test("sanitizes query failures and still rolls back and closes", async () => {
732
+ mockEgressAllowed();
733
+ const harness: Harness = createHarness({
734
+ queryError: new Error(
735
+ `Login failed for readonly_login using password ${PASSWORD} against ${HOST}`,
736
+ ),
737
+ });
738
+ const connector: SqlServerConnector = new SqlServerConnector(
739
+ harness.factory,
740
+ );
741
+
742
+ let thrown: Error | null = null;
743
+ try {
744
+ await connector.queryTimeSeries(
745
+ buildSettings(),
746
+ "SELECT time, value FROM metrics_table",
747
+ WINDOW,
748
+ );
749
+ } catch (error) {
750
+ thrown = error as Error;
751
+ }
752
+
753
+ expect(thrown).toBeInstanceOf(BadDataException);
754
+ expect(thrown!.message).not.toContain(PASSWORD);
755
+ expect(thrown!.message).not.toContain(HOST);
756
+ expect(thrown!.message).not.toContain("readonly_login");
757
+ expect(thrown!.message).toContain("***");
758
+ expect(harness.calls).toContain("rollback");
759
+ expect(harness.calls).toContain("close");
760
+ });
761
+
762
+ test("closes the pool without rolling back when connect fails", async () => {
763
+ mockEgressAllowed();
764
+ const harness: Harness = createHarness({
765
+ connectError: new Error(`getaddrinfo ENOTFOUND ${HOST}`),
766
+ });
767
+ const connector: SqlServerConnector = new SqlServerConnector(
768
+ harness.factory,
769
+ );
770
+
771
+ let thrown: Error | null = null;
772
+ try {
773
+ await connector.queryTimeSeries(
774
+ buildSettings(),
775
+ "SELECT time, value FROM metrics_table",
776
+ WINDOW,
777
+ );
778
+ } catch (error) {
779
+ thrown = error as Error;
780
+ }
781
+
782
+ expect(thrown).toBeInstanceOf(BadDataException);
783
+ expect(thrown!.message).not.toContain(HOST);
784
+ expect(harness.calls).not.toContain("rollback");
785
+ expect(harness.calls).toContain("close");
786
+ });
787
+
788
+ test("closes the pool without rolling back when begin fails", async () => {
789
+ mockEgressAllowed();
790
+ const harness: Harness = createHarness({
791
+ beginError: new Error("EALREADYBEGUN: transaction already begun"),
792
+ });
793
+ const connector: SqlServerConnector = new SqlServerConnector(
794
+ harness.factory,
795
+ );
796
+
797
+ await expect(
798
+ connector.queryTimeSeries(
799
+ buildSettings(),
800
+ "SELECT time, value FROM metrics_table",
801
+ WINDOW,
802
+ ),
803
+ ).rejects.toThrow(BadDataException);
804
+
805
+ expect(harness.calls).not.toContain("rollback");
806
+ expect(harness.calls).toContain("close");
807
+ });
808
+
809
+ test("sanitizes SET ROWCOUNT batch failures and still tears down", async () => {
810
+ mockEgressAllowed();
811
+ const harness: Harness = createHarness({
812
+ batchError: new Error(`syntax error near ROWCOUNT on ${HOST}`),
813
+ });
814
+ const connector: SqlServerConnector = new SqlServerConnector(
815
+ harness.factory,
816
+ );
817
+
818
+ let thrown: Error | null = null;
819
+ try {
820
+ await connector.queryTimeSeries(
821
+ buildSettings(),
822
+ "SELECT time, value FROM metrics_table",
823
+ WINDOW,
824
+ );
825
+ } catch (error) {
826
+ thrown = error as Error;
827
+ }
828
+
829
+ expect(thrown).toBeInstanceOf(BadDataException);
830
+ expect(thrown!.message).not.toContain(HOST);
831
+ expect(harness.calls).toContain("rollback");
832
+ expect(harness.calls).toContain("close");
833
+ });
834
+
835
+ test("swallows rollback failures and still returns rows", async () => {
836
+ mockEgressAllowed();
837
+ const harness: Harness = createHarness({
838
+ rows: [{ time: 1700000000, value: 1 }],
839
+ rollbackError: new Error("ENOTBEGUN: transaction is gone"),
840
+ });
841
+ const connector: SqlServerConnector = new SqlServerConnector(
842
+ harness.factory,
843
+ );
844
+
845
+ const result: AggregatedResult = await connector.queryTimeSeries(
846
+ buildSettings(),
847
+ "SELECT time, value FROM metrics_table",
848
+ WINDOW,
849
+ );
850
+
851
+ expect(result.data).toHaveLength(1);
852
+ expect(harness.calls).toContain("close");
853
+ });
854
+
855
+ test("swallows pool close failures and still returns rows", async () => {
856
+ mockEgressAllowed();
857
+ const harness: Harness = createHarness({
858
+ rows: [{ time: 1700000000, value: 1 }],
859
+ closeError: new Error("pool close timed out"),
860
+ });
861
+ const connector: SqlServerConnector = new SqlServerConnector(
862
+ harness.factory,
863
+ );
864
+
865
+ const result: AggregatedResult = await connector.queryTimeSeries(
866
+ buildSettings(),
867
+ "SELECT time, value FROM metrics_table",
868
+ WINDOW,
869
+ );
870
+
871
+ expect(result.data).toHaveLength(1);
872
+ });
873
+ });
874
+
875
+ describe("testConnection", () => {
876
+ test("runs SELECT 1 through the guarded transactional path and closes", async () => {
877
+ const egressSpy: jest.SpyInstance = mockEgressAllowed();
878
+ const harness: Harness = createHarness({ rows: [{ "": 1 }] });
879
+ const connector: SqlServerConnector = new SqlServerConnector(
880
+ harness.factory,
881
+ );
882
+
883
+ await connector.testConnection(buildSettings());
884
+
885
+ expect(egressSpy).toHaveBeenCalledWith(HOST);
886
+ expect(harness.calls).toEqual([
887
+ "connect",
888
+ "begin",
889
+ "batch:SET ROWCOUNT 1",
890
+ "query:SELECT 1",
891
+ "rollback",
892
+ "close",
893
+ ]);
894
+ });
895
+
896
+ test("validates egress before opening a connection", async () => {
897
+ const harness: Harness = createHarness({ rows: [] });
898
+ mockEgressAllowed(harness.calls);
899
+ const connector: SqlServerConnector = new SqlServerConnector(
900
+ harness.factory,
901
+ );
902
+
903
+ await connector.testConnection(buildSettings());
904
+
905
+ expect(harness.calls[0]).toBe("egress");
906
+ expect(harness.calls[1]).toBe("connect");
907
+ });
908
+
909
+ test("sanitizes connection failures so secrets never leak", async () => {
910
+ mockEgressAllowed();
911
+ const harness: Harness = createHarness({
912
+ connectError: new Error(
913
+ `Login failed. Connection string: Server=${HOST};Password=${PASSWORD}`,
914
+ ),
915
+ });
916
+ const connector: SqlServerConnector = new SqlServerConnector(
917
+ harness.factory,
918
+ );
919
+
920
+ let thrown: Error | null = null;
921
+ try {
922
+ await connector.testConnection(buildSettings());
923
+ } catch (error) {
924
+ thrown = error as Error;
925
+ }
926
+
927
+ expect(thrown).toBeInstanceOf(BadDataException);
928
+ expect(thrown!.message).not.toContain(PASSWORD);
929
+ expect(thrown!.message).not.toContain(HOST);
930
+ expect(thrown!.message).toContain("***");
931
+ expect(harness.calls).toContain("close");
932
+ });
933
+ });
934
+ });