@oneuptime/common 12.0.32 → 12.0.33

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 (597) hide show
  1. package/Models/AnalyticsModels/Span.ts +14 -0
  2. package/Models/DatabaseModels/EnterpriseLicense.ts +56 -0
  3. package/Models/DatabaseModels/Index.ts +10 -0
  4. package/Models/DatabaseModels/Monitor.ts +76 -0
  5. package/Models/DatabaseModels/NetworkAlertPolicy.ts +690 -0
  6. package/Models/DatabaseModels/NetworkDevice.ts +176 -1
  7. package/Models/DatabaseModels/NetworkSite.ts +201 -2
  8. package/Models/DatabaseModels/NetworkSiteType.ts +102 -3
  9. package/Models/DatabaseModels/NetworkSnmpCredentialProfile.ts +841 -0
  10. package/Models/DatabaseModels/TelemetryException.ts +119 -0
  11. package/Models/DatabaseModels/UserNotificationEmailRollupBatch.ts +334 -0
  12. package/Models/DatabaseModels/UserNotificationEmailRollupItem.ts +370 -0
  13. package/Models/DatabaseModels/UserNotificationEmailRollupSetting.ts +282 -0
  14. package/Server/API/EnterpriseLicenseAPI.ts +391 -64
  15. package/Server/API/LlmProviderAPI.ts +137 -22
  16. package/Server/EnvironmentConfig.ts +14 -0
  17. package/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.ts +55 -0
  18. package/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.ts +31 -0
  19. package/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.ts +124 -0
  20. package/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.ts +132 -0
  21. package/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.ts +79 -0
  22. package/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.ts +191 -0
  23. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +12 -0
  24. package/Server/Infrastructure/QueueWorker.ts +38 -8
  25. package/Server/Middleware/BearerTokenAuthorization.ts +12 -0
  26. package/Server/Middleware/ProjectAuthorization.ts +11 -0
  27. package/Server/Middleware/SCIMAuthorization.ts +12 -0
  28. package/Server/Middleware/TelemetryIngest.ts +11 -0
  29. package/Server/Services/AIBillingService.ts +101 -27
  30. package/Server/Services/EnterpriseLicenseInstanceService.ts +197 -0
  31. package/Server/Services/EnterpriseLicenseService.ts +55 -0
  32. package/Server/Services/Index.ts +10 -0
  33. package/Server/Services/MonitorService.ts +143 -21
  34. package/Server/Services/MonitorStatusTimelineService.ts +150 -8
  35. package/Server/Services/MonitorTemplateService.ts +217 -1
  36. package/Server/Services/NetworkAlertPolicyEngineService.ts +2159 -0
  37. package/Server/Services/NetworkAlertPolicyService.ts +944 -0
  38. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +9 -10
  39. package/Server/Services/NetworkDeviceAutoImportRuleService.ts +60 -15
  40. package/Server/Services/NetworkDeviceService.ts +1032 -73
  41. package/Server/Services/NetworkSiteService.ts +1502 -129
  42. package/Server/Services/NetworkSiteTypeService.ts +1216 -0
  43. package/Server/Services/NetworkSnmpCredentialProfileService.ts +238 -0
  44. package/Server/Services/NotificationService.ts +118 -28
  45. package/Server/Services/ProjectService.ts +105 -48
  46. package/Server/Services/StatusPagePrivateUserService.ts +103 -3
  47. package/Server/Services/UserNotificationEmailRollupBatchService.ts +78 -0
  48. package/Server/Services/UserNotificationEmailRollupItemService.ts +79 -0
  49. package/Server/Services/UserNotificationEmailRollupSettingService.ts +113 -0
  50. package/Server/Services/UserNotificationSettingService.ts +62 -25
  51. package/Server/Services/UserService.ts +95 -0
  52. package/Server/Types/Database/QueryHelper.ts +2 -2
  53. package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +14 -3
  54. package/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.ts +10 -0
  55. package/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.ts +10 -0
  56. package/Server/Utils/AI/SRE/Insights/InsightTriageRunner.ts +62 -1
  57. package/Server/Utils/AI/Toolbox/AlertTools.ts +15 -1
  58. package/Server/Utils/Billing/BillingFailureNoticeThrottle.ts +80 -0
  59. package/Server/Utils/DataSource/HttpFetch.ts +9 -2
  60. package/Server/Utils/EmailRollup/EmailRollupConstants.ts +98 -0
  61. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +872 -0
  62. package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +781 -0
  63. package/Server/Utils/EmailRollup/EmailRollupWriter.ts +368 -0
  64. package/Server/Utils/LLM/LLMService.ts +78 -0
  65. package/Server/Utils/LogRedaction.ts +8 -0
  66. package/Server/Utils/Logger.ts +79 -1
  67. package/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.ts +190 -0
  68. package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +9 -2
  69. package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +61 -1
  70. package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +14 -0
  71. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +49 -0
  72. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +255 -4
  73. package/Server/Utils/Monitor/MonitorMetricUtil.ts +41 -0
  74. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +43 -0
  75. package/Server/Utils/Monitor/NetworkDeviceHydrationUtil.ts +499 -19
  76. package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +66 -10
  77. package/Server/Utils/Monitor/NetworkDeviceWalkUtil.ts +238 -44
  78. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +146 -39
  79. package/Server/Utils/NetworkDevice/DeviceHealthAggregation.ts +31 -18
  80. package/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.ts +213 -0
  81. package/Server/Utils/OutboundUserAgent.ts +152 -0
  82. package/Server/Utils/StartServer.ts +51 -19
  83. package/Server/Utils/Telemetry/CaptureSpan.ts +32 -8
  84. package/Server/Utils/Telemetry/ErrorClassResolver.ts +120 -0
  85. package/Server/Utils/Telemetry/SpanUtil.ts +33 -0
  86. package/Server/Utils/Telemetry.ts +198 -51
  87. package/Server/Views/Partials/SensitiveUrlToken.ejs +140 -0
  88. package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +134 -33
  89. package/Tests/App/Dashboard/AskAiSuggestedPrompts.test.ts +279 -0
  90. package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +517 -0
  91. package/Tests/App/Dashboard/DashboardVariablesDiscard.test.tsx +26 -0
  92. package/Tests/App/Dashboard/DeviceStatusHero.test.tsx +588 -0
  93. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +18 -9
  94. package/Tests/App/Dashboard/MonitorCreateFromMonitorBackedDevice.test.tsx +632 -0
  95. package/Tests/App/Dashboard/MonitorRecommendationCreateProgress.test.tsx +682 -0
  96. package/Tests/App/Dashboard/MonitorTypePicker.test.tsx +4 -2
  97. package/Tests/App/Dashboard/NetworkDeviceCreateFormMonitorBinding.test.tsx +676 -0
  98. package/Tests/App/Dashboard/NetworkDeviceCreateFormPingMonitor.test.tsx +791 -0
  99. package/Tests/App/Dashboard/NetworkDeviceCreateFormProbeAndSnmp.test.tsx +833 -0
  100. package/Tests/App/Dashboard/NetworkDeviceCriteriaFilter.test.ts +161 -0
  101. package/Tests/App/Dashboard/NetworkDeviceSettingsMonitoringSection.test.tsx +458 -0
  102. package/Tests/App/Dashboard/NetworkSideMenu.test.tsx +41 -5
  103. package/Tests/App/Dashboard/PingMonitorProvisioning.test.ts +368 -0
  104. package/Tests/App/Dashboard/RecommendationCard.test.tsx +796 -0
  105. package/Tests/App/Dashboard/RecommendationToolbar.test.tsx +519 -0
  106. package/Tests/App/Dashboard/RecommendationsList.test.tsx +661 -0
  107. package/Tests/App/Dashboard/UseBulkCreatePingMonitors.test.tsx +899 -0
  108. package/Tests/App/Dashboard/UseBulkSnmpCredentialProfileActions.test.tsx +732 -0
  109. package/Tests/App/SensitiveUrlTokenBootstrap.test.ts +256 -0
  110. package/Tests/App/StatusPage/StatusPageIndexPageRobotsMeta.test.ts +11 -1
  111. package/Tests/App/StatusPage/StatusPageLoginCodeBootstrap.test.ts +9 -4
  112. package/Tests/Models/NetworkSiteHierarchy.test.ts +41 -4
  113. package/Tests/Server/API/EnterpriseLicenseReportUserCount.test.ts +951 -7
  114. package/Tests/Server/API/LlmProviderConnectionTest.test.ts +522 -0
  115. package/Tests/Server/Infrastructure/Postgres/NetworkSnmpCredentialProfilesAndAlertPoliciesMigration.test.ts +854 -0
  116. package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +18 -0
  117. package/Tests/Server/Infrastructure/Postgres/UserNotificationEmailRollupTableMigration.test.ts +535 -0
  118. package/Tests/Server/Services/AIChatPrivacyPin.test.ts +560 -0
  119. package/Tests/Server/Services/BillingRechargeOwnerEmailGuards.test.ts +783 -0
  120. package/Tests/Server/Services/EnterpriseLicenseInstanceServiceDeletionUsage.test.ts +707 -0
  121. package/Tests/Server/Services/EnterpriseLicenseServiceUsageAggregationLock.test.ts +329 -0
  122. package/Tests/Server/Services/MonitorServiceDeleteNetworkDeviceCleanup.test.ts +294 -0
  123. package/Tests/Server/Services/MonitorStatusBridgeProbePath.test.ts +802 -0
  124. package/Tests/Server/Services/MonitorStatusTimelineService.test.ts +72 -2
  125. package/Tests/Server/Services/NetworkAlertPolicyEngineHooks.test.ts +752 -0
  126. package/Tests/Server/Services/NetworkAlertPolicyEngineService.test.ts +1849 -0
  127. package/Tests/Server/Services/NetworkAlertPolicyModel.test.ts +1793 -0
  128. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +47 -4
  129. package/Tests/Server/Services/NetworkDeviceAutoImportRuleService.test.ts +97 -12
  130. package/Tests/Server/Services/NetworkDeviceAutoMonitorLifecycle.test.ts +148 -3
  131. package/Tests/Server/Services/NetworkDeviceMonitorBindingUpdateGuard.test.ts +514 -0
  132. package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +447 -58
  133. package/Tests/Server/Services/NetworkDeviceMonitoringMethodTransition.test.ts +739 -0
  134. package/Tests/Server/Services/NetworkDevicePollingTenancy.test.ts +501 -0
  135. package/Tests/Server/Services/NetworkDeviceSiteDefaultProbe.test.ts +432 -0
  136. package/Tests/Server/Services/NetworkSiteMonitoringDefaults.test.ts +498 -0
  137. package/Tests/Server/Services/NetworkSiteRollupPolicyAndMaintenance.test.ts +337 -55
  138. package/Tests/Server/Services/NetworkSiteService.test.ts +1868 -131
  139. package/Tests/Server/Services/NetworkSiteTypeService.test.ts +1109 -0
  140. package/Tests/Server/Services/NetworkSnmpCredentialProfileModel.test.ts +926 -0
  141. package/Tests/Server/Services/NetworkSnmpCredentialProfileService.test.ts +796 -0
  142. package/Tests/Server/Services/ProjectServiceNetworkSiteTypeDefaults.test.ts +214 -0
  143. package/Tests/Server/Services/StatusPagePrivateUserEmailChangePasswordResetExpiry.test.ts +269 -0
  144. package/Tests/Server/Services/UserEmailChangePasswordResetExpiry.test.ts +472 -0
  145. package/Tests/Server/Services/UserNotificationEmailRollupModels.test.ts +569 -0
  146. package/Tests/Server/Services/UserNotificationEmailRollupSettingModel.test.ts +314 -0
  147. package/Tests/Server/Services/UserNotificationEmailRollupSettingService.test.ts +409 -0
  148. package/Tests/Server/Services/UserNotificationSettingRollupRouting.test.ts +856 -0
  149. package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +102 -1
  150. package/Tests/Server/Types/Database/Permissions/DashboardAccessPermission.test.ts +321 -0
  151. package/Tests/Server/Types/Database/QueryHelperOperators.test.ts +17 -0
  152. package/Tests/Server/Utils/AI/AlertMonitorFilters.test.ts +124 -0
  153. package/Tests/Server/Utils/AI/Insights/InsightTriageRunner.test.ts +208 -0
  154. package/Tests/Server/Utils/AI/LLMServiceToolCalling.test.ts +235 -0
  155. package/Tests/Server/Utils/AI/ObservabilityChatPrompt.test.ts +253 -0
  156. package/Tests/Server/Utils/Captcha.test.ts +422 -0
  157. package/Tests/Server/Utils/DataSource/HttpFetch.test.ts +96 -2
  158. package/Tests/Server/Utils/Database/MigrationFailureLog.test.ts +466 -0
  159. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +1096 -0
  160. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerClaim.test.ts +466 -0
  161. package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +1428 -0
  162. package/Tests/Server/Utils/EmailRollup/EmailRollupTestHarness.ts +752 -0
  163. package/Tests/Server/Utils/EmailRollup/EmailRollupWriter.test.ts +864 -0
  164. package/Tests/Server/Utils/LoggerFaultDemotion.test.ts +346 -0
  165. package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +633 -0
  166. package/Tests/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.test.ts +196 -0
  167. package/Tests/Server/Utils/Monitor/DatabaseMetricWrite.test.ts +367 -0
  168. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +100 -0
  169. package/Tests/Server/Utils/Monitor/MonitorStepResourceIdentity.test.ts +5 -0
  170. package/Tests/Server/Utils/Monitor/NetworkDeviceHydrationUtil.test.ts +512 -31
  171. package/Tests/Server/Utils/Monitor/NetworkDeviceMetricUtil.test.ts +252 -0
  172. package/Tests/Server/Utils/Monitor/NetworkDeviceWalkUtil.test.ts +479 -42
  173. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +351 -5
  174. package/Tests/Server/Utils/NetworkDevice/DeviceHealthAggregation.test.ts +136 -12
  175. package/Tests/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.test.ts +306 -0
  176. package/Tests/Server/Utils/OutboundUserAgent.test.ts +266 -0
  177. package/Tests/Server/Utils/SecurityEvent/ThreatIntel/TaxiiClientUserAgent.test.ts +141 -0
  178. package/Tests/Server/Utils/Telemetry/ErrorClassResolver.test.ts +298 -0
  179. package/Tests/Server/Utils/Telemetry.test.ts +456 -66
  180. package/Tests/Types/Monitor/DatabaseMetricCatalog.test.ts +388 -0
  181. package/Tests/Types/Monitor/MonitorStepDatabaseMonitor.test.ts +243 -0
  182. package/Tests/Types/Monitor/MonitorStepDefaultTelemetryConfig.test.ts +1 -0
  183. package/Tests/Types/Monitor/MonitorType.test.ts +1 -0
  184. package/Tests/Types/Monitor/MonitorTypeKeywords.test.ts +16 -7
  185. package/Tests/Types/Monitor/NetworkDeviceAlertPack.test.ts +60 -0
  186. package/Tests/Types/NetworkDevice/NetworkAlertPolicyScope.test.ts +655 -0
  187. package/Tests/Types/NetworkDevice/NetworkDeviceMonitoringMethod.test.ts +115 -26
  188. package/Tests/Types/NetworkSite/DefaultNetworkSiteType.test.ts +39 -0
  189. package/Tests/Types/NotificationSetting/NotificationEmailRollupPolicy.test.ts +299 -0
  190. package/Tests/Types/Telemetry/ErrorClass.test.ts +483 -0
  191. package/Tests/UI/Components/BasicRadioButtons.test.tsx +336 -0
  192. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +15 -0
  193. package/Tests/UI/Components/ProgressBar.test.tsx +38 -2
  194. package/Tests/UI/Components/SideMenuItem.test.tsx +226 -4
  195. package/Tests/UI/Components/SideMenuSection.test.tsx +310 -0
  196. package/Tests/UI/Components/SideOverSubmitState.test.tsx +335 -0
  197. package/Tests/UI/Utils/PageScrollLock.test.tsx +377 -0
  198. package/Tests/UI/Utils/PermissionGate.test.ts +78 -0
  199. package/Tests/UI/Utils/SensitiveUrlToken.test.ts +140 -0
  200. package/Tests/Utils/EnterpriseLicenseSeats.test.ts +69 -0
  201. package/Tests/Utils/EnterpriseLicenseSync.test.ts +7 -1
  202. package/Tests/Utils/EnterpriseLicenseUsage.test.ts +433 -2
  203. package/Tests/Utils/Monitor/MonitorMetricType.test.ts +280 -1
  204. package/Tests/Utils/Monitor/MonitorSummarySnapshotUtil.test.ts +1 -0
  205. package/Tests/Utils/Monitor/NetworkTopologyUtil.test.ts +38 -0
  206. package/Tests/Utils/NetworkDevice/DeviceHealthStateUtil.test.ts +284 -23
  207. package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +177 -0
  208. package/Tests/Utils/NetworkDevice/MonitoringMethodThreadedClassifiers.test.ts +992 -0
  209. package/Tests/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.test.ts +427 -0
  210. package/Tests/Utils/NetworkDevice/ReachabilityStampConsistency.test.ts +185 -0
  211. package/Tests/Utils/NetworkDevice/SnmpCredentialUtil.test.ts +268 -0
  212. package/Tests/Utils/NetworkDiscovery/AutoImportRuleMatcher.test.ts +55 -5
  213. package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +98 -21
  214. package/Tests/Utils/NetworkDiscovery/DiscoveryImportEligibility.test.ts +98 -20
  215. package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +8 -7
  216. package/Tests/Utils/NetworkDiscovery/PingMonitorBuilderForAddress.test.ts +274 -0
  217. package/Tests/Utils/NetworkSite/SiteStatusRollupUtil.test.ts +230 -51
  218. package/Tests/Utils/NetworkSite/TypeHierarchyUtil.test.ts +209 -0
  219. package/Tests/Utils/Telemetry/CaptureSpanExportGating.test.ts +145 -0
  220. package/Types/AI/ExceptionAIClassification.ts +10 -24
  221. package/Types/Email/EmailTemplateType.ts +2 -0
  222. package/Types/EnterpriseLicense/EnterpriseLicenseInstanceSummary.ts +5 -0
  223. package/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.ts +13 -0
  224. package/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.ts +6 -0
  225. package/Types/Exception/ApiException.ts +10 -0
  226. package/Types/Exception/BadDataException.ts +11 -0
  227. package/Types/Exception/BadRequestException.ts +11 -0
  228. package/Types/Exception/Exception.ts +65 -0
  229. package/Types/Exception/ForbiddenException.ts +10 -0
  230. package/Types/Exception/NotAuthenticatedException.ts +10 -0
  231. package/Types/Exception/NotAuthorizedException.ts +10 -0
  232. package/Types/Exception/NotFoundException.ts +11 -0
  233. package/Types/Exception/PayloadTooLargeException.ts +11 -0
  234. package/Types/Exception/PaymentRequiredException.ts +10 -0
  235. package/Types/Exception/ServiceUnavailableException.ts +10 -0
  236. package/Types/Exception/SsoAuthorizationException.ts +10 -0
  237. package/Types/Exception/TenantNotFoundException.ts +11 -0
  238. package/Types/Exception/TimeoutException.ts +10 -0
  239. package/Types/Exception/TooManyRequestsException.ts +10 -0
  240. package/Types/Exception/UnableToReachServer.ts +10 -0
  241. package/Types/Exception/WebsiteRequestException.ts +10 -0
  242. package/Types/Monitor/CriteriaFilter.ts +60 -2
  243. package/Types/Monitor/DatabaseMetricCatalog.ts +693 -0
  244. package/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.ts +83 -0
  245. package/Types/Monitor/MonitorCriteriaInstance.ts +76 -0
  246. package/Types/Monitor/MonitorMetricType.ts +71 -0
  247. package/Types/Monitor/MonitorStep.ts +76 -0
  248. package/Types/Monitor/MonitorStepDatabaseMonitor.ts +171 -0
  249. package/Types/Monitor/MonitorStepSqlMonitor.ts +2 -20
  250. package/Types/Monitor/MonitorType.ts +47 -1
  251. package/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.ts +22 -1
  252. package/Types/Monitor/SqlConnectionConfig.ts +31 -0
  253. package/Types/NetworkDevice/NetworkAlertPolicyScope.ts +343 -0
  254. package/Types/NetworkDevice/NetworkDeviceMonitoringMethod.ts +42 -22
  255. package/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.ts +30 -0
  256. package/Types/NotificationSetting/NotificationEmailRollupCategory.ts +255 -0
  257. package/Types/NotificationSetting/NotificationEmailRollupPolicy.ts +104 -0
  258. package/Types/Permission.ts +90 -0
  259. package/Types/Probe/ProbeMonitorResponse.ts +16 -0
  260. package/Types/Telemetry/ErrorClass.ts +274 -0
  261. package/Types/Telemetry/UnitOfWork.ts +59 -0
  262. package/UI/Components/CommandPalette/CommandPalette.tsx +2 -8
  263. package/UI/Components/EditionLabel/EditionLabel.tsx +4 -0
  264. package/UI/Components/Modal/Modal.tsx +2 -8
  265. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +44 -0
  266. package/UI/Components/ProgressBar/ProgressBar.tsx +12 -1
  267. package/UI/Components/RadioButtons/BasicRadioButtons.tsx +37 -7
  268. package/UI/Components/SideMenu/SideMenu.tsx +123 -5
  269. package/UI/Components/SideMenu/SideMenuItem.tsx +76 -84
  270. package/UI/Components/SideMenu/SideMenuSection.tsx +55 -23
  271. package/UI/Components/SideOver/SideOver.tsx +21 -20
  272. package/UI/Utils/PageScrollLock.ts +79 -3
  273. package/UI/Utils/PermissionGate.ts +61 -0
  274. package/UI/Utils/SensitiveUrlToken.ts +112 -0
  275. package/UI/Utils/TestLLMProvider.ts +11 -0
  276. package/Utils/EnterpriseLicense/EnterpriseLicenseSeats.ts +16 -0
  277. package/Utils/EnterpriseLicense/EnterpriseLicenseUsage.ts +191 -11
  278. package/Utils/Monitor/MonitorMetricType.ts +153 -1
  279. package/Utils/Monitor/NetworkTopologyUtil.ts +23 -3
  280. package/Utils/NetworkDevice/DeviceHealthStateUtil.ts +65 -14
  281. package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +43 -14
  282. package/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.ts +247 -0
  283. package/Utils/NetworkDevice/SnmpCredentialUtil.ts +68 -0
  284. package/Utils/NetworkDiscovery/AutoImportRuleMatcher.ts +15 -7
  285. package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +52 -17
  286. package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +55 -23
  287. package/Utils/NetworkDiscovery/PingMonitorBuilder.ts +115 -13
  288. package/Utils/NetworkSite/SiteStatusRollupUtil.ts +66 -20
  289. package/Utils/NetworkSite/TypeHierarchyUtil.ts +313 -0
  290. package/build/dist/Models/AnalyticsModels/Span.js +14 -0
  291. package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
  292. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js +59 -0
  293. package/build/dist/Models/DatabaseModels/EnterpriseLicense.js.map +1 -1
  294. package/build/dist/Models/DatabaseModels/Index.js +10 -0
  295. package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
  296. package/build/dist/Models/DatabaseModels/Monitor.js +75 -0
  297. package/build/dist/Models/DatabaseModels/Monitor.js.map +1 -1
  298. package/build/dist/Models/DatabaseModels/NetworkAlertPolicy.js +713 -0
  299. package/build/dist/Models/DatabaseModels/NetworkAlertPolicy.js.map +1 -0
  300. package/build/dist/Models/DatabaseModels/NetworkDevice.js +180 -1
  301. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  302. package/build/dist/Models/DatabaseModels/NetworkSite.js +200 -2
  303. package/build/dist/Models/DatabaseModels/NetworkSite.js.map +1 -1
  304. package/build/dist/Models/DatabaseModels/NetworkSiteType.js +102 -3
  305. package/build/dist/Models/DatabaseModels/NetworkSiteType.js.map +1 -1
  306. package/build/dist/Models/DatabaseModels/NetworkSnmpCredentialProfile.js +878 -0
  307. package/build/dist/Models/DatabaseModels/NetworkSnmpCredentialProfile.js.map +1 -0
  308. package/build/dist/Models/DatabaseModels/TelemetryException.js +123 -0
  309. package/build/dist/Models/DatabaseModels/TelemetryException.js.map +1 -1
  310. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupBatch.js +363 -0
  311. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupBatch.js.map +1 -0
  312. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js +402 -0
  313. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupItem.js.map +1 -0
  314. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupSetting.js +296 -0
  315. package/build/dist/Models/DatabaseModels/UserNotificationEmailRollupSetting.js.map +1 -0
  316. package/build/dist/Server/API/EnterpriseLicenseAPI.js +273 -60
  317. package/build/dist/Server/API/EnterpriseLicenseAPI.js.map +1 -1
  318. package/build/dist/Server/API/LlmProviderAPI.js +108 -13
  319. package/build/dist/Server/API/LlmProviderAPI.js.map +1 -1
  320. package/build/dist/Server/EnvironmentConfig.js +12 -0
  321. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  322. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.js +24 -0
  323. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790700000000-AddNetworkSiteTypeParentHierarchy.js.map +1 -0
  324. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.js +16 -0
  325. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddEnterpriseLicenseUsageProvenance.js.map +1 -0
  326. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.js +111 -0
  327. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1790900000000-AddTelemetryExceptionErrorClass.js.map +1 -0
  328. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.js +77 -0
  329. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791000000000-AddUserNotificationEmailRollup.js.map +1 -0
  330. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.js +50 -0
  331. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791100000000-AddUserNotificationEmailRollupSetting.js.map +1 -0
  332. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.js +83 -0
  333. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791200000000-AddNetworkSnmpCredentialProfilesAndAlertPolicies.js.map +1 -0
  334. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +12 -0
  335. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  336. package/build/dist/Server/Infrastructure/QueueWorker.js +33 -5
  337. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  338. package/build/dist/Server/Middleware/BearerTokenAuthorization.js +12 -0
  339. package/build/dist/Server/Middleware/BearerTokenAuthorization.js.map +1 -1
  340. package/build/dist/Server/Middleware/ProjectAuthorization.js +11 -0
  341. package/build/dist/Server/Middleware/ProjectAuthorization.js.map +1 -1
  342. package/build/dist/Server/Middleware/SCIMAuthorization.js +12 -0
  343. package/build/dist/Server/Middleware/SCIMAuthorization.js.map +1 -1
  344. package/build/dist/Server/Middleware/TelemetryIngest.js +11 -0
  345. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  346. package/build/dist/Server/Services/AIBillingService.js +70 -16
  347. package/build/dist/Server/Services/AIBillingService.js.map +1 -1
  348. package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js +167 -0
  349. package/build/dist/Server/Services/EnterpriseLicenseInstanceService.js.map +1 -1
  350. package/build/dist/Server/Services/EnterpriseLicenseService.js +39 -0
  351. package/build/dist/Server/Services/EnterpriseLicenseService.js.map +1 -1
  352. package/build/dist/Server/Services/Index.js +10 -0
  353. package/build/dist/Server/Services/Index.js.map +1 -1
  354. package/build/dist/Server/Services/MonitorService.js +119 -21
  355. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  356. package/build/dist/Server/Services/MonitorStatusTimelineService.js +127 -8
  357. package/build/dist/Server/Services/MonitorStatusTimelineService.js.map +1 -1
  358. package/build/dist/Server/Services/MonitorTemplateService.js +162 -0
  359. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  360. package/build/dist/Server/Services/NetworkAlertPolicyEngineService.js +1683 -0
  361. package/build/dist/Server/Services/NetworkAlertPolicyEngineService.js.map +1 -0
  362. package/build/dist/Server/Services/NetworkAlertPolicyService.js +732 -0
  363. package/build/dist/Server/Services/NetworkAlertPolicyService.js.map +1 -0
  364. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +10 -9
  365. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  366. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +54 -11
  367. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -1
  368. package/build/dist/Server/Services/NetworkDeviceService.js +841 -70
  369. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  370. package/build/dist/Server/Services/NetworkSiteService.js +1145 -107
  371. package/build/dist/Server/Services/NetworkSiteService.js.map +1 -1
  372. package/build/dist/Server/Services/NetworkSiteTypeService.js +863 -0
  373. package/build/dist/Server/Services/NetworkSiteTypeService.js.map +1 -1
  374. package/build/dist/Server/Services/NetworkSnmpCredentialProfileService.js +217 -0
  375. package/build/dist/Server/Services/NetworkSnmpCredentialProfileService.js.map +1 -0
  376. package/build/dist/Server/Services/NotificationService.js +80 -17
  377. package/build/dist/Server/Services/NotificationService.js.map +1 -1
  378. package/build/dist/Server/Services/ProjectService.js +66 -30
  379. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  380. package/build/dist/Server/Services/StatusPagePrivateUserService.js +92 -2
  381. package/build/dist/Server/Services/StatusPagePrivateUserService.js.map +1 -1
  382. package/build/dist/Server/Services/UserNotificationEmailRollupBatchService.js +76 -0
  383. package/build/dist/Server/Services/UserNotificationEmailRollupBatchService.js.map +1 -0
  384. package/build/dist/Server/Services/UserNotificationEmailRollupItemService.js +77 -0
  385. package/build/dist/Server/Services/UserNotificationEmailRollupItemService.js.map +1 -0
  386. package/build/dist/Server/Services/UserNotificationEmailRollupSettingService.js +111 -0
  387. package/build/dist/Server/Services/UserNotificationEmailRollupSettingService.js.map +1 -0
  388. package/build/dist/Server/Services/UserNotificationSettingService.js +46 -20
  389. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  390. package/build/dist/Server/Services/UserService.js +90 -0
  391. package/build/dist/Server/Services/UserService.js.map +1 -1
  392. package/build/dist/Server/Types/Database/QueryHelper.js +1 -1
  393. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  394. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +14 -3
  395. package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js.map +1 -1
  396. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js +10 -0
  397. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/ExceptionSpikeDetector.js.map +1 -1
  398. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js +10 -0
  399. package/build/dist/Server/Utils/AI/SRE/Insights/Detectors/NewExceptionDetector.js.map +1 -1
  400. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js +54 -1
  401. package/build/dist/Server/Utils/AI/SRE/Insights/InsightTriageRunner.js.map +1 -1
  402. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +18 -4
  403. package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
  404. package/build/dist/Server/Utils/Billing/BillingFailureNoticeThrottle.js +59 -0
  405. package/build/dist/Server/Utils/Billing/BillingFailureNoticeThrottle.js.map +1 -0
  406. package/build/dist/Server/Utils/DataSource/HttpFetch.js +2 -1
  407. package/build/dist/Server/Utils/DataSource/HttpFetch.js.map +1 -1
  408. package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js +91 -0
  409. package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js.map +1 -0
  410. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +691 -0
  411. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -0
  412. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +497 -0
  413. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -0
  414. package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js +257 -0
  415. package/build/dist/Server/Utils/EmailRollup/EmailRollupWriter.js.map +1 -0
  416. package/build/dist/Server/Utils/LLM/LLMService.js +64 -1
  417. package/build/dist/Server/Utils/LLM/LLMService.js.map +1 -1
  418. package/build/dist/Server/Utils/LogRedaction.js +8 -0
  419. package/build/dist/Server/Utils/LogRedaction.js.map +1 -1
  420. package/build/dist/Server/Utils/Logger.js +63 -1
  421. package/build/dist/Server/Utils/Logger.js.map +1 -1
  422. package/build/dist/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.js +141 -0
  423. package/build/dist/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.js.map +1 -0
  424. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +8 -1
  425. package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
  426. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +51 -1
  427. package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
  428. package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +7 -0
  429. package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -1
  430. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +37 -0
  431. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  432. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +158 -6
  433. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  434. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js +33 -0
  435. package/build/dist/Server/Utils/Monitor/MonitorMetricUtil.js.map +1 -1
  436. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +32 -0
  437. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  438. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js +330 -21
  439. package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js.map +1 -1
  440. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +55 -10
  441. package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
  442. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js +178 -49
  443. package/build/dist/Server/Utils/Monitor/NetworkDeviceWalkUtil.js.map +1 -1
  444. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +119 -29
  445. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  446. package/build/dist/Server/Utils/NetworkDevice/DeviceHealthAggregation.js +30 -17
  447. package/build/dist/Server/Utils/NetworkDevice/DeviceHealthAggregation.js.map +1 -1
  448. package/build/dist/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.js +158 -0
  449. package/build/dist/Server/Utils/NetworkSite/NetworkSiteHierarchyLock.js.map +1 -0
  450. package/build/dist/Server/Utils/OutboundUserAgent.js +123 -0
  451. package/build/dist/Server/Utils/OutboundUserAgent.js.map +1 -0
  452. package/build/dist/Server/Utils/StartServer.js +38 -14
  453. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  454. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js +28 -8
  455. package/build/dist/Server/Utils/Telemetry/CaptureSpan.js.map +1 -1
  456. package/build/dist/Server/Utils/Telemetry/ErrorClassResolver.js +95 -0
  457. package/build/dist/Server/Utils/Telemetry/ErrorClassResolver.js.map +1 -0
  458. package/build/dist/Server/Utils/Telemetry/SpanUtil.js +29 -0
  459. package/build/dist/Server/Utils/Telemetry/SpanUtil.js.map +1 -1
  460. package/build/dist/Server/Utils/Telemetry.js +162 -46
  461. package/build/dist/Server/Utils/Telemetry.js.map +1 -1
  462. package/build/dist/Types/AI/ExceptionAIClassification.js +10 -24
  463. package/build/dist/Types/AI/ExceptionAIClassification.js.map +1 -1
  464. package/build/dist/Types/Email/EmailTemplateType.js +1 -0
  465. package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
  466. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.js +2 -0
  467. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUsageSnapshot.js.map +1 -0
  468. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.js +7 -0
  469. package/build/dist/Types/EnterpriseLicense/EnterpriseLicenseUserCountSource.js.map +1 -0
  470. package/build/dist/Types/Exception/ApiException.js +9 -0
  471. package/build/dist/Types/Exception/ApiException.js.map +1 -1
  472. package/build/dist/Types/Exception/BadDataException.js +10 -0
  473. package/build/dist/Types/Exception/BadDataException.js.map +1 -1
  474. package/build/dist/Types/Exception/BadRequestException.js +10 -0
  475. package/build/dist/Types/Exception/BadRequestException.js.map +1 -1
  476. package/build/dist/Types/Exception/Exception.js +56 -0
  477. package/build/dist/Types/Exception/Exception.js.map +1 -1
  478. package/build/dist/Types/Exception/ForbiddenException.js +9 -0
  479. package/build/dist/Types/Exception/ForbiddenException.js.map +1 -1
  480. package/build/dist/Types/Exception/NotAuthenticatedException.js +9 -0
  481. package/build/dist/Types/Exception/NotAuthenticatedException.js.map +1 -1
  482. package/build/dist/Types/Exception/NotAuthorizedException.js +9 -0
  483. package/build/dist/Types/Exception/NotAuthorizedException.js.map +1 -1
  484. package/build/dist/Types/Exception/NotFoundException.js +10 -0
  485. package/build/dist/Types/Exception/NotFoundException.js.map +1 -1
  486. package/build/dist/Types/Exception/PayloadTooLargeException.js +10 -0
  487. package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -1
  488. package/build/dist/Types/Exception/PaymentRequiredException.js +9 -0
  489. package/build/dist/Types/Exception/PaymentRequiredException.js.map +1 -1
  490. package/build/dist/Types/Exception/ServiceUnavailableException.js +9 -0
  491. package/build/dist/Types/Exception/ServiceUnavailableException.js.map +1 -1
  492. package/build/dist/Types/Exception/SsoAuthorizationException.js +9 -0
  493. package/build/dist/Types/Exception/SsoAuthorizationException.js.map +1 -1
  494. package/build/dist/Types/Exception/TenantNotFoundException.js +10 -0
  495. package/build/dist/Types/Exception/TenantNotFoundException.js.map +1 -1
  496. package/build/dist/Types/Exception/TimeoutException.js +9 -0
  497. package/build/dist/Types/Exception/TimeoutException.js.map +1 -1
  498. package/build/dist/Types/Exception/TooManyRequestsException.js +9 -0
  499. package/build/dist/Types/Exception/TooManyRequestsException.js.map +1 -1
  500. package/build/dist/Types/Exception/UnableToReachServer.js +9 -0
  501. package/build/dist/Types/Exception/UnableToReachServer.js.map +1 -1
  502. package/build/dist/Types/Exception/WebsiteRequestException.js +9 -0
  503. package/build/dist/Types/Exception/WebsiteRequestException.js.map +1 -1
  504. package/build/dist/Types/Monitor/CriteriaFilter.js +45 -2
  505. package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
  506. package/build/dist/Types/Monitor/DatabaseMetricCatalog.js +594 -0
  507. package/build/dist/Types/Monitor/DatabaseMetricCatalog.js.map +1 -0
  508. package/build/dist/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.js +21 -0
  509. package/build/dist/Types/Monitor/DatabaseMonitor/DatabaseMonitorResponse.js.map +1 -0
  510. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +70 -0
  511. package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
  512. package/build/dist/Types/Monitor/MonitorMetricType.js +61 -0
  513. package/build/dist/Types/Monitor/MonitorMetricType.js.map +1 -1
  514. package/build/dist/Types/Monitor/MonitorStep.js +54 -0
  515. package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
  516. package/build/dist/Types/Monitor/MonitorStepDatabaseMonitor.js +122 -0
  517. package/build/dist/Types/Monitor/MonitorStepDatabaseMonitor.js.map +1 -0
  518. package/build/dist/Types/Monitor/MonitorStepSqlMonitor.js.map +1 -1
  519. package/build/dist/Types/Monitor/MonitorType.js +45 -1
  520. package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
  521. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js +21 -1
  522. package/build/dist/Types/Monitor/SnmpMonitor/NetworkDeviceAlertPack.js.map +1 -1
  523. package/build/dist/Types/Monitor/SqlConnectionConfig.js +2 -0
  524. package/build/dist/Types/Monitor/SqlConnectionConfig.js.map +1 -0
  525. package/build/dist/Types/NetworkDevice/NetworkAlertPolicyScope.js +172 -0
  526. package/build/dist/Types/NetworkDevice/NetworkAlertPolicyScope.js.map +1 -0
  527. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js +40 -22
  528. package/build/dist/Types/NetworkDevice/NetworkDeviceMonitoringMethod.js.map +1 -1
  529. package/build/dist/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.js +26 -0
  530. package/build/dist/Types/NetworkSite/DefaultNetworkSiteTypeHierarchy.js.map +1 -0
  531. package/build/dist/Types/NotificationSetting/NotificationEmailRollupCategory.js +186 -0
  532. package/build/dist/Types/NotificationSetting/NotificationEmailRollupCategory.js.map +1 -0
  533. package/build/dist/Types/NotificationSetting/NotificationEmailRollupPolicy.js +87 -0
  534. package/build/dist/Types/NotificationSetting/NotificationEmailRollupPolicy.js.map +1 -0
  535. package/build/dist/Types/Permission.js +80 -0
  536. package/build/dist/Types/Permission.js.map +1 -1
  537. package/build/dist/Types/Telemetry/ErrorClass.js +216 -0
  538. package/build/dist/Types/Telemetry/ErrorClass.js.map +1 -0
  539. package/build/dist/Types/Telemetry/UnitOfWork.js +59 -0
  540. package/build/dist/Types/Telemetry/UnitOfWork.js.map +1 -0
  541. package/build/dist/UI/Components/CommandPalette/CommandPalette.js +2 -7
  542. package/build/dist/UI/Components/CommandPalette/CommandPalette.js.map +1 -1
  543. package/build/dist/UI/Components/EditionLabel/EditionLabel.js +3 -0
  544. package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
  545. package/build/dist/UI/Components/Modal/Modal.js +2 -7
  546. package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
  547. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +39 -0
  548. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  549. package/build/dist/UI/Components/ProgressBar/ProgressBar.js +12 -1
  550. package/build/dist/UI/Components/ProgressBar/ProgressBar.js.map +1 -1
  551. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js +6 -6
  552. package/build/dist/UI/Components/RadioButtons/BasicRadioButtons.js.map +1 -1
  553. package/build/dist/UI/Components/SideMenu/SideMenu.js +67 -5
  554. package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
  555. package/build/dist/UI/Components/SideMenu/SideMenuItem.js +49 -53
  556. package/build/dist/UI/Components/SideMenu/SideMenuItem.js.map +1 -1
  557. package/build/dist/UI/Components/SideMenu/SideMenuSection.js +25 -7
  558. package/build/dist/UI/Components/SideMenu/SideMenuSection.js.map +1 -1
  559. package/build/dist/UI/Components/SideOver/SideOver.js +5 -20
  560. package/build/dist/UI/Components/SideOver/SideOver.js.map +1 -1
  561. package/build/dist/UI/Utils/PageScrollLock.js +31 -3
  562. package/build/dist/UI/Utils/PageScrollLock.js.map +1 -1
  563. package/build/dist/UI/Utils/PermissionGate.js +42 -1
  564. package/build/dist/UI/Utils/PermissionGate.js.map +1 -1
  565. package/build/dist/UI/Utils/SensitiveUrlToken.js +97 -0
  566. package/build/dist/UI/Utils/SensitiveUrlToken.js.map +1 -0
  567. package/build/dist/UI/Utils/TestLLMProvider.js +4 -5
  568. package/build/dist/UI/Utils/TestLLMProvider.js.map +1 -1
  569. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js +15 -0
  570. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseSeats.js.map +1 -1
  571. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js +111 -9
  572. package/build/dist/Utils/EnterpriseLicense/EnterpriseLicenseUsage.js.map +1 -1
  573. package/build/dist/Utils/Monitor/MonitorMetricType.js +109 -1
  574. package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
  575. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +8 -3
  576. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  577. package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js +27 -7
  578. package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js.map +1 -1
  579. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +7 -5
  580. package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
  581. package/build/dist/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.js +180 -0
  582. package/build/dist/Utils/NetworkDevice/NetworkAlertPolicyBootstrapUtil.js.map +1 -0
  583. package/build/dist/Utils/NetworkDevice/SnmpCredentialUtil.js +12 -0
  584. package/build/dist/Utils/NetworkDevice/SnmpCredentialUtil.js.map +1 -0
  585. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js +15 -5
  586. package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -1
  587. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +44 -15
  588. package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
  589. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +52 -23
  590. package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -1
  591. package/build/dist/Utils/NetworkDiscovery/PingMonitorBuilder.js +85 -13
  592. package/build/dist/Utils/NetworkDiscovery/PingMonitorBuilder.js.map +1 -1
  593. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js +30 -15
  594. package/build/dist/Utils/NetworkSite/SiteStatusRollupUtil.js.map +1 -1
  595. package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js +191 -0
  596. package/build/dist/Utils/NetworkSite/TypeHierarchyUtil.js.map +1 -0
  597. package/package.json +2 -2
@@ -0,0 +1,864 @@
1
+ import MailService from "../../../../Server/Services/MailService";
2
+ import UserNotificationEmailRollupItemService from "../../../../Server/Services/UserNotificationEmailRollupItemService";
3
+ import UserNotificationEmailRollupSettingService from "../../../../Server/Services/UserNotificationEmailRollupSettingService";
4
+ import EmailRollupWriter, {
5
+ RollupMailOptions,
6
+ SendOrRollupData,
7
+ } from "../../../../Server/Utils/EmailRollup/EmailRollupWriter";
8
+ import {
9
+ BURST_THRESHOLD,
10
+ BURST_WINDOW_MINUTES,
11
+ ROLLUP_SUBJECT_MAX_LENGTH,
12
+ } from "../../../../Server/Utils/EmailRollup/EmailRollupConstants";
13
+ import logger from "../../../../Server/Utils/Logger";
14
+ import CreateBy from "../../../../Server/Types/Database/CreateBy";
15
+ import UserNotificationEmailRollupItem from "../../../../Models/DatabaseModels/UserNotificationEmailRollupItem";
16
+ import Dictionary from "../../../../Types/Dictionary";
17
+ import Email from "../../../../Types/Email";
18
+ import { EmailEnvelope } from "../../../../Types/Email/EmailMessage";
19
+ import EmailTemplateType from "../../../../Types/Email/EmailTemplateType";
20
+ import { JSONObject } from "../../../../Types/JSON";
21
+ import * as NotificationEmailRollupCategory from "../../../../Types/NotificationSetting/NotificationEmailRollupCategory";
22
+ import { NEVER_ROLLED_UP_EVENT_TYPES } from "../../../../Types/NotificationSetting/NotificationEmailRollupPolicy";
23
+ import NotificationSettingEventType from "../../../../Types/NotificationSetting/NotificationSettingEventType";
24
+ import ObjectID from "../../../../Types/ObjectID";
25
+ import PositiveNumber from "../../../../Types/PositiveNumber";
26
+ import { describe, expect, test, beforeEach, afterEach } from "@jest/globals";
27
+ import { FindOperator } from "typeorm";
28
+
29
+ /*
30
+ * EmailRollupWriter is the single place an owner notification email can be
31
+ * held back on its way to MailService. What breaks in production if any of
32
+ * the behaviour below regresses:
33
+ *
34
+ * 1. FAIL OPEN. Every database line in the writer sits inside one try/catch
35
+ * whose catch resets `deferred`. If that ever stops being true, a missing
36
+ * table, a statement timeout or a degraded Postgres stops being "the
37
+ * product behaves exactly as it did last month" and starts being silently
38
+ * dropped incident mail. This is the property the whole feature's safety
39
+ * argument rests on, so four separate failure shapes are pinned here.
40
+ *
41
+ * 2. THE ON-CALL BYPASS. NEVER_ROLLED_UP event types must reach MailService
42
+ * with no database work at all - not "usually", not "unless the counter
43
+ * says otherwise". A regression here delays a page.
44
+ *
45
+ * 3. THE PER-CATEGORY COUNTER. The burst counter is scoped to one
46
+ * RollupCategory. If it ever becomes global, a monitor that flaps every
47
+ * thirty seconds eats the free immediate sends the first "production is
48
+ * down" incident email needs, and the feature makes the exact problem it
49
+ * exists to fix worse.
50
+ *
51
+ * 4. BYTE-IDENTICAL BELOW THRESHOLD. Under the threshold MailService must
52
+ * receive the same envelope and the same correlation-id bag it received
53
+ * before this feature existed. Those ids are what join an email to its
54
+ * incident in every downstream log; losing one breaks attribution with
55
+ * nothing failing loudly.
56
+ *
57
+ * 5. THE SUBJECT IS SANITISED BEFORE STORAGE. Subjects are Handlebars-
58
+ * compiled downstream, and the stored subject is replayed into the rollup
59
+ * email, so an incident title containing `{{` would be evaluated as a
60
+ * template. Over-length subjects must truncate rather than throw, because
61
+ * a throw inside the writer degrades every notification for that project
62
+ * to the fail-open path.
63
+ */
64
+
65
+ const PROJECT_ID: ObjectID = new ObjectID(
66
+ "11111111-1111-4111-8111-111111111111",
67
+ );
68
+ const USER_ID: ObjectID = new ObjectID("22222222-2222-4222-8222-222222222222");
69
+ const INCIDENT_ID: ObjectID = new ObjectID(
70
+ "33333333-3333-4333-8333-333333333333",
71
+ );
72
+ const TO_EMAIL: Email = new Email("owner@example.com");
73
+
74
+ const INCIDENT_EVENT: NotificationSettingEventType =
75
+ NotificationSettingEventType.SEND_INCIDENT_CREATED_OWNER_NOTIFICATION;
76
+ const MONITOR_EVENT: NotificationSettingEventType =
77
+ NotificationSettingEventType.SEND_MONITOR_STATUS_CHANGED_OWNER_NOTIFICATION;
78
+
79
+ const INCIDENT_LINK: string = "https://oneuptime.com/dashboard/incidents/1";
80
+
81
+ /*
82
+ * What the writer hands countBy. Declared here rather than reaching through
83
+ * Query<Model>, because the two things worth asserting - the category scope
84
+ * and the window bound - are exactly the two the generic type erases.
85
+ */
86
+ interface CapturedCountBy {
87
+ query: {
88
+ projectId: ObjectID;
89
+ userId: ObjectID;
90
+ toEmail: Email;
91
+ rollupCategory: NotificationEmailRollupCategory.RollupCategory;
92
+ createdAt: FindOperator<Date>;
93
+ };
94
+ props: {
95
+ isRoot: boolean;
96
+ };
97
+ }
98
+
99
+ /*
100
+ * What the writer hands the preference read. Two ids and nothing else: the
101
+ * preference is per (user, project), and asserting the shape here is what
102
+ * proves the writer asks about the recipient of THIS email rather than about
103
+ * whoever it happened to have in scope.
104
+ */
105
+ interface CapturedPreferenceRead {
106
+ userId: ObjectID;
107
+ projectId: ObjectID;
108
+ }
109
+
110
+ function buildEnvelope(overrides: Partial<EmailEnvelope> = {}): EmailEnvelope {
111
+ return {
112
+ subject: "Incident created: Checkout is down",
113
+ templateType: EmailTemplateType.BlankTemplate,
114
+ vars: {
115
+ incidentViewLink: INCIDENT_LINK,
116
+ },
117
+ ...overrides,
118
+ } as EmailEnvelope;
119
+ }
120
+
121
+ function buildMailOptions(): RollupMailOptions {
122
+ return {
123
+ projectId: PROJECT_ID,
124
+ incidentId: INCIDENT_ID,
125
+ userId: USER_ID,
126
+ };
127
+ }
128
+
129
+ function sendData(overrides: Partial<SendOrRollupData> = {}): SendOrRollupData {
130
+ return {
131
+ projectId: PROJECT_ID,
132
+ userId: USER_ID,
133
+ toEmail: TO_EMAIL,
134
+ eventType: INCIDENT_EVENT,
135
+ emailEnvelope: buildEnvelope(),
136
+ mailOptions: buildMailOptions(),
137
+ ...overrides,
138
+ };
139
+ }
140
+
141
+ describe("EmailRollupWriter.sendOrRollup", () => {
142
+ let sendMail: jest.SpyInstance;
143
+ let countRecent: jest.SpyInstance;
144
+ let createItem: jest.SpyInstance;
145
+ let loggerError: jest.SpyInstance;
146
+ let rollupEnabled: jest.SpyInstance;
147
+
148
+ beforeEach(() => {
149
+ loggerError = jest.spyOn(logger, "error").mockImplementation((): void => {
150
+ return undefined;
151
+ });
152
+
153
+ sendMail = jest
154
+ .spyOn(MailService, "sendMail")
155
+ .mockResolvedValue(undefined as never);
156
+
157
+ countRecent = jest
158
+ .spyOn(UserNotificationEmailRollupItemService, "countBy")
159
+ .mockResolvedValue(new PositiveNumber(0) as never);
160
+
161
+ createItem = jest
162
+ .spyOn(UserNotificationEmailRollupItemService, "create")
163
+ .mockResolvedValue(new UserNotificationEmailRollupItem() as never);
164
+
165
+ /*
166
+ * The per-user escape hatch, stubbed to its default for every test in the
167
+ * file. Absent a row the real service answers `true`, so this is the
168
+ * behaviour every existing test above was written against; stubbing it
169
+ * keeps them asserting the burst decision rather than accidentally
170
+ * asserting what an unreachable database does to the preference read.
171
+ */
172
+ rollupEnabled = jest
173
+ .spyOn(
174
+ UserNotificationEmailRollupSettingService,
175
+ "isRollupEnabledForUser",
176
+ )
177
+ .mockResolvedValue(true as never);
178
+ });
179
+
180
+ afterEach(() => {
181
+ jest.restoreAllMocks();
182
+ });
183
+
184
+ function recentCount(count: number): void {
185
+ countRecent.mockResolvedValue(new PositiveNumber(count) as never);
186
+ }
187
+
188
+ function writtenItem(index: number = 0): UserNotificationEmailRollupItem {
189
+ const arg: CreateBy<UserNotificationEmailRollupItem> = createItem.mock
190
+ .calls[index]?.[0] as CreateBy<UserNotificationEmailRollupItem>;
191
+ return arg.data;
192
+ }
193
+
194
+ function capturedCountBy(index: number = 0): CapturedCountBy {
195
+ return countRecent.mock.calls[index]?.[0] as CapturedCountBy;
196
+ }
197
+
198
+ /*
199
+ * ----------------------------------------------------------------------- *
200
+ * (A) The decision table.
201
+ * -----------------------------------------------------------------------
202
+ */
203
+
204
+ describe("the burst decision", () => {
205
+ test("below the threshold it sends immediately with the envelope and options unchanged", async () => {
206
+ recentCount(BURST_THRESHOLD - 1);
207
+ const data: SendOrRollupData = sendData();
208
+
209
+ await EmailRollupWriter.sendOrRollup(data);
210
+
211
+ expect(sendMail).toHaveBeenCalledTimes(1);
212
+ expect(sendMail.mock.calls[0]?.[0]).toEqual({
213
+ subject: "Incident created: Checkout is down",
214
+ templateType: EmailTemplateType.BlankTemplate,
215
+ vars: {
216
+ incidentViewLink: INCIDENT_LINK,
217
+ },
218
+ toEmail: TO_EMAIL,
219
+ });
220
+ /*
221
+ * Reference identity, not deep equality: the correlation-id bag is
222
+ * passed through verbatim, and a writer that rebuilt it would be free to
223
+ * quietly drop a field.
224
+ */
225
+ expect(sendMail.mock.calls[0]?.[1]).toBe(data.mailOptions);
226
+ });
227
+
228
+ test("below the threshold exactly one row is written, with sentAt set", async () => {
229
+ recentCount(BURST_THRESHOLD - 1);
230
+
231
+ await EmailRollupWriter.sendOrRollup(sendData());
232
+
233
+ expect(createItem).toHaveBeenCalledTimes(1);
234
+
235
+ const item: UserNotificationEmailRollupItem = writtenItem();
236
+ expect(item.sentAt).toBeInstanceOf(Date);
237
+ expect(item.projectId?.toString()).toBe(PROJECT_ID.toString());
238
+ expect(item.userId?.toString()).toBe(USER_ID.toString());
239
+ expect(item.toEmail?.toString()).toBe(TO_EMAIL.toString());
240
+ expect(item.eventType).toBe(INCIDENT_EVENT);
241
+ expect(item.rollupCategory).toBe(
242
+ NotificationEmailRollupCategory.RollupCategory.Incidents,
243
+ );
244
+ expect(item.subject).toBe("Incident created: Checkout is down");
245
+ expect(item.viewLink).toBe(INCIDENT_LINK);
246
+ expect(item.rollupBatchId).toBeUndefined();
247
+ });
248
+
249
+ test("at the threshold it defers: no mail, one pending row with sentAt undefined", async () => {
250
+ recentCount(BURST_THRESHOLD);
251
+
252
+ await EmailRollupWriter.sendOrRollup(sendData());
253
+
254
+ expect(sendMail).not.toHaveBeenCalled();
255
+ expect(createItem).toHaveBeenCalledTimes(1);
256
+ expect(writtenItem().sentAt).toBeUndefined();
257
+ });
258
+
259
+ test("above the threshold it defers too", async () => {
260
+ recentCount(BURST_THRESHOLD + 40);
261
+
262
+ await EmailRollupWriter.sendOrRollup(sendData());
263
+
264
+ expect(sendMail).not.toHaveBeenCalled();
265
+ expect(createItem).toHaveBeenCalledTimes(1);
266
+ expect(writtenItem().sentAt).toBeUndefined();
267
+ });
268
+
269
+ test("the ledger row is always written as root, since the service denies everything else", async () => {
270
+ recentCount(0);
271
+
272
+ await EmailRollupWriter.sendOrRollup(sendData());
273
+
274
+ const arg: CreateBy<UserNotificationEmailRollupItem> = createItem.mock
275
+ .calls[0]?.[0] as CreateBy<UserNotificationEmailRollupItem>;
276
+ expect(arg.props.isRoot).toBe(true);
277
+ expect(capturedCountBy().props.isRoot).toBe(true);
278
+ });
279
+ });
280
+
281
+ /*
282
+ * ----------------------------------------------------------------------- *
283
+ * (B) The counter is scoped per category.
284
+ * -----------------------------------------------------------------------
285
+ */
286
+
287
+ describe("the per-category counter scope", () => {
288
+ test("ten recent Monitors rows do not defer an incident-created notification", async () => {
289
+ /*
290
+ * The regression guard for the whole feature: the noisiest resource must
291
+ * never be able to delay the most important one. A flapping monitor
292
+ * produces a Monitors-category flood; the first incident email that
293
+ * follows it must still go out immediately.
294
+ */
295
+ countRecent.mockImplementation(
296
+ (params: CapturedCountBy): Promise<PositiveNumber> => {
297
+ return Promise.resolve(
298
+ new PositiveNumber(
299
+ params.query.rollupCategory ===
300
+ NotificationEmailRollupCategory.RollupCategory.Monitors
301
+ ? 10
302
+ : 0,
303
+ ),
304
+ );
305
+ },
306
+ );
307
+
308
+ await EmailRollupWriter.sendOrRollup(sendData());
309
+
310
+ expect(sendMail).toHaveBeenCalledTimes(1);
311
+ expect(writtenItem().sentAt).toBeInstanceOf(Date);
312
+
313
+ /*
314
+ * And the same flood does defer the next monitor notification, which is
315
+ * what proves the mock above was actually reached rather than the
316
+ * incident having been immediate for some unrelated reason.
317
+ */
318
+ sendMail.mockClear();
319
+ createItem.mockClear();
320
+
321
+ await EmailRollupWriter.sendOrRollup(
322
+ sendData({ eventType: MONITOR_EVENT }),
323
+ );
324
+
325
+ expect(sendMail).not.toHaveBeenCalled();
326
+ expect(writtenItem().sentAt).toBeUndefined();
327
+ });
328
+
329
+ test("the count is keyed on project, user, address and category", async () => {
330
+ await EmailRollupWriter.sendOrRollup(sendData());
331
+
332
+ const captured: CapturedCountBy = capturedCountBy();
333
+ expect(captured.query.projectId.toString()).toBe(PROJECT_ID.toString());
334
+ expect(captured.query.userId.toString()).toBe(USER_ID.toString());
335
+ expect(captured.query.toEmail.toString()).toBe(TO_EMAIL.toString());
336
+ expect(captured.query.rollupCategory).toBe(
337
+ NotificationEmailRollupCategory.RollupCategory.Incidents,
338
+ );
339
+ });
340
+
341
+ test("the counter counts rows NEWER than now minus BURST_WINDOW_MINUTES, not older", async () => {
342
+ /*
343
+ * Both halves of this matter, and only one of them is a number.
344
+ *
345
+ * The BOUND is now minus ten minutes. The DIRECTION is what makes the
346
+ * bound mean anything: QueryHelper.greaterThan and QueryHelper.lessThan
347
+ * are the same call shape, take the same Date and produce the same
348
+ * objectLiteralParameters, so a test that reads only the bound stays
349
+ * green through the one-token swap that inverts the entire feature -
350
+ * `lessThan` would count everything OLDER than the window, so a genuine
351
+ * burst would never be detected and a recipient who had been quiet for
352
+ * an hour would be deferred forever.
353
+ *
354
+ * The only part of the returned FindOperator that distinguishes them is
355
+ * the SQL fragment its generator builds, so that is what is asserted.
356
+ */
357
+ const before: number = Date.now();
358
+
359
+ await EmailRollupWriter.sendOrRollup(sendData());
360
+
361
+ const after: number = Date.now();
362
+
363
+ const createdAt: FindOperator<Date> = capturedCountBy().query.createdAt;
364
+
365
+ const rawParameters: Record<string, unknown> =
366
+ (createdAt.objectLiteralParameters ?? {}) as Record<string, unknown>;
367
+ const parameterNames: Array<string> = Object.keys(rawParameters);
368
+ expect(parameterNames).toHaveLength(1);
369
+
370
+ const parameterName: string = parameterNames[0] ?? "";
371
+ const windowStart: Date = rawParameters[parameterName] as Date;
372
+ expect(windowStart).toBeInstanceOf(Date);
373
+
374
+ const offsetMs: number = BURST_WINDOW_MINUTES * 60 * 1000;
375
+ expect(windowStart.getTime()).toBeGreaterThanOrEqual(before - offsetMs);
376
+ expect(windowStart.getTime()).toBeLessThanOrEqual(after - offsetMs);
377
+
378
+ const buildSql: ((alias: string) => string) | undefined =
379
+ createdAt.getSql;
380
+ expect(typeof buildSql).toBe("function");
381
+
382
+ const sql: string = (buildSql as (alias: string) => string)(
383
+ "createdAtColumn",
384
+ );
385
+
386
+ /*
387
+ * Anchored, and the parameter name is the one the bound above was read
388
+ * from, so this cannot pass on a fragment that compares some other
389
+ * column or some other value. `>=` is allowed because a half-open window
390
+ * is the same window; `<` in either position is not, and neither is the
391
+ * `or IS NULL` variant, which would make every never-created row count.
392
+ */
393
+ expect(sql).toMatch(
394
+ new RegExp(`^\\(createdAtColumn >=? :${parameterName}\\)$`),
395
+ );
396
+ });
397
+ });
398
+
399
+ /*
400
+ * ----------------------------------------------------------------------- *
401
+ * (C) The structural bypasses.
402
+ * -----------------------------------------------------------------------
403
+ */
404
+
405
+ describe("the structural bypasses", () => {
406
+ test("every never-rolled-up event type sends immediately, writes no row and never counts", async () => {
407
+ /*
408
+ * Armed with a flood that would defer anything eligible, precisely so a
409
+ * regression that moved the eligibility check below the counter would
410
+ * fail here rather than pass by luck.
411
+ */
412
+ recentCount(100);
413
+
414
+ for (const eventType of Array.from(NEVER_ROLLED_UP_EVENT_TYPES)) {
415
+ sendMail.mockClear();
416
+ countRecent.mockClear();
417
+ createItem.mockClear();
418
+
419
+ await EmailRollupWriter.sendOrRollup(sendData({ eventType }));
420
+
421
+ expect(sendMail).toHaveBeenCalledTimes(1);
422
+ expect(countRecent).not.toHaveBeenCalled();
423
+ expect(createItem).not.toHaveBeenCalled();
424
+ }
425
+ });
426
+
427
+ test("forceImmediate sends immediately, writes no row and counts nothing", async () => {
428
+ recentCount(100);
429
+
430
+ await EmailRollupWriter.sendOrRollup(sendData({ forceImmediate: true }));
431
+
432
+ expect(sendMail).toHaveBeenCalledTimes(1);
433
+ expect(countRecent).not.toHaveBeenCalled();
434
+ expect(createItem).not.toHaveBeenCalled();
435
+ });
436
+
437
+ test("forceImmediate false is not a bypass and still goes through the counter", async () => {
438
+ recentCount(BURST_THRESHOLD);
439
+
440
+ await EmailRollupWriter.sendOrRollup(sendData({ forceImmediate: false }));
441
+
442
+ expect(countRecent).toHaveBeenCalledTimes(1);
443
+ expect(sendMail).not.toHaveBeenCalled();
444
+ });
445
+ });
446
+
447
+ /*
448
+ * ----------------------------------------------------------------------- *
449
+ * (D) Fail open.
450
+ * -----------------------------------------------------------------------
451
+ */
452
+
453
+ describe("fail open", () => {
454
+ test("a countBy that throws still sends the email, logging once", async () => {
455
+ countRecent.mockRejectedValue(
456
+ new Error('relation "UserNotificationEmailRollupItem" does not exist'),
457
+ );
458
+
459
+ await expect(
460
+ EmailRollupWriter.sendOrRollup(sendData()),
461
+ ).resolves.toBeUndefined();
462
+
463
+ expect(sendMail).toHaveBeenCalledTimes(1);
464
+ expect(createItem).not.toHaveBeenCalled();
465
+ expect(loggerError).toHaveBeenCalledTimes(1);
466
+ });
467
+
468
+ test("a create that throws still sends the email, logging once", async () => {
469
+ recentCount(0);
470
+ createItem.mockRejectedValue(new Error("insert timed out"));
471
+
472
+ await expect(
473
+ EmailRollupWriter.sendOrRollup(sendData()),
474
+ ).resolves.toBeUndefined();
475
+
476
+ expect(sendMail).toHaveBeenCalledTimes(1);
477
+ expect(loggerError).toHaveBeenCalledTimes(1);
478
+ });
479
+
480
+ test("both throwing still sends the email, logging once", async () => {
481
+ countRecent.mockRejectedValue(new Error("postgres is unreachable"));
482
+ createItem.mockRejectedValue(new Error("postgres is unreachable"));
483
+
484
+ await expect(
485
+ EmailRollupWriter.sendOrRollup(sendData()),
486
+ ).resolves.toBeUndefined();
487
+
488
+ expect(sendMail).toHaveBeenCalledTimes(1);
489
+ expect(loggerError).toHaveBeenCalledTimes(1);
490
+ });
491
+
492
+ test("a category lookup that throws still sends the email, logging once", async () => {
493
+ /*
494
+ * Stands in for every way the classification layer can go wrong at
495
+ * runtime - a value the Record has no entry for, an import cycle that
496
+ * leaves the map undefined at first call. The answer has to be the same
497
+ * as for a dead database: send it the old way.
498
+ */
499
+ jest
500
+ .spyOn(NotificationEmailRollupCategory, "getRollupCategory")
501
+ .mockImplementation((): never => {
502
+ throw new Error("unclassifiable event type");
503
+ });
504
+
505
+ await expect(
506
+ EmailRollupWriter.sendOrRollup(sendData()),
507
+ ).resolves.toBeUndefined();
508
+
509
+ expect(sendMail).toHaveBeenCalledTimes(1);
510
+ expect(countRecent).not.toHaveBeenCalled();
511
+ expect(createItem).not.toHaveBeenCalled();
512
+ expect(loggerError).toHaveBeenCalledTimes(1);
513
+ });
514
+
515
+ test("a deferred notification is NOT sent twice when the write succeeds", async () => {
516
+ /*
517
+ * The mirror image of fail-open, and the reason the catch resets a flag
518
+ * rather than returning early: if the catch were ever widened to cover
519
+ * the send decision itself, a successful deferral could still fall
520
+ * through to sendNow and the recipient would get both the immediate mail
521
+ * and the rollup line.
522
+ */
523
+ recentCount(BURST_THRESHOLD);
524
+
525
+ await EmailRollupWriter.sendOrRollup(sendData());
526
+
527
+ expect(sendMail).not.toHaveBeenCalled();
528
+ expect(loggerError).not.toHaveBeenCalled();
529
+ });
530
+ });
531
+
532
+ /*
533
+ * ----------------------------------------------------------------------- *
534
+ * (E) The stored subject.
535
+ * -----------------------------------------------------------------------
536
+ */
537
+
538
+ describe("the stored subject", () => {
539
+ test("a 5,000 character subject is truncated to the column length rather than throwing", async () => {
540
+ const longSubject: string = "x".repeat(5000);
541
+
542
+ await expect(
543
+ EmailRollupWriter.sendOrRollup(
544
+ sendData({ emailEnvelope: buildEnvelope({ subject: longSubject }) }),
545
+ ),
546
+ ).resolves.toBeUndefined();
547
+
548
+ expect(loggerError).not.toHaveBeenCalled();
549
+ expect(writtenItem().subject).toHaveLength(ROLLUP_SUBJECT_MAX_LENGTH);
550
+ });
551
+
552
+ test("Handlebars braces are stripped before storage", async () => {
553
+ await EmailRollupWriter.sendOrRollup(
554
+ sendData({
555
+ emailEnvelope: buildEnvelope({
556
+ subject: "{{projectName}} is down }}",
557
+ }),
558
+ }),
559
+ );
560
+
561
+ expect(writtenItem().subject).toBe("projectName is down ");
562
+ expect(writtenItem().subject).not.toContain("{{");
563
+ expect(writtenItem().subject).not.toContain("}}");
564
+ });
565
+
566
+ test("the outgoing email keeps the producer's original subject", async () => {
567
+ /*
568
+ * Stripping and truncation are storage concerns only. The immediate send
569
+ * below the threshold must be byte-identical to what shipped before.
570
+ */
571
+ const subject: string = "{{projectName}} is down";
572
+
573
+ await EmailRollupWriter.sendOrRollup(
574
+ sendData({ emailEnvelope: buildEnvelope({ subject }) }),
575
+ );
576
+
577
+ expect((sendMail.mock.calls[0]?.[0] as EmailEnvelope).subject).toBe(
578
+ subject,
579
+ );
580
+ });
581
+
582
+ test("an empty subject is stored as an empty string rather than failing the insert", async () => {
583
+ await EmailRollupWriter.sendOrRollup(
584
+ sendData({ emailEnvelope: buildEnvelope({ subject: "" }) }),
585
+ );
586
+
587
+ expect(loggerError).not.toHaveBeenCalled();
588
+ expect(writtenItem().subject).toBe("");
589
+ });
590
+ });
591
+
592
+ /*
593
+ * ----------------------------------------------------------------------- *
594
+ * (F) extractViewLink - pure, no mocks needed.
595
+ * -----------------------------------------------------------------------
596
+ */
597
+
598
+ describe("extractViewLink", () => {
599
+ function vars(
600
+ value: Dictionary<string | JSONObject>,
601
+ ): Dictionary<string | JSONObject> {
602
+ return value;
603
+ }
604
+
605
+ test("the first known variable in list order wins", () => {
606
+ /*
607
+ * Insertion order here is deliberately the opposite of preference order,
608
+ * so a fallback that merely walked Object.keys would pick the monitor
609
+ * link and fail.
610
+ */
611
+ expect(
612
+ EmailRollupWriter.extractViewLink(
613
+ vars({
614
+ monitorViewLink: "https://oneuptime.com/dashboard/monitors/1",
615
+ incidentViewLink: INCIDENT_LINK,
616
+ }),
617
+ ),
618
+ ).toBe(INCIDENT_LINK);
619
+ });
620
+
621
+ test("the plural probe and AI agent variables resolve, as well as their singular twins", () => {
622
+ expect(
623
+ EmailRollupWriter.extractViewLink(
624
+ vars({ viewProbesLink: "https://oneuptime.com/dashboard/probes" }),
625
+ ),
626
+ ).toBe("https://oneuptime.com/dashboard/probes");
627
+
628
+ expect(
629
+ EmailRollupWriter.extractViewLink(
630
+ vars({ viewProbeLink: "https://oneuptime.com/dashboard/probes/1" }),
631
+ ),
632
+ ).toBe("https://oneuptime.com/dashboard/probes/1");
633
+
634
+ expect(
635
+ EmailRollupWriter.extractViewLink(
636
+ vars({
637
+ viewAIAgentsLink: "https://oneuptime.com/dashboard/ai-agents",
638
+ }),
639
+ ),
640
+ ).toBe("https://oneuptime.com/dashboard/ai-agents");
641
+
642
+ expect(
643
+ EmailRollupWriter.extractViewLink(
644
+ vars({
645
+ viewAIAgentLink: "https://oneuptime.com/dashboard/ai-agents/1",
646
+ }),
647
+ ),
648
+ ).toBe("https://oneuptime.com/dashboard/ai-agents/1");
649
+ });
650
+
651
+ test("deviceViewLink resolves", () => {
652
+ expect(
653
+ EmailRollupWriter.extractViewLink(
654
+ vars({ deviceViewLink: "https://oneuptime.com/dashboard/devices/1" }),
655
+ ),
656
+ ).toBe("https://oneuptime.com/dashboard/devices/1");
657
+ });
658
+
659
+ test("an unknown *Link variable with an http value resolves through the fallback", () => {
660
+ /*
661
+ * This is what buys the feature a clickable line for a producer nobody
662
+ * has written yet, with no list here to keep in sync.
663
+ */
664
+ expect(
665
+ EmailRollupWriter.extractViewLink(
666
+ vars({
667
+ somethingBrandNewViewLink: "https://oneuptime.com/dashboard/new/1",
668
+ }),
669
+ ),
670
+ ).toBe("https://oneuptime.com/dashboard/new/1");
671
+ });
672
+
673
+ test("a non-string or relative value is ignored", () => {
674
+ expect(
675
+ EmailRollupWriter.extractViewLink(
676
+ vars({
677
+ incidentViewLink: 42 as unknown as string,
678
+ alertViewLink: { url: "https://oneuptime.com" } as JSONObject,
679
+ monitorViewLink: "/dashboard/monitors/1",
680
+ }),
681
+ ),
682
+ ).toBeUndefined();
683
+ });
684
+
685
+ test("no link variables at all returns undefined", () => {
686
+ expect(
687
+ EmailRollupWriter.extractViewLink(vars({ projectName: "Acme" })),
688
+ ).toBeUndefined();
689
+ expect(EmailRollupWriter.extractViewLink(undefined)).toBeUndefined();
690
+ });
691
+
692
+ test("an envelope with no usable link still writes the row", async () => {
693
+ await EmailRollupWriter.sendOrRollup(
694
+ sendData({
695
+ emailEnvelope: buildEnvelope({ vars: { projectName: "Acme" } }),
696
+ }),
697
+ );
698
+
699
+ expect(createItem).toHaveBeenCalledTimes(1);
700
+ expect(writtenItem().viewLink).toBeUndefined();
701
+ expect(sendMail).toHaveBeenCalledTimes(1);
702
+ });
703
+ });
704
+
705
+ /*
706
+ * ----------------------------------------------------------------------- *
707
+ * (G) The per-user escape hatch.
708
+ * -----------------------------------------------------------------------
709
+ */
710
+
711
+ describe("the per-user escape hatch", () => {
712
+ function capturedPreferenceRead(index: number = 0): CapturedPreferenceRead {
713
+ return rollupEnabled.mock.calls[index]?.[0] as CapturedPreferenceRead;
714
+ }
715
+
716
+ test("below the threshold the preference is never read", async () => {
717
+ /*
718
+ * The reason the read sits inside the `if (deferred)` and not above it.
719
+ * Under the threshold the outcome is an immediate send whatever the
720
+ * preference says, so a read here would be one extra indexed query per
721
+ * owner notification per address, on the path that every project takes
722
+ * every day, that could never change the answer. The feature's
723
+ * no-op-below-the-threshold promise has to hold in queries, not only in
724
+ * emails.
725
+ */
726
+ recentCount(BURST_THRESHOLD - 1);
727
+
728
+ await EmailRollupWriter.sendOrRollup(sendData());
729
+
730
+ expect(rollupEnabled).not.toHaveBeenCalled();
731
+ expect(sendMail).toHaveBeenCalledTimes(1);
732
+ });
733
+
734
+ test("at the threshold with rollup on, the email defers exactly as it did before the toggle existed", async () => {
735
+ recentCount(BURST_THRESHOLD);
736
+ rollupEnabled.mockResolvedValue(true as never);
737
+
738
+ await EmailRollupWriter.sendOrRollup(sendData());
739
+
740
+ expect(rollupEnabled).toHaveBeenCalledTimes(1);
741
+ expect(sendMail).not.toHaveBeenCalled();
742
+ expect(createItem).toHaveBeenCalledTimes(1);
743
+ expect(writtenItem().sentAt).toBeUndefined();
744
+ expect(loggerError).not.toHaveBeenCalled();
745
+ });
746
+
747
+ test("at the threshold with rollup off, the email is sent immediately with the envelope and options unchanged", async () => {
748
+ /*
749
+ * Opting out has to reproduce the pre-rollup product exactly, envelope
750
+ * and correlation-id bag included - not "an email that says roughly the
751
+ * same thing". The assertions here are deliberately the same two the
752
+ * below-threshold test makes.
753
+ */
754
+ recentCount(BURST_THRESHOLD + 10);
755
+ rollupEnabled.mockResolvedValue(false as never);
756
+
757
+ const data: SendOrRollupData = sendData();
758
+
759
+ await EmailRollupWriter.sendOrRollup(data);
760
+
761
+ expect(sendMail).toHaveBeenCalledTimes(1);
762
+ expect(sendMail.mock.calls[0]?.[0]).toEqual({
763
+ subject: "Incident created: Checkout is down",
764
+ templateType: EmailTemplateType.BlankTemplate,
765
+ vars: {
766
+ incidentViewLink: INCIDENT_LINK,
767
+ },
768
+ toEmail: TO_EMAIL,
769
+ });
770
+ expect(sendMail.mock.calls[0]?.[1]).toBe(data.mailOptions);
771
+ expect(loggerError).not.toHaveBeenCalled();
772
+ });
773
+
774
+ test("an opted-out send STILL writes its ledger row, with sentAt set", async () => {
775
+ /*
776
+ * The one most likely to be lost in a later refactor, because returning
777
+ * early on the opt-out reads like the tidy version of the same thing.
778
+ *
779
+ * It is not. The row is what the burst counter counts, and the counter is
780
+ * scoped to (project, user, address, category) - so an opted-out person
781
+ * whose sends wrote no row would make the window under-count, and their
782
+ * traffic would become invisible to the volume ledger this table exists
783
+ * to be. sentAt must be SET, because the row describes an email that has
784
+ * already gone out; a NULL sentAt is what "pending" means to the flush
785
+ * sweep, and the sweep would pick this row up and send the notification a
786
+ * second time inside a rollup.
787
+ */
788
+ recentCount(BURST_THRESHOLD);
789
+ rollupEnabled.mockResolvedValue(false as never);
790
+
791
+ await EmailRollupWriter.sendOrRollup(sendData());
792
+
793
+ expect(createItem).toHaveBeenCalledTimes(1);
794
+
795
+ const item: UserNotificationEmailRollupItem = writtenItem();
796
+ expect(item.sentAt).toBeInstanceOf(Date);
797
+ expect(item.projectId?.toString()).toBe(PROJECT_ID.toString());
798
+ expect(item.userId?.toString()).toBe(USER_ID.toString());
799
+ expect(item.toEmail?.toString()).toBe(TO_EMAIL.toString());
800
+ expect(item.rollupCategory).toBe(
801
+ NotificationEmailRollupCategory.RollupCategory.Incidents,
802
+ );
803
+ });
804
+
805
+ test("the preference is read for the recipient of this email", async () => {
806
+ recentCount(BURST_THRESHOLD);
807
+
808
+ await EmailRollupWriter.sendOrRollup(sendData());
809
+
810
+ const captured: CapturedPreferenceRead = capturedPreferenceRead();
811
+ expect(captured.userId.toString()).toBe(USER_ID.toString());
812
+ expect(captured.projectId.toString()).toBe(PROJECT_ID.toString());
813
+ });
814
+
815
+ test("a preference read that throws sends the email immediately and logs, rather than deferring", async () => {
816
+ /*
817
+ * Fail open, again: the failure mode of the escape hatch is the escape
818
+ * hatch. A read that threw and left `deferred` true would hold back mail
819
+ * on the strength of a preference nobody managed to look up.
820
+ */
821
+ recentCount(BURST_THRESHOLD);
822
+ rollupEnabled.mockRejectedValue(
823
+ new Error(
824
+ 'relation "UserNotificationEmailRollupSetting" does not exist',
825
+ ),
826
+ );
827
+
828
+ await expect(
829
+ EmailRollupWriter.sendOrRollup(sendData()),
830
+ ).resolves.toBeUndefined();
831
+
832
+ expect(sendMail).toHaveBeenCalledTimes(1);
833
+ expect(loggerError).toHaveBeenCalledTimes(1);
834
+ });
835
+
836
+ test("forceImmediate short-circuits before the preference is read", async () => {
837
+ /*
838
+ * The structural bypasses have to cost zero extra queries. A preference
839
+ * read reached from either of them would put a database round trip on
840
+ * the paging-adjacent path the bypasses exist to keep clear.
841
+ */
842
+ recentCount(100);
843
+
844
+ await EmailRollupWriter.sendOrRollup(sendData({ forceImmediate: true }));
845
+
846
+ expect(rollupEnabled).not.toHaveBeenCalled();
847
+ expect(sendMail).toHaveBeenCalledTimes(1);
848
+ });
849
+
850
+ test("a never-rolled-up event type short-circuits before the preference is read", async () => {
851
+ recentCount(100);
852
+
853
+ for (const eventType of Array.from(NEVER_ROLLED_UP_EVENT_TYPES)) {
854
+ rollupEnabled.mockClear();
855
+ sendMail.mockClear();
856
+
857
+ await EmailRollupWriter.sendOrRollup(sendData({ eventType }));
858
+
859
+ expect(rollupEnabled).not.toHaveBeenCalled();
860
+ expect(sendMail).toHaveBeenCalledTimes(1);
861
+ }
862
+ });
863
+ });
864
+ });