@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,796 @@
1
+ import NetworkSnmpCredentialProfileService, {
2
+ Service as NetworkSnmpCredentialProfileServiceType,
3
+ } from "../../../Server/Services/NetworkSnmpCredentialProfileService";
4
+ import NetworkDeviceService from "../../../Server/Services/NetworkDeviceService";
5
+ import NetworkSiteService from "../../../Server/Services/NetworkSiteService";
6
+ import CreateBy from "../../../Server/Types/Database/CreateBy";
7
+ import DeleteBy from "../../../Server/Types/Database/DeleteBy";
8
+ import UpdateBy from "../../../Server/Types/Database/UpdateBy";
9
+ import {
10
+ OnCreate,
11
+ OnDelete,
12
+ OnUpdate,
13
+ } from "../../../Server/Types/Database/Hooks";
14
+ import NetworkSnmpCredentialProfile from "../../../Models/DatabaseModels/NetworkSnmpCredentialProfile";
15
+ import DatabaseCommonInteractionProps from "../../../Types/BaseDatabase/DatabaseCommonInteractionProps";
16
+ import BadDataException from "../../../Types/Exception/BadDataException";
17
+ import ObjectID from "../../../Types/ObjectID";
18
+ import PositiveNumber from "../../../Types/PositiveNumber";
19
+ import * as SnmpCredentialUtil from "../../../Utils/NetworkDevice/SnmpCredentialUtil";
20
+ import { afterEach, beforeEach, describe, expect, test } from "@jest/globals";
21
+
22
+ /*
23
+ * NetworkDeviceService and NetworkSiteService each import the complete
24
+ * monitoring/alerting service graph. This suite tests the profile service at
25
+ * its own boundary, so both collaborators are replaced before modules load;
26
+ * the individual tests still assert every count made through the boundary.
27
+ */
28
+ jest.mock("../../../Server/Services/NetworkDeviceService", () => {
29
+ return {
30
+ __esModule: true,
31
+ default: {
32
+ countBy: jest.fn(),
33
+ },
34
+ };
35
+ });
36
+
37
+ jest.mock("../../../Server/Services/NetworkSiteService", () => {
38
+ return {
39
+ __esModule: true,
40
+ default: {
41
+ countBy: jest.fn(),
42
+ },
43
+ };
44
+ });
45
+
46
+ /*
47
+ * The service owns two things and this suite pins both.
48
+ *
49
+ * THE NAME is trimmed on the way in and a blank one is refused, on create
50
+ * and on update alike. It is unique per project and is the label every
51
+ * device and site listing shows for the profile it joins. Without the trim,
52
+ * "Branch v2c" and "Branch v2c " are two profiles that render identically;
53
+ * without the blank check, " " sails past the required-column check (it
54
+ * is a truthy string) and produces a profile with no visible name in every
55
+ * picker.
56
+ *
57
+ * THE DELETE GUARD refuses to delete a profile any device or site still
58
+ * points at. Both foreign keys are ON DELETE SET NULL, so without the guard
59
+ * the delete succeeds and every device using the profile - directly, or
60
+ * through its site - silently goes from walked to ping-only on its next
61
+ * poll. The guard counts devices AND sites, reads as root but scoped to the
62
+ * caller's tenant, and names both counts in one message.
63
+ *
64
+ * Nothing here touches a database. The hooks are called directly; the
65
+ * service's own reads and the two collaborators' counts are spied.
66
+ */
67
+
68
+ const PROJECT_ID: ObjectID = new ObjectID(
69
+ "11111111-1111-4111-8111-111111111111",
70
+ );
71
+ const OTHER_PROJECT_ID: ObjectID = new ObjectID(
72
+ "33333333-3333-4333-8333-333333333333",
73
+ );
74
+ const PROFILE_ID: ObjectID = new ObjectID(
75
+ "22222222-2222-4222-8222-222222222222",
76
+ );
77
+ const SECOND_PROFILE_ID: ObjectID = new ObjectID(
78
+ "44444444-4444-4444-8444-444444444444",
79
+ );
80
+ const USER_ID: ObjectID = new ObjectID("55555555-5555-4555-8555-555555555555");
81
+
82
+ type ServiceInternals = {
83
+ onBeforeCreate: (
84
+ createBy: CreateBy<NetworkSnmpCredentialProfile>,
85
+ ) => Promise<OnCreate<NetworkSnmpCredentialProfile>>;
86
+ onBeforeUpdate: (
87
+ updateBy: UpdateBy<NetworkSnmpCredentialProfile>,
88
+ ) => Promise<OnUpdate<NetworkSnmpCredentialProfile>>;
89
+ onBeforeDelete: (
90
+ deleteBy: DeleteBy<NetworkSnmpCredentialProfile>,
91
+ ) => Promise<OnDelete<NetworkSnmpCredentialProfile>>;
92
+ };
93
+
94
+ function buildService(): {
95
+ service: NetworkSnmpCredentialProfileServiceType;
96
+ internals: ServiceInternals;
97
+ } {
98
+ const service: NetworkSnmpCredentialProfileServiceType =
99
+ new NetworkSnmpCredentialProfileServiceType();
100
+
101
+ const internals: ServiceInternals = service as unknown as ServiceInternals;
102
+
103
+ return { service, internals };
104
+ }
105
+
106
+ function createBy(
107
+ name: string | undefined,
108
+ extra?: Partial<NetworkSnmpCredentialProfile>,
109
+ ): CreateBy<NetworkSnmpCredentialProfile> {
110
+ const profile: NetworkSnmpCredentialProfile =
111
+ new NetworkSnmpCredentialProfile();
112
+ profile.projectId = PROJECT_ID;
113
+
114
+ if (name !== undefined) {
115
+ profile.name = name;
116
+ }
117
+
118
+ Object.assign(profile, extra || {});
119
+
120
+ return {
121
+ data: profile,
122
+ props: { isRoot: true },
123
+ } as CreateBy<NetworkSnmpCredentialProfile>;
124
+ }
125
+
126
+ function updateBy(
127
+ data: Partial<Record<keyof NetworkSnmpCredentialProfile, unknown>>,
128
+ ): UpdateBy<NetworkSnmpCredentialProfile> {
129
+ return {
130
+ query: { _id: PROFILE_ID.toString() },
131
+ data: data,
132
+ props: { isRoot: true },
133
+ limit: new PositiveNumber(1),
134
+ skip: new PositiveNumber(0),
135
+ } as unknown as UpdateBy<NetworkSnmpCredentialProfile>;
136
+ }
137
+
138
+ function deleteBy(
139
+ props: DatabaseCommonInteractionProps,
140
+ ): DeleteBy<NetworkSnmpCredentialProfile> {
141
+ return {
142
+ query: { _id: PROFILE_ID.toString() },
143
+ props: props,
144
+ limit: new PositiveNumber(1),
145
+ skip: new PositiveNumber(0),
146
+ } as unknown as DeleteBy<NetworkSnmpCredentialProfile>;
147
+ }
148
+
149
+ /*
150
+ * A row as the delete guard's own findBy returns it: id, name and projectId
151
+ * selected, nothing else.
152
+ */
153
+ function profileRow(data: {
154
+ id?: ObjectID | undefined;
155
+ projectId?: ObjectID | undefined;
156
+ name?: string | undefined;
157
+ }): NetworkSnmpCredentialProfile {
158
+ const profile: NetworkSnmpCredentialProfile =
159
+ new NetworkSnmpCredentialProfile();
160
+
161
+ if (data.id) {
162
+ profile.id = data.id;
163
+ }
164
+
165
+ if (data.projectId) {
166
+ profile.projectId = data.projectId;
167
+ }
168
+
169
+ profile.name = data.name ?? "Branch v2c";
170
+
171
+ return profile;
172
+ }
173
+
174
+ /*
175
+ * A caller who is NOT root: a project member deleting through the API. This
176
+ * is the case the tenant scoping exists for.
177
+ */
178
+ const MEMBER_PROPS: DatabaseCommonInteractionProps = {
179
+ isRoot: false,
180
+ tenantId: PROJECT_ID,
181
+ userId: USER_ID,
182
+ };
183
+
184
+ const ROOT_PROPS: DatabaseCommonInteractionProps = {
185
+ isRoot: true,
186
+ };
187
+
188
+ /*
189
+ * Every read the service inherits from DatabaseService, spied so a test can
190
+ * assert the create and update hooks made none of them. A hook that reads
191
+ * would need a database, and - more to the point - a hook that reads before
192
+ * the permission check runs is how a tenant leak starts (see the delete
193
+ * guard, which reads and therefore has to re-scope by hand).
194
+ */
195
+ function spyOnReads(
196
+ service: NetworkSnmpCredentialProfileServiceType,
197
+ ): Array<jest.SpyInstance> {
198
+ return [
199
+ jest
200
+ .spyOn(service, "findBy")
201
+ .mockRejectedValue(new Error("findBy must not be called")),
202
+ jest
203
+ .spyOn(service, "findOneBy")
204
+ .mockRejectedValue(new Error("findOneBy must not be called")),
205
+ jest
206
+ .spyOn(service, "countBy")
207
+ .mockRejectedValue(new Error("countBy must not be called")),
208
+ ];
209
+ }
210
+
211
+ function deviceCountBy(): jest.Mock {
212
+ return NetworkDeviceService.countBy as unknown as jest.Mock;
213
+ }
214
+
215
+ function siteCountBy(): jest.Mock {
216
+ return NetworkSiteService.countBy as unknown as jest.Mock;
217
+ }
218
+
219
+ function stubCounts(data: { devices: number; sites: number }): void {
220
+ deviceCountBy().mockResolvedValue(new PositiveNumber(data.devices));
221
+ siteCountBy().mockResolvedValue(new PositiveNumber(data.sites));
222
+ }
223
+
224
+ beforeEach(() => {
225
+ deviceCountBy().mockReset();
226
+ siteCountBy().mockReset();
227
+ });
228
+
229
+ afterEach(() => {
230
+ jest.restoreAllMocks();
231
+ });
232
+
233
+ describe("onBeforeCreate - the name", () => {
234
+ test("is trimmed", async () => {
235
+ const { internals } = buildService();
236
+
237
+ const result: OnCreate<NetworkSnmpCredentialProfile> =
238
+ await internals.onBeforeCreate(createBy(" Branch offices v2c "));
239
+
240
+ expect(result.createBy.data.name).toBe("Branch offices v2c");
241
+ });
242
+
243
+ test("is returned unchanged when it has nothing to trim", async () => {
244
+ const { internals } = buildService();
245
+
246
+ const result: OnCreate<NetworkSnmpCredentialProfile> =
247
+ await internals.onBeforeCreate(createBy("Core switches v3"));
248
+
249
+ expect(result.createBy.data.name).toBe("Core switches v3");
250
+ });
251
+
252
+ /*
253
+ * Interior whitespace is part of the name. "Branch offices" with two
254
+ * spaces is odd but is what the operator typed; only the edges are
255
+ * anybody's business.
256
+ */
257
+ test("keeps interior whitespace", async () => {
258
+ const { internals } = buildService();
259
+
260
+ const result: OnCreate<NetworkSnmpCredentialProfile> =
261
+ await internals.onBeforeCreate(createBy(" Branch offices "));
262
+
263
+ expect(result.createBy.data.name).toBe("Branch offices");
264
+ });
265
+
266
+ test.each(["", " ", " ", "\t", "\n", " \t\n "])(
267
+ "a blank name %p is refused",
268
+ async (name: string) => {
269
+ const { internals } = buildService();
270
+
271
+ await expect(internals.onBeforeCreate(createBy(name))).rejects.toThrow(
272
+ BadDataException,
273
+ );
274
+ },
275
+ );
276
+
277
+ /*
278
+ * The required-column check in DatabaseService also refuses an undefined
279
+ * name, but it runs after this hook, and the hook must not crash on the
280
+ * way there with a TypeError from calling trim() on undefined.
281
+ */
282
+ test("a missing name is refused with a BadDataException, not a TypeError", async () => {
283
+ const { internals } = buildService();
284
+
285
+ await expect(internals.onBeforeCreate(createBy(undefined))).rejects.toThrow(
286
+ BadDataException,
287
+ );
288
+ });
289
+
290
+ test("the refusal names the profile so the form can show it", async () => {
291
+ const { internals } = buildService();
292
+
293
+ await expect(internals.onBeforeCreate(createBy(" "))).rejects.toThrow(
294
+ /SNMP Credential Profile name/,
295
+ );
296
+ });
297
+ });
298
+
299
+ describe("onBeforeCreate - everything that is not the name", () => {
300
+ /*
301
+ * The secrets are passed through byte for byte. A community string or a
302
+ * passphrase may legitimately begin or end with whitespace, and a service
303
+ * that "helpfully" trimmed one would produce a profile that never
304
+ * authenticates, with nothing anywhere to say why. (Encryption at rest
305
+ * happens in DatabaseService AFTER this hook, on the value as returned
306
+ * here - so what this hook returns is what gets encrypted.)
307
+ */
308
+ test("the credentials are not trimmed or otherwise touched", async () => {
309
+ const { internals } = buildService();
310
+
311
+ const result: OnCreate<NetworkSnmpCredentialProfile> =
312
+ await internals.onBeforeCreate(
313
+ createBy("Branch", {
314
+ snmpVersion: "V3",
315
+ snmpCommunityString: " public ",
316
+ snmpPort: 1161,
317
+ snmpV3SecurityLevel: "authPriv",
318
+ snmpV3Username: " monitoring ",
319
+ snmpV3AuthProtocol: "SHA",
320
+ snmpV3AuthKey: " auth key with spaces ",
321
+ snmpV3PrivProtocol: "AES",
322
+ snmpV3PrivKey: " priv key with spaces ",
323
+ }),
324
+ );
325
+
326
+ expect(result.createBy.data.snmpVersion).toBe("V3");
327
+ expect(result.createBy.data.snmpCommunityString).toBe(" public ");
328
+ expect(result.createBy.data.snmpPort).toBe(1161);
329
+ expect(result.createBy.data.snmpV3SecurityLevel).toBe("authPriv");
330
+ expect(result.createBy.data.snmpV3Username).toBe(" monitoring ");
331
+ expect(result.createBy.data.snmpV3AuthProtocol).toBe("SHA");
332
+ expect(result.createBy.data.snmpV3AuthKey).toBe(" auth key with spaces ");
333
+ expect(result.createBy.data.snmpV3PrivProtocol).toBe("AES");
334
+ expect(result.createBy.data.snmpV3PrivKey).toBe(" priv key with spaces ");
335
+ });
336
+
337
+ /*
338
+ * A profile with no credentials at all is allowed. It is a name an
339
+ * operator can attach to devices now and fill in later, and the poller's
340
+ * predicate (hasUsableCredentials) is what decides it is not yet usable -
341
+ * refusing it here would just move the empty state to the form.
342
+ */
343
+ test("a profile with no credentials is accepted", async () => {
344
+ const { internals } = buildService();
345
+
346
+ const result: OnCreate<NetworkSnmpCredentialProfile> =
347
+ await internals.onBeforeCreate(createBy("Not yet configured"));
348
+
349
+ expect(result.createBy.data.name).toBe("Not yet configured");
350
+ });
351
+
352
+ test("it carries nothing forward and makes no database read", async () => {
353
+ const { service, internals } = buildService();
354
+ const reads: Array<jest.SpyInstance> = spyOnReads(service);
355
+
356
+ const result: OnCreate<NetworkSnmpCredentialProfile> =
357
+ await internals.onBeforeCreate(createBy("Branch"));
358
+
359
+ expect(result.carryForward).toBeNull();
360
+
361
+ for (const read of reads) {
362
+ expect(read).not.toHaveBeenCalled();
363
+ }
364
+ });
365
+ });
366
+
367
+ describe("onBeforeUpdate - the name", () => {
368
+ test("is trimmed when the update carries one", async () => {
369
+ const { internals } = buildService();
370
+
371
+ const result: OnUpdate<NetworkSnmpCredentialProfile> =
372
+ await internals.onBeforeUpdate(updateBy({ name: " Renamed " }));
373
+
374
+ expect(result.updateBy.data.name).toBe("Renamed");
375
+ });
376
+
377
+ test.each(["", " ", "\t\n"])(
378
+ "a blank name %p is refused",
379
+ async (name: string) => {
380
+ const { internals } = buildService();
381
+
382
+ await expect(
383
+ internals.onBeforeUpdate(updateBy({ name: name })),
384
+ ).rejects.toThrow(BadDataException);
385
+ },
386
+ );
387
+
388
+ /*
389
+ * null is what a form sends when a field is cleared, and PartialEntity
390
+ * allows it on every column. On the name it means "no name", which the
391
+ * NOT NULL constraint would reject anyway - but as a database error, not
392
+ * a form error. Refuse it here, where the message reaches the operator.
393
+ */
394
+ test("a null name is refused as blank rather than left for the database", async () => {
395
+ const { internals } = buildService();
396
+
397
+ await expect(
398
+ internals.onBeforeUpdate(updateBy({ name: null })),
399
+ ).rejects.toThrow(BadDataException);
400
+ });
401
+
402
+ /*
403
+ * PartialEntity also allows a `() => string` on any column, which is the
404
+ * raw-SQL-expression escape hatch. A label built from a SQL expression is
405
+ * not a thing, and letting one through would bypass both the trim and the
406
+ * blank check.
407
+ */
408
+ test("a raw SQL expression is not accepted as a name", async () => {
409
+ const { internals } = buildService();
410
+
411
+ await expect(
412
+ internals.onBeforeUpdate(
413
+ updateBy({
414
+ name: (): string => {
415
+ return "UPPER(name)";
416
+ },
417
+ }),
418
+ ),
419
+ ).rejects.toThrow(BadDataException);
420
+ });
421
+ });
422
+
423
+ describe("onBeforeUpdate - updates that do not mention the name", () => {
424
+ /*
425
+ * THE CASE THAT MATTERS MOST for update. Rotating a community string is
426
+ * the whole reason profiles exist, and it is an update whose data has no
427
+ * `name` key at all. The hook must recognise "not mentioned" (undefined)
428
+ * as distinct from "blank" and leave the row's name alone - a truthiness
429
+ * check here would refuse every credential rotation as a blank rename.
430
+ */
431
+ test("a credential rotation passes through with the name untouched", async () => {
432
+ const { internals } = buildService();
433
+
434
+ const result: OnUpdate<NetworkSnmpCredentialProfile> =
435
+ await internals.onBeforeUpdate(
436
+ updateBy({ snmpCommunityString: "rotated-2026" }),
437
+ );
438
+
439
+ expect(result.updateBy.data.snmpCommunityString).toBe("rotated-2026");
440
+ expect("name" in result.updateBy.data).toBe(false);
441
+ });
442
+
443
+ test("a description-only edit passes through", async () => {
444
+ const { internals } = buildService();
445
+
446
+ const result: OnUpdate<NetworkSnmpCredentialProfile> =
447
+ await internals.onBeforeUpdate(
448
+ updateBy({ description: "Used by the branch switches." }),
449
+ );
450
+
451
+ expect(result.updateBy.data.description).toBe(
452
+ "Used by the branch switches.",
453
+ );
454
+ expect(result.updateBy.data.name).toBeUndefined();
455
+ });
456
+
457
+ test("the credentials are not trimmed on update either", async () => {
458
+ const { internals } = buildService();
459
+
460
+ const result: OnUpdate<NetworkSnmpCredentialProfile> =
461
+ await internals.onBeforeUpdate(
462
+ updateBy({
463
+ name: " Renamed ",
464
+ snmpV3AuthKey: " key with spaces ",
465
+ snmpV3PrivKey: " other key ",
466
+ }),
467
+ );
468
+
469
+ expect(result.updateBy.data.name).toBe("Renamed");
470
+ expect(result.updateBy.data.snmpV3AuthKey).toBe(" key with spaces ");
471
+ expect(result.updateBy.data.snmpV3PrivKey).toBe(" other key ");
472
+ });
473
+
474
+ test("it carries nothing forward and makes no database read", async () => {
475
+ const { service, internals } = buildService();
476
+ const reads: Array<jest.SpyInstance> = spyOnReads(service);
477
+
478
+ const result: OnUpdate<NetworkSnmpCredentialProfile> =
479
+ await internals.onBeforeUpdate(updateBy({ name: "Renamed" }));
480
+
481
+ expect(result.carryForward).toBeNull();
482
+
483
+ for (const read of reads) {
484
+ expect(read).not.toHaveBeenCalled();
485
+ }
486
+ });
487
+ });
488
+
489
+ describe("onBeforeDelete - a profile still in use cannot be deleted", () => {
490
+ const EXPECTED_MESSAGE: (devices: number, sites: number) => string = (
491
+ devices: number,
492
+ sites: number,
493
+ ): string => {
494
+ return `This SNMP Credential Profile is used by ${devices} devices and ${sites} sites. Remove it from them first, or point them at another profile.`;
495
+ };
496
+
497
+ test("refused when devices point at it", async () => {
498
+ const { service, internals } = buildService();
499
+ jest
500
+ .spyOn(service, "findBy")
501
+ .mockResolvedValue([
502
+ profileRow({ id: PROFILE_ID, projectId: PROJECT_ID }),
503
+ ]);
504
+ stubCounts({ devices: 3, sites: 0 });
505
+
506
+ await expect(
507
+ internals.onBeforeDelete(deleteBy(MEMBER_PROPS)),
508
+ ).rejects.toThrow(BadDataException);
509
+ await expect(
510
+ internals.onBeforeDelete(deleteBy(MEMBER_PROPS)),
511
+ ).rejects.toThrow(EXPECTED_MESSAGE(3, 0));
512
+ });
513
+
514
+ /*
515
+ * THE CASE THE SITE COUNT EXISTS FOR. A site can hold the only reference
516
+ * to a profile while no device points at it directly - "set it once per
517
+ * site" means most devices never will - and the device count alone says
518
+ * this delete is safe when it is not.
519
+ */
520
+ test("refused when only sites point at it", async () => {
521
+ const { service, internals } = buildService();
522
+ jest
523
+ .spyOn(service, "findBy")
524
+ .mockResolvedValue([
525
+ profileRow({ id: PROFILE_ID, projectId: PROJECT_ID }),
526
+ ]);
527
+ stubCounts({ devices: 0, sites: 2 });
528
+
529
+ await expect(
530
+ internals.onBeforeDelete(deleteBy(MEMBER_PROPS)),
531
+ ).rejects.toThrow(EXPECTED_MESSAGE(0, 2));
532
+ });
533
+
534
+ /*
535
+ * Both counts in one message, not the first non-zero one: the operator
536
+ * should learn everything that has to move before the delete can go
537
+ * through, rather than clearing the devices and then being told about
538
+ * the sites.
539
+ */
540
+ test("refused naming both counts when both point at it", async () => {
541
+ const { service, internals } = buildService();
542
+ jest
543
+ .spyOn(service, "findBy")
544
+ .mockResolvedValue([
545
+ profileRow({ id: PROFILE_ID, projectId: PROJECT_ID }),
546
+ ]);
547
+ stubCounts({ devices: 7, sites: 1 });
548
+
549
+ await expect(
550
+ internals.onBeforeDelete(deleteBy(MEMBER_PROPS)),
551
+ ).rejects.toThrow(EXPECTED_MESSAGE(7, 1));
552
+ });
553
+
554
+ test("allowed when neither devices nor sites point at it", async () => {
555
+ const { service, internals } = buildService();
556
+ jest
557
+ .spyOn(service, "findBy")
558
+ .mockResolvedValue([
559
+ profileRow({ id: PROFILE_ID, projectId: PROJECT_ID }),
560
+ ]);
561
+ stubCounts({ devices: 0, sites: 0 });
562
+
563
+ const input: DeleteBy<NetworkSnmpCredentialProfile> =
564
+ deleteBy(MEMBER_PROPS);
565
+ const result: OnDelete<NetworkSnmpCredentialProfile> =
566
+ await internals.onBeforeDelete(input);
567
+
568
+ expect(result.deleteBy).toBe(input);
569
+ expect(result.carryForward).toBeNull();
570
+ expect(deviceCountBy()).toHaveBeenCalledTimes(1);
571
+ expect(siteCountBy()).toHaveBeenCalledTimes(1);
572
+ });
573
+
574
+ test("a delete that matches no profile is allowed without counting anything", async () => {
575
+ const { service, internals } = buildService();
576
+ jest.spyOn(service, "findBy").mockResolvedValue([]);
577
+
578
+ await internals.onBeforeDelete(deleteBy(MEMBER_PROPS));
579
+
580
+ expect(deviceCountBy()).not.toHaveBeenCalled();
581
+ expect(siteCountBy()).not.toHaveBeenCalled();
582
+ });
583
+
584
+ /*
585
+ * A row the guard's own select could not fill in (no id, or no project)
586
+ * cannot be counted against and must not crash the hook. The database's
587
+ * own constraints make such a row impossible in practice; this pins that
588
+ * the loop is defensive rather than trusting.
589
+ */
590
+ test("a row without an id or a project is skipped rather than counted", async () => {
591
+ const { service, internals } = buildService();
592
+ jest
593
+ .spyOn(service, "findBy")
594
+ .mockResolvedValue([
595
+ profileRow({ id: undefined, projectId: PROJECT_ID }),
596
+ profileRow({ id: PROFILE_ID, projectId: undefined }),
597
+ ]);
598
+ stubCounts({ devices: 99, sites: 99 });
599
+
600
+ await internals.onBeforeDelete(deleteBy(MEMBER_PROPS));
601
+
602
+ expect(deviceCountBy()).not.toHaveBeenCalled();
603
+ expect(siteCountBy()).not.toHaveBeenCalled();
604
+ });
605
+
606
+ test("with several profiles, the first one in use stops the whole delete", async () => {
607
+ const { service, internals } = buildService();
608
+ jest.spyOn(service, "findBy").mockResolvedValue([
609
+ profileRow({ id: PROFILE_ID, projectId: PROJECT_ID, name: "Unused" }),
610
+ profileRow({
611
+ id: SECOND_PROFILE_ID,
612
+ projectId: PROJECT_ID,
613
+ name: "In use",
614
+ }),
615
+ ]);
616
+ deviceCountBy()
617
+ .mockResolvedValueOnce(new PositiveNumber(0))
618
+ .mockResolvedValueOnce(new PositiveNumber(4));
619
+ siteCountBy()
620
+ .mockResolvedValueOnce(new PositiveNumber(0))
621
+ .mockResolvedValueOnce(new PositiveNumber(0));
622
+
623
+ await expect(
624
+ internals.onBeforeDelete(deleteBy(MEMBER_PROPS)),
625
+ ).rejects.toThrow(EXPECTED_MESSAGE(4, 0));
626
+ });
627
+ });
628
+
629
+ describe("onBeforeDelete - the counts come from tenant-scoped queries", () => {
630
+ /*
631
+ * The hook runs BEFORE DatabaseService applies the caller's tenant to the
632
+ * delete query, and it reads as root so that it sees every profile the
633
+ * delete would touch. Root plus the caller's raw query would hand back
634
+ * other tenants' profiles - and then count other tenants' devices against
635
+ * them. The caller's tenant is re-applied by hand.
636
+ */
637
+ test("the profiles are read as root, with the caller's tenant re-applied to the query", async () => {
638
+ const { service, internals } = buildService();
639
+ const findBySpy: jest.SpyInstance = jest
640
+ .spyOn(service, "findBy")
641
+ .mockResolvedValue([]);
642
+
643
+ await internals.onBeforeDelete(deleteBy(MEMBER_PROPS));
644
+
645
+ expect(findBySpy).toHaveBeenCalledTimes(1);
646
+
647
+ const call: {
648
+ query: Record<string, unknown>;
649
+ props: { isRoot?: boolean };
650
+ } = findBySpy.mock.calls[0]![0] as {
651
+ query: Record<string, unknown>;
652
+ props: { isRoot?: boolean };
653
+ };
654
+
655
+ expect(call.props.isRoot).toBe(true);
656
+ expect(call.query["_id"]).toBe(PROFILE_ID.toString());
657
+ expect(call.query["projectId"]).toBe(PROJECT_ID);
658
+ });
659
+
660
+ test("a root caller's query is passed through untouched", async () => {
661
+ const { service, internals } = buildService();
662
+ const findBySpy: jest.SpyInstance = jest
663
+ .spyOn(service, "findBy")
664
+ .mockResolvedValue([]);
665
+
666
+ await internals.onBeforeDelete(deleteBy(ROOT_PROPS));
667
+
668
+ const call: { query: Record<string, unknown> } = findBySpy.mock
669
+ .calls[0]![0] as { query: Record<string, unknown> };
670
+
671
+ expect(call.query).toEqual({ _id: PROFILE_ID.toString() });
672
+ });
673
+
674
+ /*
675
+ * The device and site counts are keyed on the PROFILE'S project, not the
676
+ * caller's, and on the profile's id: "how many rows in this project point
677
+ * at this row". Root, because the member deleting a profile is not
678
+ * necessarily allowed to read every device, and a partial count would let
679
+ * the delete through with devices still attached.
680
+ */
681
+ test("devices and sites are counted as root, in the profile's project, by profile id", async () => {
682
+ const { service, internals } = buildService();
683
+ jest
684
+ .spyOn(service, "findBy")
685
+ .mockResolvedValue([
686
+ profileRow({ id: PROFILE_ID, projectId: OTHER_PROJECT_ID }),
687
+ ]);
688
+ stubCounts({ devices: 0, sites: 0 });
689
+
690
+ await internals.onBeforeDelete(deleteBy(MEMBER_PROPS));
691
+
692
+ for (const countBy of [deviceCountBy(), siteCountBy()]) {
693
+ expect(countBy).toHaveBeenCalledTimes(1);
694
+
695
+ const call: {
696
+ query: Record<string, unknown>;
697
+ props: { isRoot?: boolean };
698
+ } = countBy.mock.calls[0]![0] as {
699
+ query: Record<string, unknown>;
700
+ props: { isRoot?: boolean };
701
+ };
702
+
703
+ expect(call.props.isRoot).toBe(true);
704
+ /*
705
+ * Compared by value: BaseModel.id is a getter that mints a fresh
706
+ * ObjectID from _id on every read, so the instance the service passed
707
+ * is never the one this test holds.
708
+ */
709
+ expect(String(call.query["snmpCredentialProfileId"])).toBe(
710
+ PROFILE_ID.toString(),
711
+ );
712
+ expect(String(call.query["projectId"])).toBe(OTHER_PROJECT_ID.toString());
713
+ expect(Object.keys(call.query).sort()).toEqual([
714
+ "projectId",
715
+ "snmpCredentialProfileId",
716
+ ]);
717
+ }
718
+ });
719
+
720
+ test("each profile being deleted is counted separately", async () => {
721
+ const { service, internals } = buildService();
722
+ jest
723
+ .spyOn(service, "findBy")
724
+ .mockResolvedValue([
725
+ profileRow({ id: PROFILE_ID, projectId: PROJECT_ID }),
726
+ profileRow({ id: SECOND_PROFILE_ID, projectId: PROJECT_ID }),
727
+ ]);
728
+ stubCounts({ devices: 0, sites: 0 });
729
+
730
+ await internals.onBeforeDelete(deleteBy(MEMBER_PROPS));
731
+
732
+ const countedProfileIds: (countBy: jest.Mock) => Array<string> = (
733
+ countBy: jest.Mock,
734
+ ): Array<string> => {
735
+ return countBy.mock.calls.map((call: Array<unknown>): string => {
736
+ return (
737
+ (call[0] as { query: { snmpCredentialProfileId: ObjectID } }).query
738
+ .snmpCredentialProfileId as ObjectID
739
+ ).toString();
740
+ });
741
+ };
742
+
743
+ expect(countedProfileIds(deviceCountBy())).toEqual([
744
+ PROFILE_ID.toString(),
745
+ SECOND_PROFILE_ID.toString(),
746
+ ]);
747
+ expect(countedProfileIds(siteCountBy())).toEqual([
748
+ PROFILE_ID.toString(),
749
+ SECOND_PROFILE_ID.toString(),
750
+ ]);
751
+ });
752
+ });
753
+
754
+ describe("hasUsableCredentials on the service", () => {
755
+ /*
756
+ * The service exposes the predicate for callers that hold a service and
757
+ * not the util, and it must be THE predicate - the same function the
758
+ * poller applies to a device row - not a second opinion that could drift.
759
+ * Spying on the util's export pins the delegation.
760
+ */
761
+ test("delegates to the shared util, so a device and a profile get one answer", () => {
762
+ const spy: jest.SpyInstance = jest
763
+ .spyOn(SnmpCredentialUtil, "hasUsableCredentials")
764
+ .mockReturnValue(true);
765
+
766
+ const profile: NetworkSnmpCredentialProfile =
767
+ new NetworkSnmpCredentialProfile();
768
+ profile.snmpVersion = "V3";
769
+ profile.snmpV3Username = "monitoring";
770
+
771
+ expect(
772
+ NetworkSnmpCredentialProfileService.hasUsableCredentials(profile),
773
+ ).toBe(true);
774
+ expect(spy).toHaveBeenCalledTimes(1);
775
+ expect(spy).toHaveBeenCalledWith(profile);
776
+ });
777
+
778
+ test("answers for a real profile without a spy", () => {
779
+ const usable: NetworkSnmpCredentialProfile =
780
+ new NetworkSnmpCredentialProfile();
781
+ usable.snmpVersion = "V2c";
782
+ usable.snmpCommunityString = "public";
783
+
784
+ const unusable: NetworkSnmpCredentialProfile =
785
+ new NetworkSnmpCredentialProfile();
786
+ unusable.snmpVersion = "V2c";
787
+ unusable.snmpCommunityString = " ";
788
+
789
+ expect(
790
+ NetworkSnmpCredentialProfileService.hasUsableCredentials(usable),
791
+ ).toBe(true);
792
+ expect(
793
+ NetworkSnmpCredentialProfileService.hasUsableCredentials(unusable),
794
+ ).toBe(false);
795
+ });
796
+ });