@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,1109 @@
1
+ import NetworkSiteService from "../../../Server/Services/NetworkSiteService";
2
+ import NetworkSiteTypeService from "../../../Server/Services/NetworkSiteTypeService";
3
+ import DatabaseService from "../../../Server/Services/DatabaseService";
4
+ import NetworkSiteHierarchyLock, {
5
+ NETWORK_SITE_HIERARCHY_ROOT_SCOPE_ERROR_MESSAGE,
6
+ } from "../../../Server/Utils/NetworkSite/NetworkSiteHierarchyLock";
7
+ import CreateBy from "../../../Server/Types/Database/CreateBy";
8
+ import DeleteBy from "../../../Server/Types/Database/DeleteBy";
9
+ import UpdateBy from "../../../Server/Types/Database/UpdateBy";
10
+ import NetworkSite from "../../../Models/DatabaseModels/NetworkSite";
11
+ import NetworkSiteType from "../../../Models/DatabaseModels/NetworkSiteType";
12
+ import BadDataException from "../../../Types/Exception/BadDataException";
13
+ import ObjectID from "../../../Types/ObjectID";
14
+ import PositiveNumber from "../../../Types/PositiveNumber";
15
+ import { afterEach, describe, expect, it } from "@jest/globals";
16
+
17
+ /*
18
+ * NetworkSiteService imports the complete monitoring/alerting service graph.
19
+ * This suite tests NetworkSiteTypeService at its service boundary, so replace
20
+ * that collaborator before modules load; the individual tests still spy and
21
+ * assert every site read made through the boundary.
22
+ */
23
+ jest.mock("../../../Server/Services/NetworkSiteService", () => {
24
+ return {
25
+ __esModule: true,
26
+ default: {
27
+ findBy: jest.fn(),
28
+ findOneBy: jest.fn(),
29
+ },
30
+ };
31
+ });
32
+
33
+ const PROJECT_ID: ObjectID = new ObjectID(
34
+ "11111111-1111-4111-8111-111111111111",
35
+ );
36
+ const OTHER_PROJECT_ID: ObjectID = new ObjectID(
37
+ "22222222-2222-4222-8222-222222222222",
38
+ );
39
+
40
+ function typeId(index: number): ObjectID {
41
+ return new ObjectID(
42
+ `aaaaaaaa-aaaa-4aaa-8aaa-${index.toString().padStart(12, "0")}`,
43
+ );
44
+ }
45
+
46
+ function siteId(index: number): ObjectID {
47
+ return new ObjectID(
48
+ `bbbbbbbb-bbbb-4bbb-8bbb-${index.toString().padStart(12, "0")}`,
49
+ );
50
+ }
51
+
52
+ function makeType(data: {
53
+ index: number;
54
+ projectId?: ObjectID | undefined;
55
+ parentIndex?: number | undefined;
56
+ isUnitLevel?: boolean | undefined;
57
+ order?: number | null | undefined;
58
+ }): NetworkSiteType {
59
+ const networkSiteType: NetworkSiteType = new NetworkSiteType();
60
+ networkSiteType.id = typeId(data.index);
61
+ networkSiteType.projectId = data.projectId || PROJECT_ID;
62
+
63
+ if (data.isUnitLevel !== undefined) {
64
+ networkSiteType.isUnitLevel = data.isUnitLevel;
65
+ }
66
+
67
+ if (typeof data.order === "number") {
68
+ networkSiteType.order = data.order;
69
+ }
70
+
71
+ if (data.parentIndex !== undefined) {
72
+ networkSiteType.parentNetworkSiteTypeId = typeId(data.parentIndex);
73
+ }
74
+
75
+ return networkSiteType;
76
+ }
77
+
78
+ function makeSite(data: {
79
+ index: number;
80
+ typeIndex?: number | undefined;
81
+ parentIndex?: number | undefined;
82
+ }): NetworkSite {
83
+ const site: NetworkSite = new NetworkSite();
84
+ site.id = siteId(data.index);
85
+
86
+ if (data.typeIndex !== undefined) {
87
+ site.networkSiteTypeId = typeId(data.typeIndex);
88
+ }
89
+
90
+ if (data.parentIndex !== undefined) {
91
+ site.parentSiteId = siteId(data.parentIndex);
92
+ }
93
+
94
+ return site;
95
+ }
96
+
97
+ function createBy(data: Partial<NetworkSiteType>): CreateBy<NetworkSiteType> {
98
+ const networkSiteType: NetworkSiteType = new NetworkSiteType();
99
+ networkSiteType.projectId = PROJECT_ID;
100
+ networkSiteType.name = "New Type";
101
+ Object.assign(networkSiteType, data);
102
+
103
+ return {
104
+ data: networkSiteType,
105
+ props: { isRoot: true },
106
+ };
107
+ }
108
+
109
+ function updateBy(
110
+ data: Record<string, unknown>,
111
+ props: Record<string, unknown> = { isRoot: true },
112
+ ): UpdateBy<NetworkSiteType> {
113
+ return {
114
+ query: { _id: typeId(2).toString() },
115
+ data,
116
+ limit: new PositiveNumber(1),
117
+ skip: new PositiveNumber(0),
118
+ props,
119
+ } as unknown as UpdateBy<NetworkSiteType>;
120
+ }
121
+
122
+ function deleteBy(
123
+ props: Record<string, unknown> = { isRoot: true },
124
+ ): DeleteBy<NetworkSiteType> {
125
+ return {
126
+ query: { _id: typeId(2).toString() },
127
+ limit: new PositiveNumber(1),
128
+ skip: new PositiveNumber(0),
129
+ props,
130
+ } as unknown as DeleteBy<NetworkSiteType>;
131
+ }
132
+
133
+ /*
134
+ * Configure the three type reads made by a parent update: rows matched by the
135
+ * update, all project types for cycle detection, and destination siblings for
136
+ * automatic order assignment.
137
+ */
138
+ function mockParentUpdate(data: {
139
+ moving?: NetworkSiteType | undefined;
140
+ parent?: NetworkSiteType | null | undefined;
141
+ projectTypes?: Array<NetworkSiteType> | undefined;
142
+ siblings?: Array<NetworkSiteType> | undefined;
143
+ }): void {
144
+ const moving: NetworkSiteType =
145
+ data.moving || makeType({ index: 2, parentIndex: 1 });
146
+
147
+ jest
148
+ .spyOn(NetworkSiteTypeService, "findOneById")
149
+ .mockResolvedValue(
150
+ data.parent === undefined ? makeType({ index: 4 }) : data.parent,
151
+ );
152
+
153
+ jest
154
+ .spyOn(NetworkSiteTypeService, "findBy")
155
+ .mockImplementation(async (findBy: any) => {
156
+ if (findBy.skip > 0) {
157
+ return [];
158
+ }
159
+
160
+ if (findBy.query?._id) {
161
+ return [moving];
162
+ }
163
+
164
+ if (findBy.query?.parentNetworkSiteTypeId !== undefined) {
165
+ return data.siblings || [];
166
+ }
167
+
168
+ return data.projectTypes || [moving];
169
+ });
170
+ }
171
+
172
+ afterEach(() => {
173
+ jest.clearAllMocks();
174
+ jest.restoreAllMocks();
175
+ });
176
+
177
+ describe("NetworkSiteTypeService create hierarchy validation", () => {
178
+ it("accepts an ID parent in the same project and appends after the maximum numbered sibling", async () => {
179
+ jest
180
+ .spyOn(NetworkSiteTypeService, "findOneById")
181
+ .mockResolvedValue(makeType({ index: 1 }));
182
+ jest
183
+ .spyOn(NetworkSiteTypeService, "findBy")
184
+ .mockResolvedValue([
185
+ makeType({ index: 3, order: null }),
186
+ makeType({ index: 4, order: 7 }),
187
+ makeType({ index: 5, order: 2 }),
188
+ ]);
189
+ const input: CreateBy<NetworkSiteType> = createBy({
190
+ parentNetworkSiteTypeId: typeId(1),
191
+ });
192
+
193
+ await (NetworkSiteTypeService as any).onBeforeCreate(input);
194
+
195
+ expect(input.data.order).toBe(8);
196
+ expect(NetworkSiteTypeService.findOneById).toHaveBeenCalledWith(
197
+ expect.objectContaining({ id: typeId(1) }),
198
+ );
199
+ });
200
+
201
+ it("accepts the relation-object spelling used by entity pickers", async () => {
202
+ const parent: NetworkSiteType = makeType({ index: 1 });
203
+ jest.spyOn(NetworkSiteTypeService, "findOneById").mockResolvedValue(parent);
204
+ jest.spyOn(NetworkSiteTypeService, "findBy").mockResolvedValue([]);
205
+
206
+ await expect(
207
+ (NetworkSiteTypeService as any).onBeforeCreate(
208
+ createBy({ parentNetworkSiteType: parent }),
209
+ ),
210
+ ).resolves.toBeDefined();
211
+ });
212
+
213
+ it("rejects conflicting relation and ID parent values", async () => {
214
+ const parent: NetworkSiteType = makeType({ index: 1 });
215
+
216
+ await expect(
217
+ (NetworkSiteTypeService as any).onBeforeCreate(
218
+ createBy({
219
+ parentNetworkSiteTypeId: typeId(3),
220
+ parentNetworkSiteType: parent,
221
+ }),
222
+ ),
223
+ ).rejects.toThrow("Conflicting parent Network Site Type references");
224
+ });
225
+
226
+ it("rejects a non-null relation object that does not carry an ID", async () => {
227
+ await expect(
228
+ (NetworkSiteTypeService as any).onBeforeCreate(
229
+ createBy({
230
+ parentNetworkSiteType: {} as NetworkSiteType,
231
+ }),
232
+ ),
233
+ ).rejects.toThrow("must contain a valid Network Site Type ID");
234
+ });
235
+
236
+ it("rejects a raw SQL parent expression", async () => {
237
+ await expect(
238
+ (NetworkSiteTypeService as any).onBeforeCreate(
239
+ createBy({
240
+ parentNetworkSiteTypeId: (() => {
241
+ return "some SQL";
242
+ }) as unknown as ObjectID,
243
+ }),
244
+ ),
245
+ ).rejects.toThrow("cannot be set to a raw SQL expression");
246
+ });
247
+
248
+ it("rejects a raw SQL isUnitLevel expression on create", async () => {
249
+ await expect(
250
+ (NetworkSiteTypeService as any).onBeforeCreate(
251
+ createBy({
252
+ isUnitLevel: (() => {
253
+ return true;
254
+ }) as unknown as boolean,
255
+ }),
256
+ ),
257
+ ).rejects.toThrow("unit-level leaf rules must be validated");
258
+ });
259
+
260
+ it("rejects a missing parent", async () => {
261
+ jest.spyOn(NetworkSiteTypeService, "findOneById").mockResolvedValue(null);
262
+
263
+ await expect(
264
+ (NetworkSiteTypeService as any).onBeforeCreate(
265
+ createBy({ parentNetworkSiteTypeId: typeId(1) }),
266
+ ),
267
+ ).rejects.toThrow(
268
+ new BadDataException("Parent Network Site Type not found."),
269
+ );
270
+ });
271
+
272
+ it("rejects a parent from another project", async () => {
273
+ jest
274
+ .spyOn(NetworkSiteTypeService, "findOneById")
275
+ .mockResolvedValue(makeType({ index: 1, projectId: OTHER_PROJECT_ID }));
276
+
277
+ await expect(
278
+ (NetworkSiteTypeService as any).onBeforeCreate(
279
+ createBy({ parentNetworkSiteTypeId: typeId(1) }),
280
+ ),
281
+ ).rejects.toThrow("must belong to the same project");
282
+ });
283
+
284
+ it("rejects a unit-level parent", async () => {
285
+ jest
286
+ .spyOn(NetworkSiteTypeService, "findOneById")
287
+ .mockResolvedValue(makeType({ index: 1, isUnitLevel: true }));
288
+
289
+ await expect(
290
+ (NetworkSiteTypeService as any).onBeforeCreate(
291
+ createBy({ parentNetworkSiteTypeId: typeId(1) }),
292
+ ),
293
+ ).rejects.toThrow("unit-level Network Site Type cannot have child types");
294
+ });
295
+
296
+ it("rejects self-parenting when a server-side caller supplies the new ID", async () => {
297
+ await expect(
298
+ (NetworkSiteTypeService as any).onBeforeCreate(
299
+ createBy({ id: typeId(2), parentNetworkSiteTypeId: typeId(2) }),
300
+ ),
301
+ ).rejects.toThrow("cannot be its own parent");
302
+ });
303
+
304
+ it("assigns the next root sibling order when no parent is supplied", async () => {
305
+ const findOneByIdSpy: jest.SpyInstance = jest.spyOn(
306
+ NetworkSiteTypeService,
307
+ "findOneById",
308
+ );
309
+ jest
310
+ .spyOn(NetworkSiteTypeService, "findBy")
311
+ .mockResolvedValue([
312
+ makeType({ index: 1, order: 3 }),
313
+ makeType({ index: 3, order: 9 }),
314
+ ]);
315
+ const input: CreateBy<NetworkSiteType> = createBy({});
316
+
317
+ await (NetworkSiteTypeService as any).onBeforeCreate(input);
318
+
319
+ expect(input.data.order).toBe(10);
320
+ expect(findOneByIdSpy).not.toHaveBeenCalled();
321
+ });
322
+
323
+ it("preserves an explicitly supplied order without reading siblings", async () => {
324
+ const findBySpy: jest.SpyInstance = jest.spyOn(
325
+ NetworkSiteTypeService,
326
+ "findBy",
327
+ );
328
+ const input: CreateBy<NetworkSiteType> = createBy({ order: 42 });
329
+
330
+ await (NetworkSiteTypeService as any).onBeforeCreate(input);
331
+
332
+ expect(input.data.order).toBe(42);
333
+ expect(findBySpy).not.toHaveBeenCalled();
334
+ });
335
+ });
336
+
337
+ describe("NetworkSiteTypeService update hierarchy validation", () => {
338
+ it("rejects moving a type to another project", async () => {
339
+ jest
340
+ .spyOn(NetworkSiteTypeService, "findBy")
341
+ .mockResolvedValue([makeType({ index: 2 })]);
342
+
343
+ await expect(
344
+ (NetworkSiteTypeService as any).onBeforeUpdate(
345
+ updateBy({ projectId: OTHER_PROJECT_ID }),
346
+ ),
347
+ ).rejects.toThrow("cannot be moved to another project");
348
+ });
349
+
350
+ it("accepts a valid ID parent move and places the type last among its new siblings", async () => {
351
+ mockParentUpdate({
352
+ parent: makeType({ index: 4 }),
353
+ siblings: [
354
+ makeType({ index: 6, order: 2 }),
355
+ makeType({ index: 7, order: 5 }),
356
+ ],
357
+ });
358
+ jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([]);
359
+ const input: UpdateBy<NetworkSiteType> = updateBy({
360
+ parentNetworkSiteTypeId: typeId(4),
361
+ });
362
+
363
+ await (NetworkSiteTypeService as any).onBeforeUpdate(input);
364
+
365
+ expect(input.data.order).toBe(6);
366
+ });
367
+
368
+ it("accepts the relation-object parent spelling on update", async () => {
369
+ const parent: NetworkSiteType = makeType({ index: 4 });
370
+ mockParentUpdate({ parent });
371
+ jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue([]);
372
+
373
+ await expect(
374
+ (NetworkSiteTypeService as any).onBeforeUpdate(
375
+ updateBy({ parentNetworkSiteType: parent }),
376
+ ),
377
+ ).resolves.toBeDefined();
378
+ });
379
+
380
+ it("rejects conflicting relation and ID values before reading target rows", async () => {
381
+ const findBySpy: jest.SpyInstance = jest.spyOn(
382
+ NetworkSiteTypeService,
383
+ "findBy",
384
+ );
385
+
386
+ await expect(
387
+ (NetworkSiteTypeService as any).onBeforeUpdate(
388
+ updateBy({
389
+ parentNetworkSiteTypeId: typeId(3),
390
+ parentNetworkSiteType: makeType({ index: 4 }),
391
+ }),
392
+ ),
393
+ ).rejects.toThrow("Conflicting parent Network Site Type references");
394
+ expect(findBySpy).not.toHaveBeenCalled();
395
+ });
396
+
397
+ it("rejects self-parenting", async () => {
398
+ mockParentUpdate({ moving: makeType({ index: 2 }) });
399
+
400
+ await expect(
401
+ (NetworkSiteTypeService as any).onBeforeUpdate(
402
+ updateBy({ parentNetworkSiteTypeId: typeId(2) }),
403
+ ),
404
+ ).rejects.toThrow("cannot be its own parent");
405
+ });
406
+
407
+ it("rejects moving a type under its direct child", async () => {
408
+ const moving: NetworkSiteType = makeType({ index: 2 });
409
+ const child: NetworkSiteType = makeType({
410
+ index: 3,
411
+ parentIndex: 2,
412
+ });
413
+ mockParentUpdate({
414
+ moving,
415
+ parent: child,
416
+ projectTypes: [moving, child],
417
+ });
418
+
419
+ await expect(
420
+ (NetworkSiteTypeService as any).onBeforeUpdate(
421
+ updateBy({ parentNetworkSiteTypeId: typeId(3) }),
422
+ ),
423
+ ).rejects.toThrow("cannot be moved under one of its descendants");
424
+ });
425
+
426
+ it("rejects moving a type under a deeper descendant", async () => {
427
+ const moving: NetworkSiteType = makeType({ index: 2 });
428
+ const child: NetworkSiteType = makeType({ index: 3, parentIndex: 2 });
429
+ const grandchild: NetworkSiteType = makeType({
430
+ index: 4,
431
+ parentIndex: 3,
432
+ });
433
+ mockParentUpdate({
434
+ moving,
435
+ parent: grandchild,
436
+ projectTypes: [moving, child, grandchild],
437
+ });
438
+
439
+ await expect(
440
+ (NetworkSiteTypeService as any).onBeforeUpdate(
441
+ updateBy({ parentNetworkSiteTypeId: typeId(4) }),
442
+ ),
443
+ ).rejects.toThrow("cannot be moved under one of its descendants");
444
+ });
445
+
446
+ it("rejects a missing, cross-project, or unit-level parent", async () => {
447
+ const cases: Array<{ parent: NetworkSiteType | null; message: string }> = [
448
+ { parent: null, message: "not found" },
449
+ {
450
+ parent: makeType({ index: 4, projectId: OTHER_PROJECT_ID }),
451
+ message: "same project",
452
+ },
453
+ {
454
+ parent: makeType({ index: 4, isUnitLevel: true }),
455
+ message: "unit-level",
456
+ },
457
+ ];
458
+
459
+ for (const testCase of cases) {
460
+ jest.restoreAllMocks();
461
+ mockParentUpdate({ parent: testCase.parent });
462
+
463
+ await expect(
464
+ (NetworkSiteTypeService as any).onBeforeUpdate(
465
+ updateBy({ parentNetworkSiteTypeId: typeId(4) }),
466
+ ),
467
+ ).rejects.toThrow(testCase.message);
468
+ }
469
+ });
470
+
471
+ it("allows detaching a type when all existing sites of that type are roots", async () => {
472
+ mockParentUpdate({});
473
+ jest
474
+ .spyOn(NetworkSiteService, "findBy")
475
+ .mockResolvedValue([makeSite({ index: 1, typeIndex: 2 })]);
476
+
477
+ await expect(
478
+ (NetworkSiteTypeService as any).onBeforeUpdate(
479
+ updateBy({ parentNetworkSiteTypeId: null }),
480
+ ),
481
+ ).resolves.toBeDefined();
482
+ });
483
+
484
+ it("rejects detaching a type while one of its sites still has a parent", async () => {
485
+ mockParentUpdate({});
486
+ jest
487
+ .spyOn(NetworkSiteService, "findBy")
488
+ .mockResolvedValue([
489
+ makeSite({ index: 1, typeIndex: 2, parentIndex: 10 }),
490
+ ]);
491
+
492
+ await expect(
493
+ (NetworkSiteTypeService as any).onBeforeUpdate(
494
+ updateBy({ parentNetworkSiteTypeId: null }),
495
+ ),
496
+ ).rejects.toThrow(
497
+ "Create a new site type under the desired parent, then move and reassign the sites to it",
498
+ );
499
+ });
500
+
501
+ it("accepts a new parent type when every site's actual parent has that type", async () => {
502
+ mockParentUpdate({ parent: makeType({ index: 4 }) });
503
+ jest
504
+ .spyOn(NetworkSiteService, "findBy")
505
+ .mockImplementation(async (findBy: any) => {
506
+ if (findBy.query?._id) {
507
+ return [makeSite({ index: 10, typeIndex: 4 })];
508
+ }
509
+
510
+ return [
511
+ makeSite({ index: 1, typeIndex: 2, parentIndex: 10 }),
512
+ makeSite({ index: 2, typeIndex: 2, parentIndex: 10 }),
513
+ ];
514
+ });
515
+
516
+ await expect(
517
+ (NetworkSiteTypeService as any).onBeforeUpdate(
518
+ updateBy({ parentNetworkSiteTypeId: typeId(4) }),
519
+ ),
520
+ ).resolves.toBeDefined();
521
+ });
522
+
523
+ it("rejects a new parent when an existing site is root or its parent has another type", async () => {
524
+ const cases: Array<{
525
+ sites: Array<NetworkSite>;
526
+ parentSites: Array<NetworkSite>;
527
+ }> = [
528
+ {
529
+ sites: [makeSite({ index: 1, typeIndex: 2 })],
530
+ parentSites: [],
531
+ },
532
+ {
533
+ sites: [makeSite({ index: 1, typeIndex: 2, parentIndex: 10 })],
534
+ parentSites: [makeSite({ index: 10, typeIndex: 9 })],
535
+ },
536
+ ];
537
+
538
+ for (const testCase of cases) {
539
+ jest.restoreAllMocks();
540
+ mockParentUpdate({ parent: makeType({ index: 4 }) });
541
+ jest
542
+ .spyOn(NetworkSiteService, "findBy")
543
+ .mockImplementation(async (findBy: any) => {
544
+ return findBy.query?._id ? testCase.parentSites : testCase.sites;
545
+ });
546
+
547
+ await expect(
548
+ (NetworkSiteTypeService as any).onBeforeUpdate(
549
+ updateBy({ parentNetworkSiteTypeId: typeId(4) }),
550
+ ),
551
+ ).rejects.toThrow("Create a new site type under the desired parent");
552
+ }
553
+ });
554
+
555
+ it("does not revalidate sites or reorder when the parent did not change", async () => {
556
+ const moving: NetworkSiteType = makeType({ index: 2, parentIndex: 4 });
557
+ mockParentUpdate({ moving, parent: makeType({ index: 4 }) });
558
+ const siteFindSpy: jest.SpyInstance = jest.spyOn(
559
+ NetworkSiteService,
560
+ "findBy",
561
+ );
562
+ const input: UpdateBy<NetworkSiteType> = updateBy({
563
+ parentNetworkSiteTypeId: typeId(4),
564
+ });
565
+
566
+ await (NetworkSiteTypeService as any).onBeforeUpdate(input);
567
+
568
+ expect(siteFindSpy).not.toHaveBeenCalled();
569
+ expect(input.data.order).toBeUndefined();
570
+ });
571
+
572
+ it("does no hierarchy reads for an update that touches neither parent nor unit-level", async () => {
573
+ const findBySpy: jest.SpyInstance = jest.spyOn(
574
+ NetworkSiteTypeService,
575
+ "findBy",
576
+ );
577
+
578
+ await expect(
579
+ (NetworkSiteTypeService as any).onBeforeUpdate(
580
+ updateBy({ name: "Renamed" }),
581
+ ),
582
+ ).resolves.toBeDefined();
583
+ expect(findBySpy).not.toHaveBeenCalled();
584
+ });
585
+
586
+ it("rejects a raw SQL isUnitLevel expression before it can bypass leaf validation", async () => {
587
+ const findBySpy: jest.SpyInstance = jest.spyOn(
588
+ NetworkSiteTypeService,
589
+ "findBy",
590
+ );
591
+
592
+ await expect(
593
+ (NetworkSiteTypeService as any).onBeforeUpdate(
594
+ updateBy({
595
+ isUnitLevel: () => {
596
+ return true;
597
+ },
598
+ }),
599
+ ),
600
+ ).rejects.toThrow("unit-level leaf rules must be validated");
601
+ expect(findBySpy).not.toHaveBeenCalled();
602
+ });
603
+
604
+ it("reapplies the caller tenant before resolving rows matched by an update", async () => {
605
+ const findBySpy: jest.SpyInstance = jest
606
+ .spyOn(NetworkSiteTypeService, "findBy")
607
+ .mockResolvedValue([]);
608
+
609
+ await (NetworkSiteTypeService as any).onBeforeUpdate(
610
+ updateBy(
611
+ { parentNetworkSiteTypeId: null },
612
+ { tenantId: PROJECT_ID, isRoot: false },
613
+ ),
614
+ );
615
+
616
+ expect(findBySpy.mock.calls[0]![0].query).toEqual(
617
+ expect.objectContaining({ projectId: PROJECT_ID }),
618
+ );
619
+ });
620
+
621
+ it("honors the update limit and skip when selecting rows to validate", async () => {
622
+ const findBySpy: jest.SpyInstance = jest
623
+ .spyOn(NetworkSiteTypeService, "findBy")
624
+ .mockResolvedValue([]);
625
+ const input: UpdateBy<NetworkSiteType> = updateBy({
626
+ parentNetworkSiteTypeId: null,
627
+ });
628
+ input.limit = new PositiveNumber(7);
629
+ input.skip = new PositiveNumber(3);
630
+
631
+ await (NetworkSiteTypeService as any).onBeforeUpdate(input);
632
+
633
+ expect(findBySpy).toHaveBeenCalledWith(
634
+ expect.objectContaining({ limit: 7, skip: 3 }),
635
+ );
636
+ });
637
+
638
+ it("rejects making a type unit-level when it has child types", async () => {
639
+ const moving: NetworkSiteType = makeType({
640
+ index: 2,
641
+ isUnitLevel: false,
642
+ });
643
+ jest.spyOn(NetworkSiteTypeService, "findBy").mockResolvedValue([moving]);
644
+ jest
645
+ .spyOn(NetworkSiteTypeService, "findOneBy")
646
+ .mockResolvedValue(makeType({ index: 3, parentIndex: 2 }));
647
+
648
+ await expect(
649
+ (NetworkSiteTypeService as any).onBeforeUpdate(
650
+ updateBy({ isUnitLevel: true }),
651
+ ),
652
+ ).rejects.toThrow("with child types cannot be made unit-level");
653
+ });
654
+
655
+ it("rejects making a type unit-level when one of its sites has child sites", async () => {
656
+ const moving: NetworkSiteType = makeType({
657
+ index: 2,
658
+ isUnitLevel: false,
659
+ });
660
+ jest.spyOn(NetworkSiteTypeService, "findBy").mockResolvedValue([moving]);
661
+ jest.spyOn(NetworkSiteTypeService, "findOneBy").mockResolvedValue(null);
662
+ jest
663
+ .spyOn(NetworkSiteService, "findBy")
664
+ .mockResolvedValue([makeSite({ index: 1, typeIndex: 2 })]);
665
+ const childSiteFindSpy: jest.SpyInstance = jest
666
+ .spyOn(NetworkSiteService, "findOneBy")
667
+ .mockResolvedValue(makeSite({ index: 2, parentIndex: 1 }));
668
+
669
+ await expect(
670
+ (NetworkSiteTypeService as any).onBeforeUpdate(
671
+ updateBy({ isUnitLevel: true }),
672
+ ),
673
+ ).rejects.toThrow("whose sites have child sites cannot be made unit-level");
674
+
675
+ expect(childSiteFindSpy.mock.calls[0]![0].query.projectId).toBeDefined();
676
+ });
677
+
678
+ it("batches child-site checks when a type is used by more than 1,000 sites", async () => {
679
+ const moving: NetworkSiteType = makeType({
680
+ index: 2,
681
+ isUnitLevel: false,
682
+ });
683
+ jest.spyOn(NetworkSiteTypeService, "findBy").mockResolvedValue([moving]);
684
+ jest.spyOn(NetworkSiteTypeService, "findOneBy").mockResolvedValue(null);
685
+ jest.spyOn(NetworkSiteService, "findBy").mockResolvedValue(
686
+ Array.from({ length: 1001 }, (_value: unknown, index: number) => {
687
+ return makeSite({ index: index + 1, typeIndex: 2 });
688
+ }),
689
+ );
690
+ const childSiteFindSpy: jest.SpyInstance = jest
691
+ .spyOn(NetworkSiteService, "findOneBy")
692
+ .mockResolvedValueOnce(null)
693
+ .mockResolvedValueOnce(makeSite({ index: 2000, parentIndex: 1001 }));
694
+
695
+ await expect(
696
+ (NetworkSiteTypeService as any).onBeforeUpdate(
697
+ updateBy({ isUnitLevel: true }),
698
+ ),
699
+ ).rejects.toThrow("whose sites have child sites cannot be made unit-level");
700
+
701
+ expect(childSiteFindSpy).toHaveBeenCalledTimes(2);
702
+ });
703
+
704
+ it("does not rerun transition checks for a type that is already unit-level", async () => {
705
+ jest
706
+ .spyOn(NetworkSiteTypeService, "findBy")
707
+ .mockResolvedValue([makeType({ index: 2, isUnitLevel: true })]);
708
+ const childTypeFindSpy: jest.SpyInstance = jest.spyOn(
709
+ NetworkSiteTypeService,
710
+ "findOneBy",
711
+ );
712
+ const siteFindSpy: jest.SpyInstance = jest.spyOn(
713
+ NetworkSiteService,
714
+ "findBy",
715
+ );
716
+
717
+ await expect(
718
+ (NetworkSiteTypeService as any).onBeforeUpdate(
719
+ updateBy({ isUnitLevel: true }),
720
+ ),
721
+ ).resolves.toBeDefined();
722
+
723
+ expect(childTypeFindSpy).not.toHaveBeenCalled();
724
+ expect(siteFindSpy).not.toHaveBeenCalled();
725
+ });
726
+
727
+ it("validates site parent consistency beyond the first 10,000 rows", async () => {
728
+ mockParentUpdate({});
729
+ const rootSite: NetworkSite = makeSite({ index: 1, typeIndex: 2 });
730
+ const mismatch: NetworkSite = makeSite({
731
+ index: 2,
732
+ typeIndex: 2,
733
+ parentIndex: 10,
734
+ });
735
+ jest
736
+ .spyOn(NetworkSiteService, "findBy")
737
+ .mockImplementation(async (findBy: any) => {
738
+ if (findBy.skip === 0) {
739
+ return new Array<NetworkSite>(10_000).fill(rootSite);
740
+ }
741
+
742
+ return [mismatch];
743
+ });
744
+
745
+ await expect(
746
+ (NetworkSiteTypeService as any).onBeforeUpdate(
747
+ updateBy({ parentNetworkSiteTypeId: null }),
748
+ ),
749
+ ).rejects.toThrow("Create a new site type under the desired parent");
750
+
751
+ expect(NetworkSiteService.findBy).toHaveBeenCalledWith(
752
+ expect.objectContaining({ skip: 10_000 }),
753
+ );
754
+ });
755
+ });
756
+
757
+ describe("NetworkSiteTypeService deletion guards", () => {
758
+ function mockDeleteTarget(): jest.SpyInstance {
759
+ return jest
760
+ .spyOn(NetworkSiteTypeService, "findBy")
761
+ .mockImplementation(async (findBy: any) => {
762
+ return findBy.query?._id ? [makeType({ index: 2 })] : [];
763
+ });
764
+ }
765
+
766
+ it("rejects deleting a type used as another type's parent", async () => {
767
+ jest
768
+ .spyOn(NetworkSiteTypeService, "findBy")
769
+ .mockImplementation(async (findBy: any) => {
770
+ return findBy.query?._id
771
+ ? [makeType({ index: 2 })]
772
+ : [makeType({ index: 3, parentIndex: 2 })];
773
+ });
774
+
775
+ await expect(
776
+ (NetworkSiteTypeService as any).onBeforeDelete(deleteBy()),
777
+ ).rejects.toThrow("while child types use it as their parent");
778
+ });
779
+
780
+ it("rejects deleting a type used by a Network Site", async () => {
781
+ mockDeleteTarget();
782
+ jest
783
+ .spyOn(NetworkSiteService, "findOneBy")
784
+ .mockResolvedValue(makeSite({ index: 1, typeIndex: 2 }));
785
+
786
+ await expect(
787
+ (NetworkSiteTypeService as any).onBeforeDelete(deleteBy()),
788
+ ).rejects.toThrow("while Network Sites use it");
789
+ });
790
+
791
+ it("allows deleting an unused leaf type", async () => {
792
+ const findBySpy: jest.SpyInstance = mockDeleteTarget();
793
+ jest.spyOn(NetworkSiteService, "findOneBy").mockResolvedValue(null);
794
+
795
+ await expect(
796
+ (NetworkSiteTypeService as any).onBeforeDelete(deleteBy()),
797
+ ).resolves.toBeDefined();
798
+
799
+ const childQueryCall: Array<any> = findBySpy.mock.calls.find(
800
+ (call: Array<any>) => {
801
+ return Boolean(call[0].query?.parentNetworkSiteTypeId);
802
+ },
803
+ )!;
804
+ expect(childQueryCall[0].query.projectId).toBeDefined();
805
+ });
806
+
807
+ it("allows a parent and its child type to be deleted together", async () => {
808
+ const parent: NetworkSiteType = makeType({ index: 2 });
809
+ const child: NetworkSiteType = makeType({ index: 3, parentIndex: 2 });
810
+ jest
811
+ .spyOn(NetworkSiteTypeService, "findBy")
812
+ .mockImplementation(async (findBy: any) => {
813
+ return findBy.query?.parentNetworkSiteTypeId
814
+ ? [child]
815
+ : [parent, child];
816
+ });
817
+ jest.spyOn(NetworkSiteService, "findOneBy").mockResolvedValue(null);
818
+ const input: DeleteBy<NetworkSiteType> = deleteBy();
819
+ input.query = { projectId: PROJECT_ID };
820
+ input.limit = new PositiveNumber(2);
821
+
822
+ await expect(
823
+ (NetworkSiteTypeService as any).onBeforeDelete(input),
824
+ ).resolves.toBeDefined();
825
+ });
826
+
827
+ it("honors the delete limit and skip when determining the deletion set", async () => {
828
+ const findBySpy: jest.SpyInstance = jest
829
+ .spyOn(NetworkSiteTypeService, "findBy")
830
+ .mockImplementation(async (findBy: any) => {
831
+ return findBy.query?._id ? [makeType({ index: 2 })] : [];
832
+ });
833
+ jest.spyOn(NetworkSiteService, "findOneBy").mockResolvedValue(null);
834
+ const input: DeleteBy<NetworkSiteType> = deleteBy();
835
+ input.limit = new PositiveNumber(7);
836
+ input.skip = new PositiveNumber(3);
837
+
838
+ await (NetworkSiteTypeService as any).onBeforeDelete(input);
839
+
840
+ expect(findBySpy.mock.calls[0]![0]).toEqual(
841
+ expect.objectContaining({ limit: 7, skip: 3 }),
842
+ );
843
+ });
844
+
845
+ it("reapplies the caller tenant before resolving rows matched by a delete", async () => {
846
+ const findBySpy: jest.SpyInstance = jest
847
+ .spyOn(NetworkSiteTypeService, "findBy")
848
+ .mockResolvedValue([]);
849
+
850
+ await (NetworkSiteTypeService as any).onBeforeDelete(
851
+ deleteBy({ tenantId: PROJECT_ID, isRoot: false }),
852
+ );
853
+
854
+ expect(findBySpy.mock.calls[0]![0].query).toEqual(
855
+ expect.objectContaining({ projectId: PROJECT_ID }),
856
+ );
857
+ });
858
+
859
+ it("matches root delete preflight scoping when a tenant is supplied", async () => {
860
+ const findBySpy: jest.SpyInstance = jest
861
+ .spyOn(NetworkSiteTypeService, "findBy")
862
+ .mockResolvedValue([]);
863
+
864
+ await (NetworkSiteTypeService as any).onBeforeDelete(
865
+ deleteBy({ tenantId: PROJECT_ID, isRoot: true }),
866
+ );
867
+
868
+ expect(findBySpy.mock.calls[0]![0].query).toEqual(
869
+ expect.objectContaining({ projectId: PROJECT_ID }),
870
+ );
871
+ });
872
+
873
+ it("does not invent a tenant scope for a root multi-tenant delete preflight", async () => {
874
+ const findBySpy: jest.SpyInstance = jest
875
+ .spyOn(NetworkSiteTypeService, "findBy")
876
+ .mockResolvedValue([]);
877
+
878
+ await (NetworkSiteTypeService as any).onBeforeDelete(
879
+ deleteBy({
880
+ tenantId: PROJECT_ID,
881
+ isRoot: true,
882
+ isMultiTenantRequest: true,
883
+ }),
884
+ );
885
+
886
+ expect(findBySpy.mock.calls[0]![0].query.projectId).toBeUndefined();
887
+ });
888
+ });
889
+
890
+ describe("NetworkSiteTypeService hierarchy mutation lock", () => {
891
+ const runThroughLock: (data: {
892
+ operation: () => Promise<unknown>;
893
+ }) => Promise<unknown> = async (data: {
894
+ operation: () => Promise<unknown>;
895
+ }): Promise<unknown> => {
896
+ return await data.operation();
897
+ };
898
+
899
+ it("holds the shared project lock around create", async () => {
900
+ const created: NetworkSiteType = makeType({ index: 9 });
901
+ const runExclusiveSpy: jest.SpyInstance = jest
902
+ .spyOn(NetworkSiteHierarchyLock, "runExclusive")
903
+ .mockImplementation(runThroughLock as never);
904
+ const superCreateSpy: jest.SpyInstance = jest
905
+ .spyOn(DatabaseService.prototype, "create")
906
+ .mockResolvedValue(created);
907
+
908
+ await expect(NetworkSiteTypeService.create(createBy({}))).resolves.toBe(
909
+ created,
910
+ );
911
+
912
+ expect(runExclusiveSpy).toHaveBeenCalledWith(
913
+ expect.objectContaining({ projectIds: [PROJECT_ID] }),
914
+ );
915
+ expect(runExclusiveSpy.mock.invocationCallOrder[0]!).toBeLessThan(
916
+ superCreateSpy.mock.invocationCallOrder[0]!,
917
+ );
918
+ });
919
+
920
+ it.each([
921
+ ["updateOneBy", { parentNetworkSiteTypeId: typeId(1) }],
922
+ ["updateBy", { isUnitLevel: true }],
923
+ ["updateOneBy", { projectId: PROJECT_ID }],
924
+ ])(
925
+ "locks a hierarchy-changing %s",
926
+ async (method: string, data: Record<string, unknown>) => {
927
+ const runExclusiveSpy: jest.SpyInstance = jest
928
+ .spyOn(NetworkSiteHierarchyLock, "runExclusive")
929
+ .mockImplementation(runThroughLock as never);
930
+ const findBySpy: jest.SpyInstance = jest
931
+ .spyOn(NetworkSiteTypeService, "findBy")
932
+ .mockResolvedValue([makeType({ index: 2 })]);
933
+ const superMethodSpy: jest.SpyInstance = jest
934
+ .spyOn(DatabaseService.prototype as any, method)
935
+ .mockResolvedValue(1);
936
+ const input: Record<string, unknown> = {
937
+ query: { _id: typeId(2).toString() },
938
+ data,
939
+ props: { isRoot: true },
940
+ };
941
+
942
+ if (method === "updateBy") {
943
+ input["limit"] = new PositiveNumber(1);
944
+ input["skip"] = new PositiveNumber(0);
945
+ }
946
+
947
+ await expect(
948
+ (NetworkSiteTypeService as any)[method](input),
949
+ ).resolves.toBe(1);
950
+
951
+ expect(findBySpy).toHaveBeenCalledWith(
952
+ expect.objectContaining({ select: { projectId: true } }),
953
+ );
954
+ expect(runExclusiveSpy).toHaveBeenCalledWith(
955
+ expect.objectContaining({ projectIds: [PROJECT_ID] }),
956
+ );
957
+ expect(superMethodSpy).toHaveBeenCalledTimes(1);
958
+ },
959
+ );
960
+
961
+ it.each(["deleteOneBy", "deleteBy", "hardDeleteBy"])(
962
+ "locks %s and resolves all affected projects first",
963
+ async (method: string) => {
964
+ const runExclusiveSpy: jest.SpyInstance = jest
965
+ .spyOn(NetworkSiteHierarchyLock, "runExclusive")
966
+ .mockImplementation(runThroughLock as never);
967
+ jest
968
+ .spyOn(NetworkSiteTypeService, "findBy")
969
+ .mockResolvedValue([
970
+ makeType({ index: 2, projectId: OTHER_PROJECT_ID }),
971
+ makeType({ index: 3, projectId: PROJECT_ID }),
972
+ ]);
973
+ const superMethodSpy: jest.SpyInstance = jest
974
+ .spyOn(DatabaseService.prototype as any, method)
975
+ .mockResolvedValue(2);
976
+ const input: Record<string, unknown> = {
977
+ query: { projectId: PROJECT_ID },
978
+ props: { isRoot: true },
979
+ };
980
+
981
+ if (method !== "deleteOneBy") {
982
+ input["limit"] = new PositiveNumber(2);
983
+ input["skip"] = new PositiveNumber(0);
984
+ }
985
+
986
+ await expect(
987
+ (NetworkSiteTypeService as any)[method](input),
988
+ ).resolves.toBe(2);
989
+
990
+ expect(runExclusiveSpy).toHaveBeenCalledWith(
991
+ expect.objectContaining({
992
+ projectIds: [OTHER_PROJECT_ID, PROJECT_ID],
993
+ }),
994
+ );
995
+ expect(superMethodSpy).toHaveBeenCalledTimes(1);
996
+ },
997
+ );
998
+
999
+ it("bypasses locking for unrelated updates and trusted ignoreHooks writes", async () => {
1000
+ const runExclusiveSpy: jest.SpyInstance = jest.spyOn(
1001
+ NetworkSiteHierarchyLock,
1002
+ "runExclusive",
1003
+ );
1004
+ jest.spyOn(DatabaseService.prototype, "updateOneBy").mockResolvedValue(1);
1005
+ jest
1006
+ .spyOn(DatabaseService.prototype, "create")
1007
+ .mockResolvedValue(makeType({ index: 7 }));
1008
+
1009
+ await NetworkSiteTypeService.updateOneBy({
1010
+ query: { _id: typeId(2).toString() },
1011
+ data: { name: "Renamed" },
1012
+ props: { isRoot: true },
1013
+ });
1014
+ await NetworkSiteTypeService.create({
1015
+ ...createBy({}),
1016
+ props: { isRoot: true, ignoreHooks: true },
1017
+ });
1018
+
1019
+ expect(runExclusiveSpy).not.toHaveBeenCalled();
1020
+ });
1021
+
1022
+ it("rejects an unscoped root hierarchy bulk mutation before writing", async () => {
1023
+ const superUpdateSpy: jest.SpyInstance = jest.spyOn(
1024
+ DatabaseService.prototype,
1025
+ "updateBy",
1026
+ );
1027
+
1028
+ await expect(
1029
+ NetworkSiteTypeService.updateBy({
1030
+ query: { name: "Any matching type" },
1031
+ data: { parentNetworkSiteTypeId: null },
1032
+ limit: new PositiveNumber(10),
1033
+ skip: new PositiveNumber(0),
1034
+ props: { isRoot: true },
1035
+ }),
1036
+ ).rejects.toThrow(NETWORK_SITE_HIERARCHY_ROOT_SCOPE_ERROR_MESSAGE);
1037
+
1038
+ expect(superUpdateSpy).not.toHaveBeenCalled();
1039
+ });
1040
+
1041
+ it("rejects an open-ended root update even when root supplies tenantId", async () => {
1042
+ const superUpdateSpy: jest.SpyInstance = jest.spyOn(
1043
+ DatabaseService.prototype,
1044
+ "updateBy",
1045
+ );
1046
+
1047
+ await expect(
1048
+ NetworkSiteTypeService.updateBy({
1049
+ query: { name: "Any matching type" },
1050
+ data: { parentNetworkSiteTypeId: null },
1051
+ limit: new PositiveNumber(10),
1052
+ skip: new PositiveNumber(0),
1053
+ props: { isRoot: true, tenantId: PROJECT_ID },
1054
+ }),
1055
+ ).rejects.toThrow(NETWORK_SITE_HIERARCHY_ROOT_SCOPE_ERROR_MESSAGE);
1056
+
1057
+ expect(superUpdateSpy).not.toHaveBeenCalled();
1058
+ });
1059
+
1060
+ it("turns the retention cron's open root query into an unused leaf-only ID batch", async () => {
1061
+ const retentionQuery: any = { deletedAt: { olderThanThirtyDays: true } };
1062
+ const parent: NetworkSiteType = makeType({ index: 2 });
1063
+ const leaf: NetworkSiteType = makeType({ index: 3, parentIndex: 2 });
1064
+ const usedLeaf: NetworkSiteType = makeType({ index: 4 });
1065
+
1066
+ jest
1067
+ .spyOn(NetworkSiteTypeService, "findBy")
1068
+ .mockImplementation(async (findBy: any) => {
1069
+ if (findBy.query?.parentNetworkSiteTypeId) {
1070
+ return [leaf];
1071
+ }
1072
+
1073
+ if (findBy.skip > 0) {
1074
+ return [];
1075
+ }
1076
+
1077
+ return [parent, leaf, usedLeaf];
1078
+ });
1079
+ jest
1080
+ .spyOn(NetworkSiteService, "findBy")
1081
+ .mockResolvedValue([makeSite({ index: 1, typeIndex: 4 })]);
1082
+ const runExclusiveSpy: jest.SpyInstance = jest
1083
+ .spyOn(NetworkSiteHierarchyLock, "runExclusive")
1084
+ .mockImplementation(runThroughLock as never);
1085
+ const superHardDeleteSpy: jest.SpyInstance = jest
1086
+ .spyOn(DatabaseService.prototype, "hardDeleteBy")
1087
+ .mockResolvedValue(1);
1088
+
1089
+ await expect(
1090
+ NetworkSiteTypeService.hardDeleteBy({
1091
+ query: retentionQuery,
1092
+ limit: new PositiveNumber(3),
1093
+ skip: new PositiveNumber(0),
1094
+ props: { isRoot: true },
1095
+ } as DeleteBy<NetworkSiteType>),
1096
+ ).resolves.toBe(1);
1097
+
1098
+ expect(runExclusiveSpy).toHaveBeenCalledWith(
1099
+ expect.objectContaining({ projectIds: [PROJECT_ID] }),
1100
+ );
1101
+ const closedDelete: any = superHardDeleteSpy.mock.calls[0]![0];
1102
+ expect(closedDelete.query.deletedAt).toBe(retentionQuery.deletedAt);
1103
+ expect(
1104
+ Object.values(closedDelete.query._id.objectLiteralParameters)[0],
1105
+ ).toEqual([leaf.id!.toString()]);
1106
+ expect(closedDelete.limit).toBe(1);
1107
+ expect(closedDelete.skip).toBe(0);
1108
+ });
1109
+ });