@oneuptime/common 12.0.33 → 13.0.0

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 (877) hide show
  1. package/Models/AnalyticsModels/RumSession.ts +117 -1
  2. package/Models/AnalyticsModels/RumSessionChunk.ts +13 -0
  3. package/Models/DatabaseModels/AlertCustomField.ts +77 -3
  4. package/Models/DatabaseModels/ApiKeyPermission.ts +3 -27
  5. package/Models/DatabaseModels/GlobalConfig.ts +12 -12
  6. package/Models/DatabaseModels/IncidentCustomField.ts +77 -3
  7. package/Models/DatabaseModels/InventoryItem.ts +44 -1
  8. package/Models/DatabaseModels/InventoryItemCustomField.ts +77 -3
  9. package/Models/DatabaseModels/MonitorCustomField.ts +77 -3
  10. package/Models/DatabaseModels/NetworkDevice.ts +126 -0
  11. package/Models/DatabaseModels/OnCallDutyPolicyCustomField.ts +77 -3
  12. package/Models/DatabaseModels/RumApplication.ts +6 -6
  13. package/Models/DatabaseModels/ScheduledMaintenanceCustomField.ts +77 -3
  14. package/Models/DatabaseModels/StatusPageCustomField.ts +77 -3
  15. package/Models/DatabaseModels/TeamCustomField.ts +77 -3
  16. package/Models/DatabaseModels/TeamMember.ts +0 -9
  17. package/Models/DatabaseModels/TeamMemberCustomField.ts +77 -3
  18. package/Models/DatabaseModels/TeamPermission.ts +3 -24
  19. package/Models/DatabaseModels/TelemetryIngestionKey.ts +260 -0
  20. package/Models/DatabaseModels/UserTelegram.ts +0 -4
  21. package/Scripts/benchmark-fanin-capacity.js +181 -0
  22. package/Server/API/TelemetryAPI.ts +1284 -164
  23. package/Server/API/UserNotificationSettingAPI.ts +55 -0
  24. package/Server/API/UserTelegramAPI.ts +48 -4
  25. package/Server/EnvironmentConfig.ts +82 -18
  26. package/Server/Infrastructure/Postgres/SchemaMigrations/1791300000000-AddTelemetryIngestionKeyType.ts +91 -0
  27. package/Server/Infrastructure/Postgres/SchemaMigrations/1791400000000-SessionReplayRecordEverySessionByDefault.ts +67 -0
  28. package/Server/Infrastructure/Postgres/SchemaMigrations/1791500000000-WidenCustomFieldDropdownOptions.ts +139 -0
  29. package/Server/Infrastructure/Postgres/SchemaMigrations/1791600000000-AddCustomFieldValueMapping.ts +121 -0
  30. package/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.ts +25 -0
  31. package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
  32. package/Server/Infrastructure/QueueWorker.ts +20 -14
  33. package/Server/Infrastructure/Redis.ts +8 -8
  34. package/Server/Infrastructure/Semaphore.ts +2 -0
  35. package/Server/Infrastructure/Status.ts +10 -4
  36. package/Server/Middleware/TelemetryIngest.ts +453 -7
  37. package/Server/Services/AccessTokenService.ts +1 -0
  38. package/Server/Services/AlertCustomFieldService.ts +98 -0
  39. package/Server/Services/AlertService.ts +37 -0
  40. package/Server/Services/ApiKeyPermissionService.ts +461 -29
  41. package/Server/Services/CustomFieldMappingService.ts +879 -0
  42. package/Server/Services/DatabaseService.ts +65 -13
  43. package/Server/Services/GlobalConfigService.ts +194 -0
  44. package/Server/Services/IncidentCustomFieldService.ts +98 -0
  45. package/Server/Services/IncidentService.ts +35 -0
  46. package/Server/Services/InventoryItemService.ts +31 -1
  47. package/Server/Services/MetricService.ts +194 -0
  48. package/Server/Services/MonitorService.ts +32 -0
  49. package/Server/Services/MonitorTemplateService.ts +140 -19
  50. package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +427 -66
  51. package/Server/Services/NetworkDeviceDiscoveryScanService.ts +87 -2
  52. package/Server/Services/NetworkDeviceService.ts +69 -0
  53. package/Server/Services/ProjectService.ts +22 -0
  54. package/Server/Services/RoutineEmailSettingsService.ts +73 -0
  55. package/Server/Services/RumSessionReplayViewService.ts +104 -24
  56. package/Server/Services/ScheduledMaintenanceCustomFieldService.ts +98 -0
  57. package/Server/Services/ScheduledMaintenanceService.ts +32 -0
  58. package/Server/Services/TeamMemberService.ts +78 -0
  59. package/Server/Services/TeamPermissionService.ts +235 -3
  60. package/Server/Services/TelemetryIngestionKeyService.ts +722 -19
  61. package/Server/Services/UserNotificationRuleService.ts +76 -25
  62. package/Server/Services/UserNotificationSettingService.ts +34 -6
  63. package/Server/Services/UserTelegramService.ts +326 -5
  64. package/Server/Types/Database/Permissions/ReadPermission.ts +45 -5
  65. package/Server/Types/Database/QueryHelper.ts +2 -0
  66. package/Server/Types/Database/QueryUtil.ts +56 -0
  67. package/Server/Types/Markdown.ts +93 -1
  68. package/Server/Utils/APIKey/AccessPermission.ts +5 -2
  69. package/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.ts +54 -0
  70. package/Server/Utils/CustomField/CustomFieldMappingRegistry.ts +415 -0
  71. package/Server/Utils/CustomField/CustomFieldMappingValidator.ts +335 -0
  72. package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +82 -17
  73. package/Server/Utils/DataSource/EgressGuard.ts +177 -8
  74. package/Server/Utils/EmailRollup/EmailRollupConstants.ts +38 -5
  75. package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +67 -5
  76. package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +2 -2
  77. package/Server/Utils/FrontendEnvironment.ts +40 -0
  78. package/Server/Utils/LogRedaction.ts +20 -0
  79. package/Server/Utils/Logger.ts +11 -0
  80. package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +394 -86
  81. package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +30 -4
  82. package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +31 -5
  83. package/Server/Utils/Monitor/MonitorAlert.ts +29 -6
  84. package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +312 -52
  85. package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +82 -9
  86. package/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.ts +16 -7
  87. package/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.ts +29 -17
  88. package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +107 -48
  89. package/Server/Utils/Monitor/MonitorIncident.ts +22 -6
  90. package/Server/Utils/Monitor/MonitorTemplateUtil.ts +29 -0
  91. package/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.ts +158 -0
  92. package/Server/Utils/Monitor/NetworkInventoryUtil.ts +27 -0
  93. package/Server/Utils/Monitor/SeriesContextEnricher.ts +304 -0
  94. package/Server/Utils/SSRFProtection.ts +217 -10
  95. package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +239 -138
  96. package/Server/Utils/SessionReplay/SessionReplayHealthCounters.ts +305 -0
  97. package/Server/Utils/SessionReplay/SessionReplayReadService.ts +1430 -289
  98. package/Server/Utils/StartServer.ts +2 -17
  99. package/Server/Utils/TelegramVerificationToken.ts +185 -0
  100. package/Server/Utils/Telemetry/EntityRegistry.ts +11 -1
  101. package/Server/Utils/Telemetry/PinServiceName.ts +197 -0
  102. package/Server/Utils/Telemetry/TelemetryEntity.ts +83 -9
  103. package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +38 -16
  104. package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +92 -0
  105. package/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.ts +217 -0
  106. package/Server/Utils/VM/VMAPI.ts +1 -0
  107. package/Server/Utils/VM/VMRunner.ts +975 -95
  108. package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +202 -0
  109. package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +32 -0
  110. package/Tests/App/Dashboard/AdoptedDeviceDrawer.test.tsx +374 -0
  111. package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +404 -0
  112. package/Tests/App/Dashboard/DashboardLabelVariables.test.tsx +580 -0
  113. package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +438 -0
  114. package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +5 -2
  115. package/Tests/App/Dashboard/DeviceAttachmentCard.test.tsx +273 -0
  116. package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +505 -0
  117. package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +468 -0
  118. package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +163 -0
  119. package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +401 -0
  120. package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +232 -0
  121. package/Tests/App/Dashboard/InventoryItems.test.tsx +201 -0
  122. package/Tests/App/Dashboard/InventoryTypeAndStatusFacets.test.tsx +798 -0
  123. package/Tests/App/Dashboard/NetworkTopologyLiveView.test.tsx +320 -0
  124. package/Tests/App/Dashboard/NetworkTopologyToolbar.test.tsx +275 -0
  125. package/Tests/App/Dashboard/OnCallCalendarFeedEmptyState.test.tsx +975 -0
  126. package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +63 -0
  127. package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +626 -0
  128. package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +348 -0
  129. package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +331 -0
  130. package/Tests/App/Dashboard/UserSettingsEmailPreferences.test.tsx +603 -0
  131. package/Tests/App/Dashboard/UserSettingsNotificationSettings.test.tsx +324 -0
  132. package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +13 -4
  133. package/Tests/App/StatusPage/StatusPageLastUpdated.test.tsx +263 -0
  134. package/Tests/App/StatusPage/StatusPageOidcOrigin.test.tsx +223 -0
  135. package/Tests/App/StatusPage/StatusPageOverviewLiveAndSearch.test.tsx +753 -0
  136. package/Tests/App/StatusPage/StatusPageResourceSearchBox.test.tsx +224 -0
  137. package/Tests/Models/AnalyticsModels/RumSessionReplayColumns.test.ts +262 -0
  138. package/Tests/Models/CustomFieldMappingColumns.test.ts +166 -0
  139. package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +20 -6
  140. package/Tests/Models/InventoryItemStatus.test.ts +147 -0
  141. package/Tests/Models/NetworkDeviceMacAddressColumn.test.ts +294 -0
  142. package/Tests/ResponsiveVisibility.test.ts +115 -0
  143. package/Tests/ResponsiveVisibility.ts +183 -0
  144. package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +129 -0
  145. package/Tests/Server/API/SessionReplayAPI.test.ts +1940 -216
  146. package/Tests/Server/API/UserNotificationSettingAPI.test.ts +186 -0
  147. package/Tests/Server/API/UserTelegramAPISecurity.test.ts +231 -0
  148. package/Tests/Server/EnvironmentConfigFrontendSecurity.test.ts +234 -0
  149. package/Tests/Server/EnvironmentConfigValkey.test.ts +227 -0
  150. package/Tests/Server/Infrastructure/Postgres/AddMacAddressToNetworkDeviceMigration.test.ts +365 -0
  151. package/Tests/Server/Infrastructure/Postgres/CustomFieldValueMappingMigration.test.ts +241 -0
  152. package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +41 -0
  153. package/Tests/Server/Infrastructure/Postgres/SessionReplayRecordEverySessionByDefaultMigration.test.ts +169 -0
  154. package/Tests/Server/Infrastructure/QueueWorkerTimeout.test.ts +210 -0
  155. package/Tests/Server/Infrastructure/TelemetryExporterDeploymentConfig.test.ts +130 -0
  156. package/Tests/Server/Infrastructure/ValkeyDeploymentConfig.test.ts +265 -0
  157. package/Tests/Server/Middleware/HttpMetricsMiddleware.test.ts +382 -0
  158. package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -1
  159. package/Tests/Server/Middleware/SCIMAuthorization.test.ts +525 -0
  160. package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +1163 -0
  161. package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +5 -3
  162. package/Tests/Server/Services/AddTelemetryIngestionKeyTypeMigration.test.ts +555 -0
  163. package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +678 -0
  164. package/Tests/Server/Services/ApiKeyPermissionService.test.ts +276 -28
  165. package/Tests/Server/Services/CustomFieldDropdownOptionsColumnWidth.test.ts +431 -0
  166. package/Tests/Server/Services/CustomFieldMappingService.test.ts +850 -0
  167. package/Tests/Server/Services/DatabaseServiceAggregateBy.test.ts +291 -56
  168. package/Tests/Server/Services/DatabaseServiceSortTiebreaker.test.ts +151 -0
  169. package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +32 -0
  170. package/Tests/Server/Services/GlobalConfigService.test.ts +414 -1
  171. package/Tests/Server/Services/InventoryItemManualCreate.test.ts +105 -0
  172. package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +178 -0
  173. package/Tests/Server/Services/MetricRawEntityKeyPrune.test.ts +592 -0
  174. package/Tests/Server/Services/MonitorTemplateServiceCustomFieldSync.test.ts +502 -0
  175. package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +1351 -74
  176. package/Tests/Server/Services/NetworkDeviceDiscoveryScanRegistration.test.ts +520 -0
  177. package/Tests/Server/Services/NetworkDeviceMacAddressNormalization.test.ts +572 -0
  178. package/Tests/Server/Services/RoutineEmailSettingsPostgres.test.ts +329 -0
  179. package/Tests/Server/Services/RumSessionReplayViewService.test.ts +267 -0
  180. package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +9 -0
  181. package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +9 -0
  182. package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +834 -0
  183. package/Tests/Server/Services/TelemetryIngestionKeyPolicyResolution.test.ts +814 -0
  184. package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +998 -0
  185. package/Tests/Server/Services/UserNotificationSettingRollupRouting.test.ts +141 -0
  186. package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +18 -10
  187. package/Tests/Server/Services/UserTelegramVerificationSecurity.test.ts +688 -0
  188. package/Tests/Server/Types/Database/InventoryStatusPostgres.test.ts +368 -0
  189. package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +261 -14
  190. package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroups.test.ts +169 -0
  191. package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroupsPostgres.test.ts +297 -0
  192. package/Tests/Server/Types/Markdown.test.ts +194 -1
  193. package/Tests/Server/Types/Workflow/Components/IncomingWebhookUtils.test.ts +279 -0
  194. package/Tests/Server/Types/Workflow/Components/TextToJson.test.ts +212 -0
  195. package/Tests/Server/Utils/AI/SRE/Insights/FixRouting.test.ts +497 -0
  196. package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +383 -0
  197. package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +7 -3
  198. package/Tests/Server/Utils/AnalyticsDatabase/ClusterConfig.test.ts +315 -0
  199. package/Tests/Server/Utils/CustomField/CustomFieldMappingRegistry.test.ts +436 -0
  200. package/Tests/Server/Utils/CustomField/CustomFieldMappingValidator.test.ts +437 -0
  201. package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +214 -0
  202. package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +300 -11
  203. package/Tests/Server/Utils/EmailRollup/EmailRollupBurstWindow.test.ts +83 -0
  204. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +2 -2
  205. package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerPreferences.test.ts +362 -0
  206. package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +10 -3
  207. package/Tests/Server/Utils/EmailRollup/EmailRollupTestHarness.ts +77 -1
  208. package/Tests/Server/Utils/EventLoop.test.ts +359 -0
  209. package/Tests/Server/Utils/FrontendEnvironment.test.ts +190 -0
  210. package/Tests/Server/Utils/JsonWebToken.test.ts +30 -0
  211. package/Tests/Server/Utils/LogRedaction.test.ts +26 -0
  212. package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +23 -9
  213. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +242 -0
  214. package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +590 -0
  215. package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +29 -0
  216. package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestHeaderCriteria.test.ts +236 -0
  217. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +30 -6
  218. package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +246 -0
  219. package/Tests/Server/Utils/Monitor/MonitorCriteriaEmailEvalLogAgreement.test.ts +280 -0
  220. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +659 -8
  221. package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +570 -0
  222. package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilderUnits.test.ts +30 -5
  223. package/Tests/Server/Utils/Monitor/MonitorCriteriaMessageBuilderUnits.test.ts +68 -21
  224. package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +36 -19
  225. package/Tests/Server/Utils/Monitor/MonitorTemplateUtilSeriesContext.test.ts +176 -0
  226. package/Tests/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.test.ts +573 -0
  227. package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +317 -0
  228. package/Tests/Server/Utils/Monitor/SeriesContextEnricher.test.ts +356 -0
  229. package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +595 -0
  230. package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +198 -1
  231. package/Tests/Server/Utils/SessionReplay/SessionReplayHealthCounters.test.ts +453 -0
  232. package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +1378 -0
  233. package/Tests/Server/Utils/SessionReplayOriginAllowListRefactor.test.ts +420 -0
  234. package/Tests/Server/Utils/TelegramVerificationToken.test.ts +253 -0
  235. package/Tests/Server/Utils/Telemetry/ContextSpanProcessor.test.ts +281 -0
  236. package/Tests/Server/Utils/Telemetry/InventoryDuplicateIdentity.test.ts +316 -0
  237. package/Tests/Server/Utils/Telemetry/PinServiceName.test.ts +666 -0
  238. package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +111 -8
  239. package/Tests/Server/Utils/Telemetry/TelemetryFanInWriterCapacity.test.ts +458 -0
  240. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +424 -0
  241. package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.test.ts +528 -0
  242. package/Tests/Server/Utils/TelemetryExporterEnvironment.test.ts +56 -0
  243. package/Tests/Server/Utils/VM/VMRunnerHostBridgeLatency.test.ts +74 -0
  244. package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +100 -7
  245. package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +1176 -6
  246. package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +220 -0
  247. package/Tests/Types/CustomField/CustomFieldValueMapping.test.ts +348 -0
  248. package/Tests/Types/Database/IncludesAnyOfGroups.test.ts +106 -0
  249. package/Tests/Types/JSONFunctions.test.ts +300 -0
  250. package/Tests/Types/Monitor/CephAlertTemplates.test.ts +422 -53
  251. package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +495 -15
  252. package/Tests/Types/Monitor/DockerSwarmAlertTemplates.test.ts +191 -45
  253. package/Tests/Types/Monitor/HostAlertTemplates.test.ts +370 -42
  254. package/Tests/Types/Monitor/IotAlertTemplates.test.ts +375 -20
  255. package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +636 -42
  256. package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +189 -0
  257. package/Tests/Types/Monitor/KubernetesTemplateGroupByKeys.test.ts +95 -14
  258. package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +190 -21
  259. package/Tests/Types/Monitor/ProxmoxAlertTemplates.test.ts +352 -22
  260. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationAlertDebuggability.test.ts +363 -0
  261. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +93 -18
  262. package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +259 -0
  263. package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +476 -0
  264. package/Tests/Types/Monitor/RumAlertTemplates.test.ts +136 -0
  265. package/Tests/Types/Monitor/SeriesContext/SeriesDebugHints.test.ts +661 -0
  266. package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +507 -0
  267. package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +270 -0
  268. package/Tests/Types/Monitor/TemplateGroupByKeys.test.ts +38 -10
  269. package/Tests/Types/Monitor/Utils/RecommendationCriteriaAssertions.ts +102 -0
  270. package/Tests/Types/NetworkAutomation/AutoImportRunMerge.test.ts +235 -0
  271. package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +66 -0
  272. package/Tests/Types/NetworkAutomation/RuleRunResultDescribe.test.ts +309 -1
  273. package/Tests/Types/NotificationSetting/RoutineEmailEvents.test.ts +47 -0
  274. package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +608 -0
  275. package/Tests/Types/Rum/SessionReplayCustomEvents.test.ts +434 -0
  276. package/Tests/Types/Telemetry/InventoryLiveness.test.ts +99 -0
  277. package/Tests/Types/WebsiteRequest.test.ts +285 -3
  278. package/Tests/UI/Components/Charts/ChartBucketIdentity.test.ts +16 -3
  279. package/Tests/UI/Components/Charts/ChartTrailingBucketGap.test.ts +691 -0
  280. package/Tests/UI/Components/ComponentsModal.test.tsx +12 -4
  281. package/Tests/UI/Components/ComponentsModalUsability.test.tsx +518 -0
  282. package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +160 -0
  283. package/Tests/UI/Components/CustomFields/CustomFieldsDetailMapping.test.tsx +324 -0
  284. package/Tests/UI/Components/CustomFields/MapFromCustomFieldInput.test.tsx +176 -0
  285. package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +484 -0
  286. package/Tests/UI/Components/HeaderRightRail.test.tsx +157 -0
  287. package/Tests/UI/Components/IconDropdownItem.test.tsx +102 -0
  288. package/Tests/UI/Components/JSONTablePrototypePollution.test.tsx +117 -0
  289. package/Tests/UI/Components/KeyboardShortcutsModal.test.tsx +214 -0
  290. package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +561 -0
  291. package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +302 -0
  292. package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +28 -12
  293. package/Tests/UI/Components/ShortcutDialogGuard.test.tsx +74 -0
  294. package/Tests/UI/Components/StatusPage/ResourceGroupSectionAutoExpand.test.tsx +248 -0
  295. package/Tests/UI/Components/TableCellWrapping.test.tsx +625 -0
  296. package/Tests/UI/Components/TableLoadingStates.test.tsx +205 -0
  297. package/Tests/UI/Components/Workflow/NodePlacement.test.ts +148 -0
  298. package/Tests/UI/Components/Workflow/Workflow.test.tsx +981 -0
  299. package/Tests/UI/ConfigBrowserTelemetry.test.ts +70 -0
  300. package/Tests/UI/ReactRouterSingletonBuild.test.ts +133 -0
  301. package/Tests/UI/Rum/ChunkLoader.test.ts +1036 -2
  302. package/Tests/UI/Rum/FidelityNotices.test.ts +187 -0
  303. package/Tests/UI/Rum/InactivityMap.test.ts +341 -0
  304. package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +259 -0
  305. package/Tests/UI/Rum/RecordingHealthCard.test.tsx +855 -0
  306. package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +594 -0
  307. package/Tests/UI/Rum/ReplayCard.test.tsx +451 -0
  308. package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +536 -0
  309. package/Tests/UI/Rum/ReplayEngine.test.ts +2503 -0
  310. package/Tests/UI/Rum/ReplayEngineTypes.test.ts +171 -0
  311. package/Tests/UI/Rum/ReplayHeader.test.tsx +719 -0
  312. package/Tests/UI/Rum/ReplayLink.test.tsx +119 -0
  313. package/Tests/UI/Rum/ReplayPinControl.test.tsx +408 -0
  314. package/Tests/UI/Rum/ReplayPlaybackIntent.test.ts +152 -0
  315. package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +514 -0
  316. package/Tests/UI/Rum/ReplayRail.test.tsx +1296 -0
  317. package/Tests/UI/Rum/ReplayRailDetail.test.tsx +900 -0
  318. package/Tests/UI/Rum/ReplayScrubber.test.tsx +763 -147
  319. package/Tests/UI/Rum/ReplaySignalTypes.test.ts +189 -0
  320. package/Tests/UI/Rum/ReplaySignals.test.ts +1579 -0
  321. package/Tests/UI/Rum/ReplayStage.test.tsx +396 -473
  322. package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +918 -0
  323. package/Tests/UI/Rum/ReplayTimeFormat.test.ts +94 -0
  324. package/Tests/UI/Rum/ReplayTimeline.test.tsx +696 -0
  325. package/Tests/UI/Rum/ReplayUi.test.tsx +559 -0
  326. package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +499 -0
  327. package/Tests/UI/Rum/SessionReplaySearchBar.test.tsx +346 -0
  328. package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +545 -0
  329. package/Tests/UI/Rum/SessionReplayTable.test.tsx +991 -0
  330. package/Tests/UI/Rum/TargetedCapturePanel.test.tsx +254 -0
  331. package/Tests/UI/Telemetry/BrowserExporterIsolation.test.ts +71 -0
  332. package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
  333. package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -0
  334. package/Tests/UI/Utils/GlobalKeyboardShortcut.test.ts +344 -0
  335. package/Tests/Utils/API.test.ts +308 -0
  336. package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +11 -1
  337. package/Tests/Utils/Dashboard/LabelVariable.test.ts +257 -0
  338. package/Tests/Utils/Dashboard/VariableUrlState.test.ts +144 -14
  339. package/Tests/Utils/HTTPResponseBodyReader.test.ts +323 -0
  340. package/Tests/Utils/MetricUnitUtil.test.ts +81 -0
  341. package/Tests/Utils/Monitor/DeviceMacLearningUtil.test.ts +497 -0
  342. package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +546 -0
  343. package/Tests/Utils/Monitor/MonitorTemplateCustomFieldUtil.test.ts +218 -0
  344. package/Tests/Utils/Monitor/NetworkTopologyEndpointAdoption.test.ts +1755 -0
  345. package/Tests/Utils/NetworkAutomation/AutoImportRunChain.test.ts +320 -0
  346. package/Tests/Utils/NetworkDiscovery/DiscoveryScanStatus.test.ts +117 -0
  347. package/Tests/Utils/Rum/ChunkMath.test.ts +83 -0
  348. package/Tests/Utils/Rum/SessionReplayHealthDiagnosis.test.ts +993 -0
  349. package/Tests/Utils/Rum/SessionReplayStringMap.test.ts +245 -0
  350. package/Tests/Utils/StatusPage/ResourceSearch.test.ts +631 -0
  351. package/Tests/Utils/Telemetry/OriginAllowList.test.ts +633 -0
  352. package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +461 -0
  353. package/Tests/Utils/ValueFormatter.test.ts +148 -0
  354. package/Types/BaseDatabase/IncludesAnyOfGroups.ts +77 -0
  355. package/Types/CustomField/CustomFieldMappingCatalog.ts +161 -0
  356. package/Types/CustomField/CustomFieldMappingSourceResource.ts +19 -0
  357. package/Types/CustomField/CustomFieldValueMapping.ts +288 -0
  358. package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
  359. package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +1 -0
  360. package/Types/Dashboard/DashboardVariable.ts +8 -0
  361. package/Types/Icon/IconProp.ts +1 -0
  362. package/Types/JSON.ts +4 -0
  363. package/Types/JSONFunctions.ts +205 -43
  364. package/Types/Monitor/CephAlertTemplates.ts +157 -195
  365. package/Types/Monitor/DockerAlertTemplates.ts +338 -128
  366. package/Types/Monitor/DockerSwarmAlertTemplates.ts +95 -112
  367. package/Types/Monitor/HostAlertTemplates.ts +302 -130
  368. package/Types/Monitor/IotAlertTemplates.ts +112 -104
  369. package/Types/Monitor/KubernetesAlertTemplates.ts +415 -224
  370. package/Types/Monitor/KubernetesMetricCatalog.ts +23 -19
  371. package/Types/Monitor/PodmanAlertTemplates.ts +116 -163
  372. package/Types/Monitor/ProxmoxAlertTemplates.ts +217 -99
  373. package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +98 -6
  374. package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +44 -7
  375. package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +383 -0
  376. package/Types/Monitor/RumAlertTemplates.ts +46 -7
  377. package/Types/Monitor/SeriesContext/SeriesDebugHints.ts +596 -0
  378. package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +554 -0
  379. package/Types/Monitor/ServiceAlertTemplates.ts +98 -11
  380. package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
  381. package/Types/Monitor/UptimeHistoryLabels.ts +56 -0
  382. package/Types/NetworkAutomation/RuleRunResult.ts +402 -13
  383. package/Types/NotificationSetting/RoutineEmailEvents.ts +30 -0
  384. package/Types/Rum/SessionReplay.ts +209 -0
  385. package/Types/Rum/SessionReplayApi.ts +722 -0
  386. package/Types/Rum/SessionReplayCaptureTrigger.ts +21 -12
  387. package/Types/Rum/SessionReplayConsentMode.ts +11 -7
  388. package/Types/Rum/SessionReplayCustomEvents.ts +549 -0
  389. package/Types/Rum/SessionReplayHealth.ts +238 -0
  390. package/Types/SerializableObjectDictionary.ts +4 -0
  391. package/Types/Telemetry/InventoryLiveness.ts +56 -0
  392. package/Types/Telemetry/TelemetryIngestSurface.ts +95 -0
  393. package/Types/Telemetry/TelemetryIngestionKeyPolicy.ts +75 -0
  394. package/Types/Telemetry/TelemetryIngestionKeyType.ts +29 -0
  395. package/Types/WebsiteRequest.ts +85 -3
  396. package/UI/Components/CSVFileUpload/CSVFileUpload.tsx +9 -2
  397. package/UI/Components/Charts/Area/AreaChart.tsx +13 -2
  398. package/UI/Components/Charts/Bar/BarChart.tsx +6 -2
  399. package/UI/Components/Charts/Line/LineChart.tsx +13 -2
  400. package/UI/Components/Charts/Types/XAxis/XAxis.ts +29 -0
  401. package/UI/Components/Charts/Utils/DataPoint.ts +22 -3
  402. package/UI/Components/Charts/Utils/TimeAnnotation.ts +76 -9
  403. package/UI/Components/Charts/Utils/XAxis.ts +217 -0
  404. package/UI/Components/CustomFields/CustomFieldsDetail.tsx +189 -46
  405. package/UI/Components/CustomFields/MapFromCustomFieldInput.tsx +216 -0
  406. package/UI/Components/Dashboard/DashboardVariableControl.tsx +285 -0
  407. package/UI/Components/Graphs/DayUptimeGraph.tsx +173 -8
  408. package/UI/Components/Graphs/UptimeBarTooltip.tsx +22 -278
  409. package/UI/Components/Graphs/UptimeDaySummary.tsx +327 -0
  410. package/UI/Components/Header/Header.tsx +25 -9
  411. package/UI/Components/Header/IconDropdown/IconDropdownItem.tsx +10 -1
  412. package/UI/Components/Header/IconDropdown/IconDropdownMenu.tsx +6 -1
  413. package/UI/Components/Header/ProjectPicker/ProjectPicker.tsx +6 -1
  414. package/UI/Components/HeaderAlert/NotificationBell/NotificationBellDropdown.tsx +6 -1
  415. package/UI/Components/Icon/Icon.tsx +11 -0
  416. package/UI/Components/JSONTable/JSONTable.tsx +2 -2
  417. package/UI/Components/KeyboardShortcut/KeyboardShortcutsModal.tsx +125 -0
  418. package/UI/Components/KeyboardShortcut/Screenshots/README.md +19 -0
  419. package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-command-palette.png +0 -0
  420. package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-dialog-dark.png +0 -0
  421. package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-dialog.png +0 -0
  422. package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-help-menu.png +0 -0
  423. package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-narrow.png +0 -0
  424. package/UI/Components/ModelTable/Column.ts +15 -0
  425. package/UI/Components/Monitor/SeriesDebugCommandsViewer.tsx +64 -0
  426. package/UI/Components/Monitor/SeriesLabelsViewer.tsx +93 -0
  427. package/UI/Components/MonitorGraphs/Uptime.tsx +14 -2
  428. package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +63 -10
  429. package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +44 -0
  430. package/UI/Components/StatusPage/ResourceGroupSection.tsx +40 -0
  431. package/UI/Components/Table/CellClassName.ts +81 -0
  432. package/UI/Components/Table/TableRow.tsx +20 -14
  433. package/UI/Components/Table/TableSkeletonRows.tsx +9 -9
  434. package/UI/Components/Table/Types/Column.ts +36 -0
  435. package/UI/Components/Tooltip/Tooltip.tsx +11 -1
  436. package/UI/Components/Workflow/ComponentsModal.tsx +60 -39
  437. package/UI/Components/Workflow/NodePlacement.ts +57 -0
  438. package/UI/Components/Workflow/Workflow.tsx +47 -18
  439. package/UI/Config.ts +15 -33
  440. package/UI/Utils/GlobalKeyboardShortcut.ts +208 -0
  441. package/UI/Utils/Telemetry/BrowserTelemetryConfig.ts +28 -0
  442. package/UI/Utils/Telemetry/Telemetry.ts +10 -8
  443. package/UI/esbuild-config.js +6 -0
  444. package/Utils/API.ts +129 -3
  445. package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +12 -1
  446. package/Utils/Dashboard/LabelVariable.ts +152 -0
  447. package/Utils/Dashboard/VariableUrlState.ts +27 -5
  448. package/Utils/HTTPResponseBodyReader.ts +221 -0
  449. package/Utils/MetricUnitUtil.ts +34 -0
  450. package/Utils/Monitor/DeviceMacLearningUtil.ts +177 -0
  451. package/Utils/Monitor/MetricValueFormatter.ts +342 -0
  452. package/Utils/Monitor/MonitorTemplateCustomFieldUtil.ts +124 -0
  453. package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +7 -2
  454. package/Utils/Monitor/NetworkTopologyUtil.ts +693 -0
  455. package/Utils/NetworkAutomation/AutoImportRunChain.ts +138 -0
  456. package/Utils/NetworkDiscovery/DiscoveryScanStatus.ts +77 -0
  457. package/Utils/Rum/ChunkMath.ts +106 -0
  458. package/Utils/Rum/SessionReplayHealth.ts +732 -0
  459. package/Utils/Rum/SessionReplayStringMap.ts +226 -0
  460. package/Utils/Schema/ModelSchema.ts +1 -0
  461. package/Utils/StatusPage/ResourceSearch.ts +290 -0
  462. package/Utils/Telemetry/OriginAllowList.ts +355 -0
  463. package/Utils/Uptime/DayUptimeGraphUtil.ts +205 -0
  464. package/Utils/ValueFormatter.ts +35 -1
  465. package/build/dist/Models/AnalyticsModels/RumSession.js +102 -19
  466. package/build/dist/Models/AnalyticsModels/RumSession.js.map +1 -1
  467. package/build/dist/Models/AnalyticsModels/RumSessionChunk.js +11 -9
  468. package/build/dist/Models/AnalyticsModels/RumSessionChunk.js.map +1 -1
  469. package/build/dist/Models/DatabaseModels/AlertCustomField.js +79 -3
  470. package/build/dist/Models/DatabaseModels/AlertCustomField.js.map +1 -1
  471. package/build/dist/Models/DatabaseModels/ApiKeyPermission.js +3 -27
  472. package/build/dist/Models/DatabaseModels/ApiKeyPermission.js.map +1 -1
  473. package/build/dist/Models/DatabaseModels/GlobalConfig.js +12 -12
  474. package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
  475. package/build/dist/Models/DatabaseModels/IncidentCustomField.js +79 -3
  476. package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
  477. package/build/dist/Models/DatabaseModels/InventoryItem.js +34 -1
  478. package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
  479. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +79 -3
  480. package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -1
  481. package/build/dist/Models/DatabaseModels/MonitorCustomField.js +79 -3
  482. package/build/dist/Models/DatabaseModels/MonitorCustomField.js.map +1 -1
  483. package/build/dist/Models/DatabaseModels/NetworkDevice.js +128 -0
  484. package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
  485. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyCustomField.js +79 -3
  486. package/build/dist/Models/DatabaseModels/OnCallDutyPolicyCustomField.js.map +1 -1
  487. package/build/dist/Models/DatabaseModels/RumApplication.js +6 -6
  488. package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
  489. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceCustomField.js +79 -3
  490. package/build/dist/Models/DatabaseModels/ScheduledMaintenanceCustomField.js.map +1 -1
  491. package/build/dist/Models/DatabaseModels/StatusPageCustomField.js +79 -3
  492. package/build/dist/Models/DatabaseModels/StatusPageCustomField.js.map +1 -1
  493. package/build/dist/Models/DatabaseModels/TeamCustomField.js +79 -3
  494. package/build/dist/Models/DatabaseModels/TeamCustomField.js.map +1 -1
  495. package/build/dist/Models/DatabaseModels/TeamMember.js +0 -9
  496. package/build/dist/Models/DatabaseModels/TeamMember.js.map +1 -1
  497. package/build/dist/Models/DatabaseModels/TeamMemberCustomField.js +79 -3
  498. package/build/dist/Models/DatabaseModels/TeamMemberCustomField.js.map +1 -1
  499. package/build/dist/Models/DatabaseModels/TeamPermission.js +3 -24
  500. package/build/dist/Models/DatabaseModels/TeamPermission.js.map +1 -1
  501. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js +267 -0
  502. package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
  503. package/build/dist/Models/DatabaseModels/UserTelegram.js +0 -4
  504. package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
  505. package/build/dist/Server/API/TelemetryAPI.js +769 -109
  506. package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
  507. package/build/dist/Server/API/UserNotificationSettingAPI.js +35 -0
  508. package/build/dist/Server/API/UserNotificationSettingAPI.js.map +1 -0
  509. package/build/dist/Server/API/UserTelegramAPI.js +28 -6
  510. package/build/dist/Server/API/UserTelegramAPI.js.map +1 -1
  511. package/build/dist/Server/EnvironmentConfig.js +73 -14
  512. package/build/dist/Server/EnvironmentConfig.js.map +1 -1
  513. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791300000000-AddTelemetryIngestionKeyType.js +60 -0
  514. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791300000000-AddTelemetryIngestionKeyType.js.map +1 -0
  515. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791400000000-SessionReplayRecordEverySessionByDefault.js +53 -0
  516. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791400000000-SessionReplayRecordEverySessionByDefault.js.map +1 -0
  517. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791500000000-WidenCustomFieldDropdownOptions.js +82 -0
  518. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791500000000-WidenCustomFieldDropdownOptions.js.map +1 -0
  519. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791600000000-AddCustomFieldValueMapping.js +46 -0
  520. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791600000000-AddCustomFieldValueMapping.js.map +1 -0
  521. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js +14 -0
  522. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js.map +1 -0
  523. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
  524. package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
  525. package/build/dist/Server/Infrastructure/QueueWorker.js +19 -7
  526. package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
  527. package/build/dist/Server/Infrastructure/Redis.js +8 -8
  528. package/build/dist/Server/Infrastructure/Redis.js.map +1 -1
  529. package/build/dist/Server/Infrastructure/Semaphore.js +1 -0
  530. package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
  531. package/build/dist/Server/Infrastructure/Status.js +10 -4
  532. package/build/dist/Server/Infrastructure/Status.js.map +1 -1
  533. package/build/dist/Server/Middleware/TelemetryIngest.js +315 -8
  534. package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
  535. package/build/dist/Server/Services/AccessTokenService.js +1 -0
  536. package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
  537. package/build/dist/Server/Services/AlertCustomFieldService.js +93 -0
  538. package/build/dist/Server/Services/AlertCustomFieldService.js.map +1 -1
  539. package/build/dist/Server/Services/AlertService.js +34 -0
  540. package/build/dist/Server/Services/AlertService.js.map +1 -1
  541. package/build/dist/Server/Services/ApiKeyPermissionService.js +331 -28
  542. package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
  543. package/build/dist/Server/Services/CustomFieldMappingService.js +614 -0
  544. package/build/dist/Server/Services/CustomFieldMappingService.js.map +1 -0
  545. package/build/dist/Server/Services/DatabaseService.js +58 -13
  546. package/build/dist/Server/Services/DatabaseService.js.map +1 -1
  547. package/build/dist/Server/Services/GlobalConfigService.js +133 -0
  548. package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
  549. package/build/dist/Server/Services/IncidentCustomFieldService.js +93 -0
  550. package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
  551. package/build/dist/Server/Services/IncidentService.js +32 -0
  552. package/build/dist/Server/Services/IncidentService.js.map +1 -1
  553. package/build/dist/Server/Services/InventoryItemService.js +28 -1
  554. package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
  555. package/build/dist/Server/Services/MetricService.js +163 -0
  556. package/build/dist/Server/Services/MetricService.js.map +1 -1
  557. package/build/dist/Server/Services/MonitorService.js +29 -0
  558. package/build/dist/Server/Services/MonitorService.js.map +1 -1
  559. package/build/dist/Server/Services/MonitorTemplateService.js +116 -17
  560. package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
  561. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +349 -64
  562. package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
  563. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +65 -0
  564. package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
  565. package/build/dist/Server/Services/NetworkDeviceService.js +54 -0
  566. package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
  567. package/build/dist/Server/Services/ProjectService.js +22 -0
  568. package/build/dist/Server/Services/ProjectService.js.map +1 -1
  569. package/build/dist/Server/Services/RoutineEmailSettingsService.js +69 -0
  570. package/build/dist/Server/Services/RoutineEmailSettingsService.js.map +1 -0
  571. package/build/dist/Server/Services/RumSessionReplayViewService.js +86 -24
  572. package/build/dist/Server/Services/RumSessionReplayViewService.js.map +1 -1
  573. package/build/dist/Server/Services/ScheduledMaintenanceCustomFieldService.js +93 -0
  574. package/build/dist/Server/Services/ScheduledMaintenanceCustomFieldService.js.map +1 -1
  575. package/build/dist/Server/Services/ScheduledMaintenanceService.js +29 -0
  576. package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
  577. package/build/dist/Server/Services/TeamMemberService.js +62 -2
  578. package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
  579. package/build/dist/Server/Services/TeamPermissionService.js +166 -3
  580. package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
  581. package/build/dist/Server/Services/TelemetryIngestionKeyService.js +562 -18
  582. package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
  583. package/build/dist/Server/Services/UserNotificationRuleService.js +60 -25
  584. package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
  585. package/build/dist/Server/Services/UserNotificationSettingService.js +26 -6
  586. package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
  587. package/build/dist/Server/Services/UserTelegramService.js +257 -4
  588. package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
  589. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +29 -4
  590. package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
  591. package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
  592. package/build/dist/Server/Types/Database/QueryUtil.js +36 -0
  593. package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
  594. package/build/dist/Server/Types/Markdown.js +75 -0
  595. package/build/dist/Server/Types/Markdown.js.map +1 -1
  596. package/build/dist/Server/Utils/APIKey/AccessPermission.js +2 -2
  597. package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
  598. package/build/dist/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.js +27 -0
  599. package/build/dist/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.js.map +1 -0
  600. package/build/dist/Server/Utils/CustomField/CustomFieldMappingRegistry.js +226 -0
  601. package/build/dist/Server/Utils/CustomField/CustomFieldMappingRegistry.js.map +1 -0
  602. package/build/dist/Server/Utils/CustomField/CustomFieldMappingValidator.js +189 -0
  603. package/build/dist/Server/Utils/CustomField/CustomFieldMappingValidator.js.map +1 -0
  604. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +53 -18
  605. package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
  606. package/build/dist/Server/Utils/DataSource/EgressGuard.js +121 -7
  607. package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
  608. package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js +38 -5
  609. package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js.map +1 -1
  610. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +54 -3
  611. package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
  612. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +2 -2
  613. package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
  614. package/build/dist/Server/Utils/FrontendEnvironment.js +33 -0
  615. package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -0
  616. package/build/dist/Server/Utils/LogRedaction.js +20 -0
  617. package/build/dist/Server/Utils/LogRedaction.js.map +1 -1
  618. package/build/dist/Server/Utils/Logger.js +10 -0
  619. package/build/dist/Server/Utils/Logger.js.map +1 -1
  620. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +323 -81
  621. package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
  622. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +20 -4
  623. package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
  624. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +29 -5
  625. package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
  626. package/build/dist/Server/Utils/Monitor/MonitorAlert.js +29 -6
  627. package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
  628. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +254 -48
  629. package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
  630. package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +58 -9
  631. package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -1
  632. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js +9 -3
  633. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js.map +1 -1
  634. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js +26 -12
  635. package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js.map +1 -1
  636. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +74 -33
  637. package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
  638. package/build/dist/Server/Utils/Monitor/MonitorIncident.js +22 -6
  639. package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
  640. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +22 -0
  641. package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
  642. package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js +114 -0
  643. package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js.map +1 -0
  644. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +25 -0
  645. package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
  646. package/build/dist/Server/Utils/Monitor/SeriesContextEnricher.js +237 -0
  647. package/build/dist/Server/Utils/Monitor/SeriesContextEnricher.js.map +1 -0
  648. package/build/dist/Server/Utils/SSRFProtection.js +136 -9
  649. package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
  650. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +174 -89
  651. package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
  652. package/build/dist/Server/Utils/SessionReplay/SessionReplayHealthCounters.js +223 -0
  653. package/build/dist/Server/Utils/SessionReplay/SessionReplayHealthCounters.js.map +1 -0
  654. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +945 -164
  655. package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
  656. package/build/dist/Server/Utils/StartServer.js +3 -14
  657. package/build/dist/Server/Utils/StartServer.js.map +1 -1
  658. package/build/dist/Server/Utils/TelegramVerificationToken.js +129 -0
  659. package/build/dist/Server/Utils/TelegramVerificationToken.js.map +1 -0
  660. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +9 -1
  661. package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
  662. package/build/dist/Server/Utils/Telemetry/PinServiceName.js +163 -0
  663. package/build/dist/Server/Utils/Telemetry/PinServiceName.js.map +1 -0
  664. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +70 -13
  665. package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
  666. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +28 -15
  667. package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -1
  668. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +64 -0
  669. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -0
  670. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.js +171 -0
  671. package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.js.map +1 -0
  672. package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
  673. package/build/dist/Server/Utils/VM/VMRunner.js +780 -69
  674. package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
  675. package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js +54 -0
  676. package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js.map +1 -0
  677. package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +60 -0
  678. package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -0
  679. package/build/dist/Types/CustomField/CustomFieldMappingSourceResource.js +20 -0
  680. package/build/dist/Types/CustomField/CustomFieldMappingSourceResource.js.map +1 -0
  681. package/build/dist/Types/CustomField/CustomFieldValueMapping.js +141 -0
  682. package/build/dist/Types/CustomField/CustomFieldValueMapping.js.map +1 -0
  683. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
  684. package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
  685. package/build/dist/Types/Dashboard/DashboardVariable.js +1 -0
  686. package/build/dist/Types/Dashboard/DashboardVariable.js.map +1 -1
  687. package/build/dist/Types/Icon/IconProp.js +1 -0
  688. package/build/dist/Types/Icon/IconProp.js.map +1 -1
  689. package/build/dist/Types/JSON.js +1 -0
  690. package/build/dist/Types/JSON.js.map +1 -1
  691. package/build/dist/Types/JSONFunctions.js +131 -37
  692. package/build/dist/Types/JSONFunctions.js.map +1 -1
  693. package/build/dist/Types/Monitor/CephAlertTemplates.js +130 -171
  694. package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
  695. package/build/dist/Types/Monitor/DockerAlertTemplates.js +286 -121
  696. package/build/dist/Types/Monitor/DockerAlertTemplates.js.map +1 -1
  697. package/build/dist/Types/Monitor/DockerSwarmAlertTemplates.js +84 -105
  698. package/build/dist/Types/Monitor/DockerSwarmAlertTemplates.js.map +1 -1
  699. package/build/dist/Types/Monitor/HostAlertTemplates.js +250 -122
  700. package/build/dist/Types/Monitor/HostAlertTemplates.js.map +1 -1
  701. package/build/dist/Types/Monitor/IotAlertTemplates.js +81 -81
  702. package/build/dist/Types/Monitor/IotAlertTemplates.js.map +1 -1
  703. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +375 -215
  704. package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
  705. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +19 -19
  706. package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
  707. package/build/dist/Types/Monitor/PodmanAlertTemplates.js +104 -153
  708. package/build/dist/Types/Monitor/PodmanAlertTemplates.js.map +1 -1
  709. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +156 -91
  710. package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
  711. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +67 -6
  712. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
  713. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +38 -7
  714. package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
  715. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +261 -0
  716. package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -0
  717. package/build/dist/Types/Monitor/RumAlertTemplates.js +46 -7
  718. package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
  719. package/build/dist/Types/Monitor/SeriesContext/SeriesDebugHints.js +422 -0
  720. package/build/dist/Types/Monitor/SeriesContext/SeriesDebugHints.js.map +1 -0
  721. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +441 -0
  722. package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -0
  723. package/build/dist/Types/Monitor/ServiceAlertTemplates.js +79 -15
  724. package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -1
  725. package/build/dist/Types/Monitor/UptimeHistoryLabels.js +17 -0
  726. package/build/dist/Types/Monitor/UptimeHistoryLabels.js.map +1 -0
  727. package/build/dist/Types/NetworkAutomation/RuleRunResult.js +291 -6
  728. package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
  729. package/build/dist/Types/NotificationSetting/RoutineEmailEvents.js +29 -0
  730. package/build/dist/Types/NotificationSetting/RoutineEmailEvents.js.map +1 -0
  731. package/build/dist/Types/Rum/SessionReplay.js +117 -0
  732. package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
  733. package/build/dist/Types/Rum/SessionReplayApi.js +181 -0
  734. package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -0
  735. package/build/dist/Types/Rum/SessionReplayCaptureTrigger.js +21 -12
  736. package/build/dist/Types/Rum/SessionReplayCaptureTrigger.js.map +1 -1
  737. package/build/dist/Types/Rum/SessionReplayConsentMode.js +11 -7
  738. package/build/dist/Types/Rum/SessionReplayConsentMode.js.map +1 -1
  739. package/build/dist/Types/Rum/SessionReplayCustomEvents.js +172 -0
  740. package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -0
  741. package/build/dist/Types/Rum/SessionReplayHealth.js +37 -0
  742. package/build/dist/Types/Rum/SessionReplayHealth.js.map +1 -0
  743. package/build/dist/Types/SerializableObjectDictionary.js +4 -0
  744. package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
  745. package/build/dist/Types/Telemetry/InventoryLiveness.js +37 -0
  746. package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -0
  747. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +85 -0
  748. package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -0
  749. package/build/dist/Types/Telemetry/TelemetryIngestionKeyPolicy.js +15 -0
  750. package/build/dist/Types/Telemetry/TelemetryIngestionKeyPolicy.js.map +1 -0
  751. package/build/dist/Types/Telemetry/TelemetryIngestionKeyType.js +30 -0
  752. package/build/dist/Types/Telemetry/TelemetryIngestionKeyType.js.map +1 -0
  753. package/build/dist/Types/WebsiteRequest.js +55 -3
  754. package/build/dist/Types/WebsiteRequest.js.map +1 -1
  755. package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js +7 -0
  756. package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js.map +1 -1
  757. package/build/dist/UI/Components/Charts/Area/AreaChart.js +13 -2
  758. package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
  759. package/build/dist/UI/Components/Charts/Bar/BarChart.js +6 -2
  760. package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
  761. package/build/dist/UI/Components/Charts/Line/LineChart.js +13 -2
  762. package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
  763. package/build/dist/UI/Components/Charts/Types/XAxis/XAxis.js.map +1 -1
  764. package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -3
  765. package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
  766. package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js +40 -9
  767. package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js.map +1 -1
  768. package/build/dist/UI/Components/Charts/Utils/XAxis.js +186 -0
  769. package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
  770. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +89 -8
  771. package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
  772. package/build/dist/UI/Components/CustomFields/MapFromCustomFieldInput.js +118 -0
  773. package/build/dist/UI/Components/CustomFields/MapFromCustomFieldInput.js.map +1 -0
  774. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +137 -0
  775. package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -0
  776. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +112 -7
  777. package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
  778. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -162
  779. package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
  780. package/build/dist/UI/Components/Graphs/UptimeDaySummary.js +181 -0
  781. package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -0
  782. package/build/dist/UI/Components/Header/Header.js +2 -3
  783. package/build/dist/UI/Components/Header/Header.js.map +1 -1
  784. package/build/dist/UI/Components/Header/IconDropdown/IconDropdownItem.js +2 -1
  785. package/build/dist/UI/Components/Header/IconDropdown/IconDropdownItem.js.map +1 -1
  786. package/build/dist/UI/Components/Header/IconDropdown/IconDropdownMenu.js +7 -1
  787. package/build/dist/UI/Components/Header/IconDropdown/IconDropdownMenu.js.map +1 -1
  788. package/build/dist/UI/Components/Header/ProjectPicker/ProjectPicker.js +7 -1
  789. package/build/dist/UI/Components/Header/ProjectPicker/ProjectPicker.js.map +1 -1
  790. package/build/dist/UI/Components/HeaderAlert/NotificationBell/NotificationBellDropdown.js +7 -1
  791. package/build/dist/UI/Components/HeaderAlert/NotificationBell/NotificationBellDropdown.js.map +1 -1
  792. package/build/dist/UI/Components/Icon/Icon.js +5 -0
  793. package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
  794. package/build/dist/UI/Components/JSONTable/JSONTable.js +2 -2
  795. package/build/dist/UI/Components/JSONTable/JSONTable.js.map +1 -1
  796. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcutsModal.js +35 -0
  797. package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcutsModal.js.map +1 -0
  798. package/build/dist/UI/Components/Monitor/SeriesDebugCommandsViewer.js +34 -0
  799. package/build/dist/UI/Components/Monitor/SeriesDebugCommandsViewer.js.map +1 -0
  800. package/build/dist/UI/Components/Monitor/SeriesLabelsViewer.js +49 -0
  801. package/build/dist/UI/Components/Monitor/SeriesLabelsViewer.js.map +1 -0
  802. package/build/dist/UI/Components/MonitorGraphs/Uptime.js +1 -1
  803. package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
  804. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +32 -6
  805. package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
  806. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +38 -0
  807. package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
  808. package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +22 -1
  809. package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -1
  810. package/build/dist/UI/Components/Table/CellClassName.js +55 -0
  811. package/build/dist/UI/Components/Table/CellClassName.js.map +1 -0
  812. package/build/dist/UI/Components/Table/TableRow.js +16 -13
  813. package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
  814. package/build/dist/UI/Components/Table/TableSkeletonRows.js +9 -8
  815. package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -1
  816. package/build/dist/UI/Components/Tooltip/Tooltip.js +25 -1
  817. package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
  818. package/build/dist/UI/Components/Workflow/ComponentsModal.js +47 -30
  819. package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
  820. package/build/dist/UI/Components/Workflow/NodePlacement.js +37 -0
  821. package/build/dist/UI/Components/Workflow/NodePlacement.js.map +1 -0
  822. package/build/dist/UI/Components/Workflow/Workflow.js +35 -18
  823. package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
  824. package/build/dist/UI/Config.js +10 -20
  825. package/build/dist/UI/Config.js.map +1 -1
  826. package/build/dist/UI/Utils/GlobalKeyboardShortcut.js +138 -0
  827. package/build/dist/UI/Utils/GlobalKeyboardShortcut.js.map +1 -0
  828. package/build/dist/UI/Utils/Telemetry/BrowserTelemetryConfig.js +14 -0
  829. package/build/dist/UI/Utils/Telemetry/BrowserTelemetryConfig.js.map +1 -0
  830. package/build/dist/UI/Utils/Telemetry/Telemetry.js +7 -5
  831. package/build/dist/UI/Utils/Telemetry/Telemetry.js.map +1 -1
  832. package/build/dist/Utils/API.js +67 -5
  833. package/build/dist/Utils/API.js.map +1 -1
  834. package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +9 -1
  835. package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
  836. package/build/dist/Utils/Dashboard/LabelVariable.js +96 -0
  837. package/build/dist/Utils/Dashboard/LabelVariable.js.map +1 -0
  838. package/build/dist/Utils/Dashboard/VariableUrlState.js +19 -4
  839. package/build/dist/Utils/Dashboard/VariableUrlState.js.map +1 -1
  840. package/build/dist/Utils/HTTPResponseBodyReader.js +157 -0
  841. package/build/dist/Utils/HTTPResponseBodyReader.js.map +1 -0
  842. package/build/dist/Utils/MetricUnitUtil.js +26 -0
  843. package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
  844. package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js +112 -0
  845. package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js.map +1 -0
  846. package/build/dist/Utils/Monitor/MetricValueFormatter.js +276 -0
  847. package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -0
  848. package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js +101 -0
  849. package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js.map +1 -0
  850. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +7 -2
  851. package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
  852. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +484 -0
  853. package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
  854. package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js +59 -0
  855. package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js.map +1 -0
  856. package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js +69 -0
  857. package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js.map +1 -0
  858. package/build/dist/Utils/Rum/ChunkMath.js +76 -0
  859. package/build/dist/Utils/Rum/ChunkMath.js.map +1 -1
  860. package/build/dist/Utils/Rum/SessionReplayHealth.js +534 -0
  861. package/build/dist/Utils/Rum/SessionReplayHealth.js.map +1 -0
  862. package/build/dist/Utils/Rum/SessionReplayStringMap.js +165 -0
  863. package/build/dist/Utils/Rum/SessionReplayStringMap.js.map +1 -0
  864. package/build/dist/Utils/Schema/ModelSchema.js +1 -0
  865. package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
  866. package/build/dist/Utils/StatusPage/ResourceSearch.js +200 -0
  867. package/build/dist/Utils/StatusPage/ResourceSearch.js.map +1 -0
  868. package/build/dist/Utils/Telemetry/OriginAllowList.js +299 -0
  869. package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -0
  870. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +147 -0
  871. package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -0
  872. package/build/dist/Utils/ValueFormatter.js +32 -1
  873. package/build/dist/Utils/ValueFormatter.js.map +1 -1
  874. package/jest.config.json +2 -0
  875. package/package.json +1 -1
  876. package/test-setup.sh +9 -9
  877. package/tsconfig.json +51 -11
@@ -117,6 +117,7 @@ import NetworkDeviceAutoImportRuleEngineService, {
117
117
  AUTO_IMPORT_SWEEP_LOCK_TIMEOUT_MS,
118
118
  ExistingHostnamesByProjectId,
119
119
  ImportAttemptBudgetsByProjectId,
120
+ MAX_CONSECUTIVE_MONITOR_CREATE_FAILURES,
120
121
  MAX_DEVICES_PER_AUTO_IMPORT_RUN,
121
122
  MAX_MONITORS_PER_AUTO_IMPORT_RUN,
122
123
  MAX_RESULT_AGE_IN_HOURS,
@@ -139,6 +140,7 @@ import NetworkDeviceDiscoveryScan, {
139
140
  } from "../../../Models/DatabaseModels/NetworkDeviceDiscoveryScan";
140
141
  import { DiscoveryScanSnmpConfig } from "../../../Utils/NetworkDiscovery/SnmpScanConfigUtil";
141
142
  import BadDataException from "../../../Types/Exception/BadDataException";
143
+ import { JSONObject } from "../../../Types/JSON";
142
144
  import ObjectID from "../../../Types/ObjectID";
143
145
  import OneUptimeDate from "../../../Types/Date";
144
146
  import MonitorSteps from "../../../Types/Monitor/MonitorSteps";
@@ -219,9 +221,26 @@ const semaphoreLockMock: jest.Mock = Semaphore.lock as unknown as jest.Mock;
219
221
  const semaphoreReleaseMock: jest.Mock =
220
222
  Semaphore.release as unknown as jest.Mock;
221
223
  const loggerErrorMock: jest.Mock = logger.error as unknown as jest.Mock;
224
+ const loggerWarnMock: jest.Mock = logger.warn as unknown as jest.Mock;
222
225
 
223
226
  const RECENT_COMPLETED_AT: Date = OneUptimeDate.getCurrentDate();
224
227
 
228
+ /*
229
+ * The values behind a QueryHelper.any() filter.
230
+ *
231
+ * QueryHelper.any builds a raw TypeORM FindOperator whose parameters hold the
232
+ * list, so a status filter cannot be compared to a string any more. Reading
233
+ * the values back out is what lets a test still say WHICH statuses a query
234
+ * accepts rather than merely that it is some operator.
235
+ */
236
+ function statusesMatchedBy(filter: unknown): Array<string> {
237
+ const parameters: Record<string, Array<string>> = (
238
+ filter as { _objectLiteralParameters?: Record<string, Array<string>> }
239
+ )._objectLiteralParameters!;
240
+
241
+ return Object.values(parameters)[0]!;
242
+ }
243
+
225
244
  /*
226
245
  * The scan's credential sets (issue #3458), and the ids the probe stamps onto
227
246
  * a discovered host to record which one answered it. Opaque literals, because
@@ -542,9 +561,19 @@ describe("NetworkDeviceAutoImportRuleEngineService.processCompletedScan", () =>
542
561
  expect(updateCall.data.autoImportProcessedAt).toBeInstanceOf(Date);
543
562
  // Nothing was created, so the stored results are not rewritten.
544
563
  expect(Object.keys(updateCall.data)).toEqual(["autoImportProcessedAt"]);
564
+ /*
565
+ * The compare-and-set is over the run state this pass READ, not over the
566
+ * literal "Completed" it used to pin: a scan that is still sweeping is
567
+ * importable now (issue #3599), and its status is exactly what changes
568
+ * when its run ends. The two host counters ride along because they are
569
+ * what a running scan's partial uploads move, so a partial landing between
570
+ * the read and the stamp is caught the way a completion is.
571
+ */
545
572
  expect(updateCall.expectedData).toEqual({
546
573
  status: "Completed",
547
574
  completedAt: RECENT_COMPLETED_AT,
575
+ scannedHostCount: null,
576
+ respondedHostCount: null,
548
577
  });
549
578
  });
550
579
 
@@ -560,6 +589,7 @@ describe("NetworkDeviceAutoImportRuleEngineService.processCompletedScan", () =>
560
589
  const unmatchedRow: DiscoveredNetworkDevice = makeHost({
561
590
  ipAddress: "192.168.1.5",
562
591
  sysName: "printer-01",
592
+ isAlreadyRegistered: true,
563
593
  });
564
594
 
565
595
  scanFindOneByMock.mockResolvedValue(
@@ -584,6 +614,10 @@ describe("NetworkDeviceAutoImportRuleEngineService.processCompletedScan", () =>
584
614
  expect(createMock).toHaveBeenCalledTimes(1);
585
615
  expect(monitorFindByMock).not.toHaveBeenCalled();
586
616
  expect(monitorTemplateFindByMock).not.toHaveBeenCalled();
617
+ expect(scanFindOneByMock.mock.calls[0]![0].props).toEqual({
618
+ isRoot: true,
619
+ ignoreHooks: true,
620
+ });
587
621
  const device: NetworkDevice = createdDevice(0);
588
622
  // The address is the hostname AND the dedup key downstream.
589
623
  expect(device.hostname).toBe("10.0.0.5");
@@ -786,6 +820,66 @@ describe("NetworkDeviceAutoImportRuleEngineService.processCompletedScan", () =>
786
820
  ).toBe(TEMPLATE_DEVICE_ID);
787
821
  });
788
822
 
823
+ /*
824
+ * ISSUE #3548. A template's custom fields are the DEFAULTS its monitors
825
+ * are born with, and this is the path that made that worth having: a
826
+ * discovery scan importing a thousand devices used to leave a thousand
827
+ * monitors with every custom field empty, fillable only one monitor at a
828
+ * time.
829
+ *
830
+ * Driven through the engine rather than the builder alone for the same
831
+ * reason as the name test above — the values only reach the create call if
832
+ * `customFields` is still in this service's template select, and a dropped
833
+ * select column is indistinguishable from a template nobody set defaults
834
+ * on. Two hosts, because the cached template serves the whole estate: a
835
+ * shallow copy would hand every monitor in the run the same jsonb object,
836
+ * so one later edit would reach the rest of the fleet and the template.
837
+ */
838
+ it("gives every monitor in one run its own copy of the template's custom field defaults", async () => {
839
+ const template: MonitorTemplate = makeTemplate({
840
+ customFields: { Vendor: "Cisco", Thresholds: { cpu: 80 } },
841
+ });
842
+ scanFindOneByMock.mockResolvedValue(
843
+ makeScan({
844
+ discoveredDevices: [
845
+ makeHost(),
846
+ makeHost({ ipAddress: "10.0.0.6", sysName: "core-switch-02" }),
847
+ ],
848
+ }),
849
+ );
850
+ ruleFindByMock.mockResolvedValue([
851
+ makeRule({ monitorTemplateId: TEMPLATE_ID }),
852
+ ]);
853
+ monitorTemplateFindByMock.mockResolvedValue([template]);
854
+
855
+ const result: AutoImportRuleRunResult | null = await processScan();
856
+
857
+ expect(result).toMatchObject({ devicesCreated: 2, monitorsCreated: 2 });
858
+
859
+ const firstMonitor: Monitor = provisionedMonitor(0);
860
+ const secondMonitor: Monitor = provisionedMonitor(1);
861
+
862
+ for (const monitor of [firstMonitor, secondMonitor]) {
863
+ expect(monitor.customFields).toEqual({
864
+ Vendor: "Cisco",
865
+ Thresholds: { cpu: 80 },
866
+ });
867
+ }
868
+
869
+ expect(firstMonitor.customFields).not.toBe(secondMonitor.customFields);
870
+ expect(firstMonitor.customFields?.["Thresholds"]).not.toBe(
871
+ secondMonitor.customFields?.["Thresholds"],
872
+ );
873
+
874
+ (firstMonitor.customFields!["Thresholds"] as JSONObject)["cpu"] = 10;
875
+ expect(
876
+ (secondMonitor.customFields!["Thresholds"] as JSONObject)["cpu"],
877
+ ).toBe(80);
878
+ expect((template.customFields!["Thresholds"] as JSONObject)["cpu"]).toBe(
879
+ 80,
880
+ );
881
+ });
882
+
789
883
  /*
790
884
  * ISSUE #3486. "Default Monitor Name" is optional, and a template that
791
885
  * leaves it blank names what it provisions after the DEVICE and nothing
@@ -1287,7 +1381,89 @@ describe("NetworkDeviceAutoImportRuleEngineService.processCompletedScan", () =>
1287
1381
  expect(monitorCreateMock).not.toHaveBeenCalled();
1288
1382
  });
1289
1383
 
1290
- it("caps missing-template failures across a large existing estate", async () => {
1384
+ /*
1385
+ * OneUptime/oneuptime#3643, the report this whole block exists for.
1386
+ *
1387
+ * An estate of 501 devices that a previous run already imported, and a
1388
+ * rule whose Monitor Template no longer resolves. The template is a
1389
+ * property of the RULE: it fails for the first device and for all 500
1390
+ * after it, and no amount of running again changes that.
1391
+ *
1392
+ * What the customer saw was the engine re-proving that 500 times, one
1393
+ * wasted monitor budget slot at a time, until the budget ran out — then
1394
+ * reporting "Stopped at the run cap - run again to continue", with no hint
1395
+ * of what was actually wrong. What it must do instead is settle the
1396
+ * verdict once, count every monitor that is genuinely missing, and say
1397
+ * why.
1398
+ */
1399
+ it("settles an unresolvable template once instead of re-failing it per device", async () => {
1400
+ const hosts: Array<DiscoveredNetworkDevice> = Array.from(
1401
+ { length: MAX_MONITORS_PER_AUTO_IMPORT_RUN + 1 },
1402
+ (_value: unknown, index: number): DiscoveredNetworkDevice => {
1403
+ return makeHost({
1404
+ ipAddress: `10.2.${Math.floor(index / 256)}.${index % 256}`,
1405
+ sysName: `missing-template-switch-${index}`,
1406
+ });
1407
+ },
1408
+ );
1409
+ const devices: Array<NetworkDevice> = hosts.map(
1410
+ (host: DiscoveredNetworkDevice): NetworkDevice => {
1411
+ return makeExistingDevice({
1412
+ id: ObjectID.generate(),
1413
+ hostname: host.ipAddress,
1414
+ name: host.sysName,
1415
+ });
1416
+ },
1417
+ );
1418
+
1419
+ ruleFindOneByMock.mockResolvedValue(
1420
+ makeRule({
1421
+ ipMatchTarget: "10.0.0.0/8",
1422
+ monitorTemplateId: TEMPLATE_ID,
1423
+ }),
1424
+ );
1425
+ ruleFindByMock.mockResolvedValue([]);
1426
+ deviceFindByMock.mockResolvedValue(devices);
1427
+ monitorTemplateFindByMock.mockResolvedValue([]);
1428
+ mockRunNowScans([makeScan({ discoveredDevices: hosts })]);
1429
+
1430
+ const result: AutoImportRuleRunResult = await runRule(false);
1431
+
1432
+ expect(result).toMatchObject({
1433
+ hostsMatched: hosts.length,
1434
+ hostsSkippedAlreadyRegistered: hosts.length,
1435
+ devicesCreated: 0,
1436
+ monitorsCreated: 0,
1437
+ // Every device really is missing its monitor, so every one is counted.
1438
+ monitorsFailed: hosts.length,
1439
+ monitorProvisioningHalted: true,
1440
+ /*
1441
+ * Nothing was cut short by a cap, so nothing is waiting for another
1442
+ * run. This is the misleading half of the customer's dialog.
1443
+ */
1444
+ isTruncated: false,
1445
+ });
1446
+
1447
+ expect(result.monitorFailureReasons).toEqual([
1448
+ "This rule's Monitor Template could not be loaded. It may have been deleted, moved to another project, or changed to a monitor type other than Network Device. Edit the rule and select a Network Device Monitor Template.",
1449
+ ]);
1450
+ expect(monitorCreateMock).not.toHaveBeenCalled();
1451
+
1452
+ /*
1453
+ * And it costs one log line and one budget slot, not 500 of each. The
1454
+ * per-device race check is an unindexed search of the monitor step
1455
+ * payload; running it 500 times to reach the same verdict is the other
1456
+ * half of what made this run expensive.
1457
+ */
1458
+ expect(
1459
+ loggerErrorMock.mock.calls.filter((call: Array<unknown>): boolean => {
1460
+ return String(call[0]).includes("is missing, deleted, or not valid");
1461
+ }),
1462
+ ).toHaveLength(1);
1463
+ expect(monitorFindByMock.mock.calls.length).toBeLessThanOrEqual(2);
1464
+ });
1465
+
1466
+ it("reports the same unresolvable template on a dry run without a cap", async () => {
1291
1467
  const hosts: Array<DiscoveredNetworkDevice> = Array.from(
1292
1468
  { length: MAX_MONITORS_PER_AUTO_IMPORT_RUN + 1 },
1293
1469
  (_value: unknown, index: number): DiscoveredNetworkDevice => {
@@ -1322,106 +1498,400 @@ describe("NetworkDeviceAutoImportRuleEngineService.processCompletedScan", () =>
1322
1498
 
1323
1499
  expect(result).toMatchObject({
1324
1500
  monitorsWouldCreate: 0,
1325
- monitorsFailed: MAX_MONITORS_PER_AUTO_IMPORT_RUN,
1326
- isTruncated: true,
1501
+ monitorsFailed: hosts.length,
1502
+ monitorProvisioningHalted: true,
1503
+ isTruncated: false,
1327
1504
  isDryRun: true,
1328
1505
  });
1329
1506
  expect(monitorCreateMock).not.toHaveBeenCalled();
1330
1507
  });
1331
- });
1332
1508
 
1333
- /*
1334
- * An over-long "address" is junk that would fail the varchar(100)
1335
- * hostname on every pass, identically, foreverso it is refused before
1336
- * any counting rather than logged as a devicesFailed per worker tick.
1337
- */
1338
- it("skips a host whose address could never be a hostname, before any counting", async () => {
1339
- scanFindOneByMock.mockResolvedValue(
1340
- makeScan({
1341
- discoveredDevices: [
1342
- makeHost({ ipAddress: "9".repeat(101) }),
1343
- makeHost(),
1344
- ],
1345
- }),
1346
- );
1509
+ /*
1510
+ * The other shape of the same report: the template resolves fine, and
1511
+ * every create fails the same way anyway a plan limit, criteria naming
1512
+ * a monitor status that no longer exists, a database refusing writes.
1513
+ * Indistinguishable from a per-host failure on the first device, so the
1514
+ * engine tries, and stops once the evidence is decisive.
1515
+ */
1516
+ it("stops provisioning after the same create failure repeats for every device", async () => {
1517
+ const hosts: Array<DiscoveredNetworkDevice> = Array.from(
1518
+ { length: MAX_MONITORS_PER_AUTO_IMPORT_RUN + 1 },
1519
+ (_value: unknown, index: number): DiscoveredNetworkDevice => {
1520
+ return makeHost({
1521
+ ipAddress: `10.3.${Math.floor(index / 256)}.${index % 256}`,
1522
+ sysName: `systemic-switch-${index}`,
1523
+ });
1524
+ },
1525
+ );
1526
+ const devices: Array<NetworkDevice> = hosts.map(
1527
+ (host: DiscoveredNetworkDevice): NetworkDevice => {
1528
+ return makeExistingDevice({
1529
+ id: ObjectID.generate(),
1530
+ hostname: host.ipAddress,
1531
+ name: host.sysName,
1532
+ });
1533
+ },
1534
+ );
1347
1535
 
1348
- const result: AutoImportRuleRunResult | null = await processScan();
1536
+ ruleFindOneByMock.mockResolvedValue(
1537
+ makeRule({
1538
+ ipMatchTarget: "10.0.0.0/8",
1539
+ monitorTemplateId: TEMPLATE_ID,
1540
+ }),
1541
+ );
1542
+ ruleFindByMock.mockResolvedValue([]);
1543
+ deviceFindByMock.mockResolvedValue(devices);
1544
+ monitorFindByMock.mockResolvedValue([]);
1545
+ monitorFindOneByMock.mockResolvedValue(null);
1546
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
1547
+ monitorCreateMock.mockRejectedValue(
1548
+ new BadDataException(
1549
+ "You have reached the maximum allowed monitor limit for the free plan.",
1550
+ ),
1551
+ );
1552
+ mockRunNowScans([makeScan({ discoveredDevices: hosts })]);
1349
1553
 
1350
- // The junk row was never evaluated, let alone matched or attempted.
1351
- expect(result).toMatchObject({
1352
- hostsEvaluated: 1,
1353
- hostsMatched: 1,
1354
- devicesCreated: 1,
1355
- devicesFailed: 0,
1554
+ const result: AutoImportRuleRunResult = await runRule(false);
1555
+
1556
+ expect(monitorCreateMock).toHaveBeenCalledTimes(
1557
+ MAX_CONSECUTIVE_MONITOR_CREATE_FAILURES,
1558
+ );
1559
+ expect(result).toMatchObject({
1560
+ monitorsCreated: 0,
1561
+ monitorsFailed: hosts.length,
1562
+ monitorProvisioningHalted: true,
1563
+ isTruncated: false,
1564
+ });
1565
+ expect(result.monitorFailureReasons).toEqual([
1566
+ "You have reached the maximum allowed monitor limit for the free plan.",
1567
+ ]);
1356
1568
  });
1357
- expect(createMock).toHaveBeenCalledTimes(1);
1358
- expect(createdDevice(0).hostname).toBe("10.0.0.5");
1359
- });
1360
1569
 
1361
- describe("the create-failure protocol", () => {
1362
1570
  /*
1363
- * Names are unique per project while addresses are not, and two devices
1364
- * legitimately sharing a sysName is common on real estates. When the
1365
- * ADDRESS is still free after a failed create, the name was the problem
1366
- * — one retry under the address-suffixed fallback settles it.
1571
+ * The guard has to stay narrow, or it becomes a new way to lose monitors.
1572
+ * Failures that differ are per-host failures: every device still gets its
1573
+ * try.
1367
1574
  */
1368
- it("retries a name collision once under the fallback name", async () => {
1369
- scanFindOneByMock.mockResolvedValue(
1370
- makeScan({ discoveredDevices: [makeHost()] }),
1575
+ it("keeps trying every device when the failures are not identical", async () => {
1576
+ const hosts: Array<DiscoveredNetworkDevice> = Array.from(
1577
+ { length: 30 },
1578
+ (_value: unknown, index: number): DiscoveredNetworkDevice => {
1579
+ return makeHost({
1580
+ ipAddress: `10.4.0.${index}`,
1581
+ sysName: `varied-switch-${index}`,
1582
+ });
1583
+ },
1584
+ );
1585
+ const devices: Array<NetworkDevice> = hosts.map(
1586
+ (host: DiscoveredNetworkDevice): NetworkDevice => {
1587
+ return makeExistingDevice({
1588
+ id: ObjectID.generate(),
1589
+ hostname: host.ipAddress,
1590
+ name: host.sysName,
1591
+ });
1592
+ },
1371
1593
  );
1372
- createMock
1373
- .mockRejectedValueOnce(
1374
- new BadDataException("Network Device with this name already exists."),
1375
- )
1376
- .mockResolvedValueOnce({});
1377
- // The address re-check finds nothing: the name really was the problem.
1378
- deviceFindOneByMock.mockResolvedValue(null);
1379
1594
 
1380
- const result: AutoImportRuleRunResult | null = await processScan();
1595
+ ruleFindOneByMock.mockResolvedValue(
1596
+ makeRule({
1597
+ ipMatchTarget: "10.0.0.0/8",
1598
+ monitorTemplateId: TEMPLATE_ID,
1599
+ }),
1600
+ );
1601
+ ruleFindByMock.mockResolvedValue([]);
1602
+ deviceFindByMock.mockResolvedValue(devices);
1603
+ monitorFindByMock.mockResolvedValue([]);
1604
+ monitorFindOneByMock.mockResolvedValue(null);
1605
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
1381
1606
 
1382
- expect(createMock).toHaveBeenCalledTimes(2);
1383
- // Same device both times; only the name changed.
1384
- expect(createdDevice(0).name).toBe("core-switch-01");
1385
- expect(createdDevice(1).name).toBe("core-switch-01 (10.0.0.5)");
1386
- expect(createdDevice(1).name!.endsWith(" (10.0.0.5)")).toBe(true);
1387
- expect(createdDevice(1).hostname).toBe("10.0.0.5");
1607
+ let attempt: number = 0;
1608
+ monitorCreateMock.mockImplementation((): Promise<Monitor> => {
1609
+ attempt++;
1610
+ return Promise.reject(new Error(`name collision on device ${attempt}`));
1611
+ });
1612
+ mockRunNowScans([makeScan({ discoveredDevices: hosts })]);
1613
+
1614
+ const result: AutoImportRuleRunResult = await runRule(false);
1388
1615
 
1616
+ expect(monitorCreateMock).toHaveBeenCalledTimes(hosts.length);
1389
1617
  expect(result).toMatchObject({
1390
- devicesCreated: 1,
1391
- devicesFailed: 0,
1392
- hostsSkippedAlreadyRegistered: 0,
1618
+ monitorsFailed: hosts.length,
1619
+ monitorProvisioningHalted: false,
1393
1620
  });
1621
+ // Distinct reasons are reported, bounded.
1622
+ expect(result.monitorFailureReasons).toHaveLength(3);
1394
1623
  });
1395
1624
 
1396
1625
  /*
1397
- * Losing a race to a concurrent import of the SAME host must read as
1398
- * "already registered" retrying under a new name here would spawn a
1399
- * renamed twin of a device that now exists.
1626
+ * The guard must never cost an established estate its monitors. Once this
1627
+ * run has provisioned even one, the create pipeline is known to work, and
1628
+ * everything after it is a per-host failure however many of them there are
1629
+ * and however alike they read.
1400
1630
  */
1401
- it("reads a lost create race as already-registered, with no rename retry", async () => {
1402
- scanFindOneByMock.mockResolvedValue(
1403
- makeScan({ discoveredDevices: [makeHost()] }),
1631
+ it("never gives up once the run has created a monitor at all", async () => {
1632
+ const hosts: Array<DiscoveredNetworkDevice> = Array.from(
1633
+ { length: MAX_CONSECUTIVE_MONITOR_CREATE_FAILURES * 3 },
1634
+ (_value: unknown, index: number): DiscoveredNetworkDevice => {
1635
+ return makeHost({
1636
+ ipAddress: `10.7.0.${index}`,
1637
+ sysName: `established-switch-${index}`,
1638
+ });
1639
+ },
1404
1640
  );
1405
- createMock.mockRejectedValueOnce(
1406
- new BadDataException("Network Device with this name already exists."),
1641
+ const devices: Array<NetworkDevice> = hosts.map(
1642
+ (host: DiscoveredNetworkDevice): NetworkDevice => {
1643
+ return makeExistingDevice({
1644
+ id: ObjectID.generate(),
1645
+ hostname: host.ipAddress,
1646
+ name: host.sysName,
1647
+ });
1648
+ },
1407
1649
  );
1408
- // The address re-check finds a device: someone imported it meanwhile.
1409
- deviceFindOneByMock.mockResolvedValue({
1410
- id: new ObjectID("44444444-4444-4444-8444-444444444444"),
1411
- } as unknown as NetworkDevice);
1412
1650
 
1413
- const result: AutoImportRuleRunResult | null = await processScan();
1651
+ ruleFindOneByMock.mockResolvedValue(
1652
+ makeRule({
1653
+ ipMatchTarget: "10.0.0.0/8",
1654
+ monitorTemplateId: TEMPLATE_ID,
1655
+ }),
1656
+ );
1657
+ ruleFindByMock.mockResolvedValue([]);
1658
+ deviceFindByMock.mockResolvedValue(devices);
1659
+ monitorFindByMock.mockResolvedValue([]);
1660
+ monitorFindOneByMock.mockResolvedValue(null);
1661
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
1414
1662
 
1415
- expect(createMock).toHaveBeenCalledTimes(1);
1416
- expect(deviceFindOneByMock.mock.calls[0]![0].select.projectId).toBe(true);
1663
+ let call: number = 0;
1664
+ monitorCreateMock.mockImplementation(
1665
+ ({ data }: { data: Monitor }): Promise<Monitor> => {
1666
+ call++;
1667
+ if (call === 1) {
1668
+ return Promise.resolve(data);
1669
+ }
1670
+ return Promise.reject(new Error("name already in use"));
1671
+ },
1672
+ );
1673
+ mockRunNowScans([makeScan({ discoveredDevices: hosts })]);
1674
+
1675
+ const result: AutoImportRuleRunResult = await runRule(false);
1676
+
1677
+ expect(monitorCreateMock).toHaveBeenCalledTimes(hosts.length);
1417
1678
  expect(result).toMatchObject({
1418
- hostsMatched: 1,
1419
- hostsSkippedAlreadyRegistered: 1,
1420
- devicesCreated: 0,
1421
- devicesFailed: 0,
1679
+ monitorsCreated: 1,
1680
+ monitorsFailed: hosts.length - 1,
1681
+ monitorProvisioningHalted: false,
1422
1682
  });
1423
1683
  });
1424
- });
1684
+
1685
+ it("lets one success reset the systemic-failure streak", async () => {
1686
+ const hosts: Array<DiscoveredNetworkDevice> = Array.from(
1687
+ { length: MAX_CONSECUTIVE_MONITOR_CREATE_FAILURES * 2 - 1 },
1688
+ (_value: unknown, index: number): DiscoveredNetworkDevice => {
1689
+ return makeHost({
1690
+ ipAddress: `10.5.0.${index}`,
1691
+ sysName: `recovering-switch-${index}`,
1692
+ });
1693
+ },
1694
+ );
1695
+ const devices: Array<NetworkDevice> = hosts.map(
1696
+ (host: DiscoveredNetworkDevice): NetworkDevice => {
1697
+ return makeExistingDevice({
1698
+ id: ObjectID.generate(),
1699
+ hostname: host.ipAddress,
1700
+ name: host.sysName,
1701
+ });
1702
+ },
1703
+ );
1704
+
1705
+ ruleFindOneByMock.mockResolvedValue(
1706
+ makeRule({
1707
+ ipMatchTarget: "10.0.0.0/8",
1708
+ monitorTemplateId: TEMPLATE_ID,
1709
+ }),
1710
+ );
1711
+ ruleFindByMock.mockResolvedValue([]);
1712
+ deviceFindByMock.mockResolvedValue(devices);
1713
+ monitorFindByMock.mockResolvedValue([]);
1714
+ monitorFindOneByMock.mockResolvedValue(null);
1715
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
1716
+
1717
+ /*
1718
+ * Fail one short of the threshold, succeed once, then fail one short
1719
+ * again. Without the reset the second stretch would inherit the first
1720
+ * and halt the run.
1721
+ */
1722
+ let call: number = 0;
1723
+ monitorCreateMock.mockImplementation(
1724
+ ({ data }: { data: Monitor }): Promise<Monitor> => {
1725
+ call++;
1726
+ if (call === MAX_CONSECUTIVE_MONITOR_CREATE_FAILURES) {
1727
+ return Promise.resolve(data);
1728
+ }
1729
+ return Promise.reject(new Error("transient write failure"));
1730
+ },
1731
+ );
1732
+ mockRunNowScans([makeScan({ discoveredDevices: hosts })]);
1733
+
1734
+ const result: AutoImportRuleRunResult = await runRule(false);
1735
+
1736
+ expect(monitorCreateMock).toHaveBeenCalledTimes(hosts.length);
1737
+ expect(result).toMatchObject({
1738
+ monitorsCreated: 1,
1739
+ monitorsFailed: hosts.length - 1,
1740
+ monitorProvisioningHalted: false,
1741
+ });
1742
+ });
1743
+
1744
+ /*
1745
+ * Overlapping scans of the same subnet are normal, and a host in both is
1746
+ * ONE device with one missing monitor. The short circuit has to keep the
1747
+ * same (device, template) bookkeeping a real attempt does, or the report
1748
+ * inflates itself once per scan that mentions the host.
1749
+ */
1750
+ it("counts a halted device once even when two scans carry the same host", async () => {
1751
+ const host: DiscoveredNetworkDevice = makeHost({
1752
+ ipAddress: "10.6.0.1",
1753
+ sysName: "overlapping-switch",
1754
+ });
1755
+ const device: NetworkDevice = makeExistingDevice({
1756
+ id: ObjectID.generate(),
1757
+ hostname: host.ipAddress,
1758
+ name: host.sysName,
1759
+ });
1760
+
1761
+ ruleFindOneByMock.mockResolvedValue(
1762
+ makeRule({
1763
+ ipMatchTarget: "10.0.0.0/8",
1764
+ monitorTemplateId: TEMPLATE_ID,
1765
+ }),
1766
+ );
1767
+ ruleFindByMock.mockResolvedValue([]);
1768
+ deviceFindByMock.mockResolvedValue([device]);
1769
+ monitorTemplateFindByMock.mockResolvedValue([]);
1770
+ mockRunNowScans([
1771
+ makeScan({ discoveredDevices: [host] }),
1772
+ makeScan({ discoveredDevices: [host] }),
1773
+ ]);
1774
+
1775
+ const result: AutoImportRuleRunResult = await runRule(false);
1776
+
1777
+ expect(result).toMatchObject({
1778
+ hostsMatched: 2,
1779
+ monitorsFailed: 1,
1780
+ monitorProvisioningHalted: true,
1781
+ });
1782
+ });
1783
+
1784
+ it("records why a device could not be imported", async () => {
1785
+ scanFindOneByMock.mockResolvedValue(
1786
+ makeScan({ discoveredDevices: [makeHost()] }),
1787
+ );
1788
+ ruleFindByMock.mockResolvedValue([makeRule()]);
1789
+ deviceFindOneByMock.mockResolvedValue(null);
1790
+ createMock.mockRejectedValue(
1791
+ new BadDataException("Network device name is too long."),
1792
+ );
1793
+
1794
+ const result: AutoImportRuleRunResult | null = await processScan();
1795
+
1796
+ expect(result).toMatchObject({ devicesCreated: 0, devicesFailed: 1 });
1797
+ expect(result?.deviceFailureReasons).toEqual([
1798
+ "Network device name is too long.",
1799
+ ]);
1800
+ });
1801
+ });
1802
+
1803
+ /*
1804
+ * An over-long "address" is junk that would fail the varchar(100)
1805
+ * hostname on every pass, identically, forever — so it is refused before
1806
+ * any counting rather than logged as a devicesFailed per worker tick.
1807
+ */
1808
+ it("skips a host whose address could never be a hostname, before any counting", async () => {
1809
+ scanFindOneByMock.mockResolvedValue(
1810
+ makeScan({
1811
+ discoveredDevices: [
1812
+ makeHost({ ipAddress: "9".repeat(101) }),
1813
+ makeHost(),
1814
+ ],
1815
+ }),
1816
+ );
1817
+
1818
+ const result: AutoImportRuleRunResult | null = await processScan();
1819
+
1820
+ // The junk row was never evaluated, let alone matched or attempted.
1821
+ expect(result).toMatchObject({
1822
+ hostsEvaluated: 1,
1823
+ hostsMatched: 1,
1824
+ devicesCreated: 1,
1825
+ devicesFailed: 0,
1826
+ });
1827
+ expect(createMock).toHaveBeenCalledTimes(1);
1828
+ expect(createdDevice(0).hostname).toBe("10.0.0.5");
1829
+ });
1830
+
1831
+ describe("the create-failure protocol", () => {
1832
+ /*
1833
+ * Names are unique per project while addresses are not, and two devices
1834
+ * legitimately sharing a sysName is common on real estates. When the
1835
+ * ADDRESS is still free after a failed create, the name was the problem
1836
+ * — one retry under the address-suffixed fallback settles it.
1837
+ */
1838
+ it("retries a name collision once under the fallback name", async () => {
1839
+ scanFindOneByMock.mockResolvedValue(
1840
+ makeScan({ discoveredDevices: [makeHost()] }),
1841
+ );
1842
+ createMock
1843
+ .mockRejectedValueOnce(
1844
+ new BadDataException("Network Device with this name already exists."),
1845
+ )
1846
+ .mockResolvedValueOnce({});
1847
+ // The address re-check finds nothing: the name really was the problem.
1848
+ deviceFindOneByMock.mockResolvedValue(null);
1849
+
1850
+ const result: AutoImportRuleRunResult | null = await processScan();
1851
+
1852
+ expect(createMock).toHaveBeenCalledTimes(2);
1853
+ // Same device both times; only the name changed.
1854
+ expect(createdDevice(0).name).toBe("core-switch-01");
1855
+ expect(createdDevice(1).name).toBe("core-switch-01 (10.0.0.5)");
1856
+ expect(createdDevice(1).name!.endsWith(" (10.0.0.5)")).toBe(true);
1857
+ expect(createdDevice(1).hostname).toBe("10.0.0.5");
1858
+
1859
+ expect(result).toMatchObject({
1860
+ devicesCreated: 1,
1861
+ devicesFailed: 0,
1862
+ hostsSkippedAlreadyRegistered: 0,
1863
+ });
1864
+ });
1865
+
1866
+ /*
1867
+ * Losing a race to a concurrent import of the SAME host must read as
1868
+ * "already registered" — retrying under a new name here would spawn a
1869
+ * renamed twin of a device that now exists.
1870
+ */
1871
+ it("reads a lost create race as already-registered, with no rename retry", async () => {
1872
+ scanFindOneByMock.mockResolvedValue(
1873
+ makeScan({ discoveredDevices: [makeHost()] }),
1874
+ );
1875
+ createMock.mockRejectedValueOnce(
1876
+ new BadDataException("Network Device with this name already exists."),
1877
+ );
1878
+ // The address re-check finds a device: someone imported it meanwhile.
1879
+ deviceFindOneByMock.mockResolvedValue({
1880
+ id: new ObjectID("44444444-4444-4444-8444-444444444444"),
1881
+ } as unknown as NetworkDevice);
1882
+
1883
+ const result: AutoImportRuleRunResult | null = await processScan();
1884
+
1885
+ expect(createMock).toHaveBeenCalledTimes(1);
1886
+ expect(deviceFindOneByMock.mock.calls[0]![0].select.projectId).toBe(true);
1887
+ expect(result).toMatchObject({
1888
+ hostsMatched: 1,
1889
+ hostsSkippedAlreadyRegistered: 1,
1890
+ devicesCreated: 0,
1891
+ devicesFailed: 0,
1892
+ });
1893
+ });
1894
+ });
1425
1895
 
1426
1896
  /*
1427
1897
  * The cap turns "a typo'd rule meets a huge subnet" into a few paced
@@ -1779,9 +2249,20 @@ describe("NetworkDeviceAutoImportRuleEngineService.applyRuleToCompletedScans", (
1779
2249
 
1780
2250
  expect(scanFindOneByMock).toHaveBeenCalledTimes(1);
1781
2251
  const rereadCall: any = scanFindOneByMock.mock.calls[0]![0];
1782
- expect(rereadCall.query.status).toBe("Completed");
2252
+ /*
2253
+ * The status filter is a SET now, not the single "Completed" it used to
2254
+ * be: a scan that is still sweeping already holds real results, and
2255
+ * refusing to look at them is what left 527 discovered switches
2256
+ * unimportable for a day (issue #3599).
2257
+ */
2258
+ expect(statusesMatchedBy(rereadCall.query.status)).toEqual([
2259
+ "Completed",
2260
+ "In Progress",
2261
+ ]);
1783
2262
  expect(rereadCall.query.projectId.toString()).toBe(PROJECT_ID.toString());
1784
2263
  expect(rereadCall.select.discoveredDevices).toBe(true);
2264
+ expect(rereadCall.props).toEqual({ isRoot: true, ignoreHooks: true });
2265
+ expect(listCall.props).toEqual({ isRoot: true });
1785
2266
  });
1786
2267
 
1787
2268
  it("skips a scan that vanished between the stub listing and the re-read", async () => {
@@ -2025,3 +2506,799 @@ describe("NetworkDeviceAutoImportRuleEngineService.applyRuleToCompletedScans", (
2025
2506
  });
2026
2507
  });
2027
2508
  });
2509
+
2510
+ describe("re-import after deleting discovered devices or monitors (issue #3560)", () => {
2511
+ it.each([false, true])(
2512
+ "uses current inventory when a processed scan still marks a deleted device registered (dry run: %s)",
2513
+ async (isDryRun: boolean) => {
2514
+ ruleFindOneByMock.mockResolvedValue(
2515
+ makeRule({ monitorTemplateId: TEMPLATE_ID }),
2516
+ );
2517
+ ruleFindByMock.mockResolvedValue([]);
2518
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
2519
+ mockRunNowScans([
2520
+ makeScan({
2521
+ completedAt: new Date("2020-01-01T00:00:00.000Z"),
2522
+ autoImportProcessedAt: OneUptimeDate.getCurrentDate(),
2523
+ discoveredDevices: [makeHost({ isAlreadyRegistered: true })],
2524
+ }),
2525
+ ]);
2526
+
2527
+ const result: AutoImportRuleRunResult = await runRule(isDryRun);
2528
+
2529
+ expect(result).toMatchObject({
2530
+ hostsMatched: 1,
2531
+ hostsSkippedAlreadyRegistered: 0,
2532
+ devicesCreated: isDryRun ? 0 : 1,
2533
+ monitorsCreated: isDryRun ? 0 : 1,
2534
+ monitorsWouldCreate: isDryRun ? 1 : 0,
2535
+ devicesFailed: 0,
2536
+ monitorsFailed: 0,
2537
+ matchedIpAddressSample: ["10.0.0.5"],
2538
+ isTruncated: false,
2539
+ isDryRun,
2540
+ });
2541
+ expect(createMock).toHaveBeenCalledTimes(isDryRun ? 0 : 1);
2542
+ expect(monitorCreateMock).toHaveBeenCalledTimes(isDryRun ? 0 : 1);
2543
+ expect(scanUpdateMock).toHaveBeenCalledTimes(isDryRun ? 0 : 1);
2544
+
2545
+ if (!isDryRun) {
2546
+ const device: NetworkDevice = createdDevice(0);
2547
+ expect(device.hostname).toBe("10.0.0.5");
2548
+ expect(device.snmpCommunityString).toBe("public");
2549
+ expect(device.projectId?.toString()).toBe(PROJECT_ID.toString());
2550
+ expect(
2551
+ provisionedMonitor(0).autoProvisionedNetworkDeviceId?.toString(),
2552
+ ).toBe(device.id?.toString());
2553
+ expect(scanUpdateMock.mock.calls[0]![0].data).toEqual({
2554
+ discoveredDevices: [makeHost({ isAlreadyRegistered: true })],
2555
+ });
2556
+ }
2557
+ },
2558
+ );
2559
+
2560
+ it("imports a deleted device from fresh automatic results with a stale registration flag", async () => {
2561
+ scanFindOneByMock.mockResolvedValue(
2562
+ makeScan({
2563
+ discoveredDevices: [makeHost({ isAlreadyRegistered: true })],
2564
+ }),
2565
+ );
2566
+
2567
+ const result: AutoImportRuleRunResult | null = await processScan();
2568
+
2569
+ expect(result).toMatchObject({
2570
+ devicesCreated: 1,
2571
+ hostsSkippedAlreadyRegistered: 0,
2572
+ });
2573
+ expect(
2574
+ scanUpdateMock.mock.calls[0]![0].data.autoImportProcessedAt,
2575
+ ).toBeInstanceOf(Date);
2576
+ });
2577
+
2578
+ it.each([false, true])(
2579
+ "restores a deleted template monitor while retaining its registered device (dry run: %s)",
2580
+ async (isDryRun: boolean) => {
2581
+ const existingDevice: NetworkDevice = makeExistingDevice();
2582
+ deviceFindByMock.mockResolvedValue([existingDevice]);
2583
+ ruleFindOneByMock.mockResolvedValue(
2584
+ makeRule({ monitorTemplateId: TEMPLATE_ID }),
2585
+ );
2586
+ ruleFindByMock.mockResolvedValue([]);
2587
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
2588
+ mockRunNowScans([
2589
+ makeScan({
2590
+ autoImportProcessedAt: OneUptimeDate.getCurrentDate(),
2591
+ discoveredDevices: [makeHost({ isAlreadyRegistered: true })],
2592
+ }),
2593
+ ]);
2594
+
2595
+ const result: AutoImportRuleRunResult = await runRule(isDryRun);
2596
+
2597
+ expect(result).toMatchObject({
2598
+ hostsSkippedAlreadyRegistered: 1,
2599
+ devicesCreated: 0,
2600
+ monitorsCreated: isDryRun ? 0 : 1,
2601
+ monitorsWouldCreate: isDryRun ? 1 : 0,
2602
+ monitorsSkippedAlreadyExisting: 0,
2603
+ });
2604
+ expect(createMock).not.toHaveBeenCalled();
2605
+ expect(scanUpdateMock).not.toHaveBeenCalled();
2606
+ if (!isDryRun) {
2607
+ expect(
2608
+ provisionedMonitor(0).autoProvisionedNetworkDeviceId?.toString(),
2609
+ ).toBe(existingDevice.id?.toString());
2610
+ }
2611
+ },
2612
+ );
2613
+
2614
+ it.each([false, true])(
2615
+ "deduplicates stale registered rows within and across scans (dry run: %s)",
2616
+ async (isDryRun: boolean) => {
2617
+ ruleFindOneByMock.mockResolvedValue(
2618
+ makeRule({ monitorTemplateId: TEMPLATE_ID }),
2619
+ );
2620
+ ruleFindByMock.mockResolvedValue([]);
2621
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
2622
+ mockRunNowScans([
2623
+ makeScan({
2624
+ discoveredDevices: [
2625
+ makeHost({ isAlreadyRegistered: true }),
2626
+ makeHost({ ipAddress: " 10.0.0.5 ", isAlreadyRegistered: false }),
2627
+ ],
2628
+ }),
2629
+ makeScan({
2630
+ id: ObjectID.generate(),
2631
+ discoveredDevices: [makeHost({ isAlreadyRegistered: true })],
2632
+ }),
2633
+ ]);
2634
+
2635
+ const result: AutoImportRuleRunResult = await runRule(isDryRun);
2636
+
2637
+ expect(result).toMatchObject({
2638
+ hostsMatched: 3,
2639
+ hostsSkippedAlreadyRegistered: 2,
2640
+ devicesCreated: isDryRun ? 0 : 1,
2641
+ monitorsCreated: isDryRun ? 0 : 1,
2642
+ monitorsWouldCreate: isDryRun ? 1 : 0,
2643
+ monitorsSkippedAlreadyExisting: 2,
2644
+ matchedIpAddressSample: ["10.0.0.5"],
2645
+ });
2646
+ expect(createMock).toHaveBeenCalledTimes(isDryRun ? 0 : 1);
2647
+ expect(monitorCreateMock).toHaveBeenCalledTimes(isDryRun ? 0 : 1);
2648
+ },
2649
+ );
2650
+
2651
+ it("still honors exclusions when the registered device has been deleted", async () => {
2652
+ ruleFindOneByMock.mockResolvedValue(makeRule());
2653
+ ruleFindByMock.mockResolvedValue([
2654
+ makeRule({
2655
+ id: ObjectID.generate(),
2656
+ isExclusion: true,
2657
+ ipMatchTarget: "10.0.0.5",
2658
+ }),
2659
+ ]);
2660
+ mockRunNowScans([
2661
+ makeScan({
2662
+ discoveredDevices: [makeHost({ isAlreadyRegistered: true })],
2663
+ }),
2664
+ ]);
2665
+
2666
+ const result: AutoImportRuleRunResult = await runRule(false);
2667
+
2668
+ expect(result).toMatchObject({
2669
+ hostsExcluded: 1,
2670
+ hostsMatched: 0,
2671
+ devicesCreated: 0,
2672
+ });
2673
+ expect(createMock).not.toHaveBeenCalled();
2674
+ expect(scanUpdateMock).not.toHaveBeenCalled();
2675
+ });
2676
+
2677
+ it("resumes re-importing a deleted 903-device fleet across the run cap without duplicates", async () => {
2678
+ const hosts: Array<DiscoveredNetworkDevice> = Array.from(
2679
+ { length: 903 },
2680
+ (_value: unknown, index: number): DiscoveredNetworkDevice => {
2681
+ return makeHost({
2682
+ ipAddress: `10.0.${Math.floor(index / 256)}.${index % 256}`,
2683
+ sysName: `switch-${index}`,
2684
+ isAlreadyRegistered: true,
2685
+ });
2686
+ },
2687
+ );
2688
+ const scan: NetworkDeviceDiscoveryScan = makeScan({
2689
+ autoImportProcessedAt: OneUptimeDate.getCurrentDate(),
2690
+ discoveredDevices: hosts,
2691
+ });
2692
+ const inventory: Map<string, NetworkDevice> = new Map();
2693
+ devicesByHostnamesMock.mockImplementation(
2694
+ async (data: {
2695
+ hostnames: Array<string>;
2696
+ }): Promise<Map<string, NetworkDevice>> => {
2697
+ const found: Map<string, NetworkDevice> = new Map();
2698
+ for (const hostname of data.hostnames) {
2699
+ const device: NetworkDevice | undefined = inventory.get(hostname);
2700
+ if (device) {
2701
+ found.set(hostname, device);
2702
+ }
2703
+ }
2704
+ return found;
2705
+ },
2706
+ );
2707
+ createMock.mockImplementation(
2708
+ async ({ data }: { data: NetworkDevice }): Promise<NetworkDevice> => {
2709
+ data.id = ObjectID.generate();
2710
+ inventory.set(data.hostname!, data);
2711
+ return data;
2712
+ },
2713
+ );
2714
+ ruleFindOneByMock.mockResolvedValue(
2715
+ makeRule({ ipMatchTarget: "10.0.0.0/8" }),
2716
+ );
2717
+ ruleFindByMock.mockResolvedValue([]);
2718
+
2719
+ mockRunNowScans([scan]);
2720
+ const firstRun: AutoImportRuleRunResult = await runRule(false);
2721
+ expect(firstRun).toMatchObject({
2722
+ devicesCreated: MAX_DEVICES_PER_AUTO_IMPORT_RUN,
2723
+ hostsSkippedAlreadyRegistered: 0,
2724
+ isTruncated: true,
2725
+ });
2726
+
2727
+ mockRunNowScans([scan]);
2728
+ const secondRun: AutoImportRuleRunResult = await runRule(false);
2729
+ expect(secondRun).toMatchObject({
2730
+ devicesCreated: hosts.length - MAX_DEVICES_PER_AUTO_IMPORT_RUN,
2731
+ hostsSkippedAlreadyRegistered: MAX_DEVICES_PER_AUTO_IMPORT_RUN,
2732
+ isTruncated: false,
2733
+ });
2734
+
2735
+ mockRunNowScans([scan]);
2736
+ const thirdRun: AutoImportRuleRunResult = await runRule(false);
2737
+ expect(thirdRun).toMatchObject({
2738
+ devicesCreated: 0,
2739
+ hostsSkippedAlreadyRegistered: hosts.length,
2740
+ isTruncated: false,
2741
+ });
2742
+ expect(inventory.size).toBe(hosts.length);
2743
+ expect(createMock).toHaveBeenCalledTimes(hosts.length);
2744
+ expect(scanUpdateMock).toHaveBeenCalledTimes(2);
2745
+ });
2746
+ });
2747
+
2748
+ /*
2749
+ * github.com/OneUptime/oneuptime/issues/3599 — "Auto Import Rule can't see
2750
+ * already-discovered devices while the Discovery Scan is still running".
2751
+ *
2752
+ * A discovery sweep reports incrementally now: the probe uploads what it has
2753
+ * found every 30 seconds, so an In Progress scan's row already holds real
2754
+ * hosts. The engine used to read only Completed scans, which meant that on the
2755
+ * 24-hour sweep of #3598 the 527 switches it had already found — stored,
2756
+ * visible in the product, correct — were unimportable for the whole day.
2757
+ *
2758
+ * These tests pin that a running scan's results are treated as first-class,
2759
+ * and that the marker protocol still holds when the row underneath is moving.
2760
+ */
2761
+ describe("auto-import from a scan that is still sweeping (issue #3599)", () => {
2762
+ /*
2763
+ * A scan mid-sweep: no completedAt (the run has not ended), and the two
2764
+ * host counters its partial uploads have been moving.
2765
+ */
2766
+ function makeInProgressScan(
2767
+ overrides: Record<string, unknown> = {},
2768
+ ): NetworkDeviceDiscoveryScan {
2769
+ return makeScan({
2770
+ status: "In Progress",
2771
+ completedAt: undefined,
2772
+ scannedHostCount: 4096,
2773
+ respondedHostCount: 12,
2774
+ ...overrides,
2775
+ });
2776
+ }
2777
+
2778
+ it("imports the hosts a running sweep has already found", async () => {
2779
+ scanFindOneByMock.mockResolvedValue(
2780
+ makeInProgressScan({ discoveredDevices: [makeHost()] }),
2781
+ );
2782
+ ruleFindByMock.mockResolvedValue([makeRule()]);
2783
+
2784
+ const result: AutoImportRuleRunResult | null = await processScan();
2785
+
2786
+ expect(result).toMatchObject({ hostsMatched: 1, devicesCreated: 1 });
2787
+ expect(createdDevice(0).hostname).toBe("10.0.0.5");
2788
+ });
2789
+
2790
+ it("asks for a running scan as well as a finished one", async () => {
2791
+ scanFindOneByMock.mockResolvedValue(makeInProgressScan());
2792
+ ruleFindByMock.mockResolvedValue([makeRule()]);
2793
+
2794
+ await processScan();
2795
+
2796
+ const query: any = scanFindOneByMock.mock.calls[0]![0].query;
2797
+
2798
+ expect(statusesMatchedBy(query.status)).toEqual([
2799
+ "Completed",
2800
+ "In Progress",
2801
+ ]);
2802
+ });
2803
+
2804
+ /*
2805
+ * The freshness horizon retires results older than MAX_RESULT_AGE_IN_HOURS
2806
+ * without importing them. It is keyed on completedAt, which a running scan
2807
+ * does not have — and must not be read as "infinitely old". A sweep that has
2808
+ * been running for two hours is the opposite of stale.
2809
+ */
2810
+ it("never treats a running scan's results as too old to import", async () => {
2811
+ scanFindOneByMock.mockResolvedValue(
2812
+ makeInProgressScan({ discoveredDevices: [makeHost()] }),
2813
+ );
2814
+ ruleFindByMock.mockResolvedValue([makeRule()]);
2815
+
2816
+ const result: AutoImportRuleRunResult | null = await processScan();
2817
+
2818
+ expect(result).toMatchObject({ devicesCreated: 1 });
2819
+ expect(loggerWarnMock).not.toHaveBeenCalledWith(
2820
+ expect.stringContaining("stamping it processed without importing"),
2821
+ expect.anything(),
2822
+ );
2823
+ });
2824
+
2825
+ /*
2826
+ * The marker protocol, on a row that is still moving.
2827
+ *
2828
+ * A running scan's status and completedAt do not change between uploads, so
2829
+ * they cannot by themselves tell "the results I evaluated" from "the results
2830
+ * that landed while I was evaluating". The host counters can, and they are
2831
+ * what a partial upload moves.
2832
+ */
2833
+ it("guards the stamp on the run state it actually read, counters included", async () => {
2834
+ scanFindOneByMock.mockResolvedValue(
2835
+ makeInProgressScan({ discoveredDevices: [makeHost()] }),
2836
+ );
2837
+ ruleFindByMock.mockResolvedValue([makeRule()]);
2838
+
2839
+ await processScan();
2840
+
2841
+ const stamp: any = scanUpdateMock.mock.calls[0]![0];
2842
+
2843
+ expect(stamp.expectedData).toEqual({
2844
+ status: "In Progress",
2845
+ completedAt: null,
2846
+ scannedHostCount: 4096,
2847
+ respondedHostCount: 12,
2848
+ });
2849
+ });
2850
+
2851
+ // The counters are only a version token if they are actually read.
2852
+ it("selects the counters it guards on", async () => {
2853
+ scanFindOneByMock.mockResolvedValue(makeInProgressScan());
2854
+ ruleFindByMock.mockResolvedValue([makeRule()]);
2855
+
2856
+ await processScan();
2857
+
2858
+ const select: any = scanFindOneByMock.mock.calls[0]![0].select;
2859
+
2860
+ expect(select.scannedHostCount).toBe(true);
2861
+ expect(select.respondedHostCount).toBe(true);
2862
+ expect(select.status).toBe(true);
2863
+ expect(select.completedAt).toBe(true);
2864
+ });
2865
+
2866
+ /*
2867
+ * The write-back still retires the hosts this pass consumed, so the Review
2868
+ * dialog stops offering them — on a running scan just as on a finished one.
2869
+ */
2870
+ it("flips isAlreadyRegistered on the rows it imported", async () => {
2871
+ scanFindOneByMock.mockResolvedValue(
2872
+ makeInProgressScan({
2873
+ discoveredDevices: [makeHost(), makeHost({ ipAddress: "10.0.0.99" })],
2874
+ }),
2875
+ );
2876
+ ruleFindByMock.mockResolvedValue([makeRule({ ipMatchTarget: "10.0.0.5" })]);
2877
+
2878
+ await processScan();
2879
+
2880
+ const written: Array<DiscoveredNetworkDevice> = scanUpdateMock.mock
2881
+ .calls[0]![0].data.discoveredDevices as Array<DiscoveredNetworkDevice>;
2882
+
2883
+ expect(written[0]!.isAlreadyRegistered).toBe(true);
2884
+ expect(written[1]!.isAlreadyRegistered).toBeUndefined();
2885
+ });
2886
+
2887
+ /*
2888
+ * A running scan is offered again on the next tick — its next partial upload
2889
+ * clears the marker — so an address it already imported must read as
2890
+ * registered rather than being created twice.
2891
+ */
2892
+ it("skips an address it already imported when the scan is offered again", async () => {
2893
+ const alreadyImported: NetworkDevice = {
2894
+ id: new ObjectID("55555555-5555-4555-8555-555555555555"),
2895
+ projectId: PROJECT_ID,
2896
+ hostname: "10.0.0.5",
2897
+ } as unknown as NetworkDevice;
2898
+
2899
+ deviceFindByMock.mockResolvedValue([alreadyImported]);
2900
+ scanFindOneByMock.mockResolvedValue(
2901
+ makeInProgressScan({ discoveredDevices: [makeHost()] }),
2902
+ );
2903
+ ruleFindByMock.mockResolvedValue([makeRule()]);
2904
+
2905
+ const result: AutoImportRuleRunResult | null = await processScan();
2906
+
2907
+ expect(createMock).not.toHaveBeenCalled();
2908
+ expect(result).toMatchObject({
2909
+ hostsMatched: 1,
2910
+ devicesCreated: 0,
2911
+ hostsSkippedAlreadyRegistered: 1,
2912
+ });
2913
+ });
2914
+
2915
+ /*
2916
+ * The manual path, which is where the report came from: "Run Now" said it
2917
+ * had checked 25 hosts while 527 sat in the Devices list, because the scan
2918
+ * that found them had not finished.
2919
+ */
2920
+ it("Run Now reads a running scan's results too", async () => {
2921
+ ruleFindOneByMock.mockResolvedValue(makeRule());
2922
+ ruleFindByMock.mockResolvedValue([]);
2923
+ mockRunNowScans([makeInProgressScan({ discoveredDevices: [makeHost()] })]);
2924
+
2925
+ const result: AutoImportRuleRunResult = await runRule(true);
2926
+
2927
+ expect(result).toMatchObject({ hostsEvaluated: 1, hostsMatched: 1 });
2928
+
2929
+ const listQuery: any = scanFindByMock.mock.calls[0]![0].query;
2930
+ expect(statusesMatchedBy(listQuery.status)).toEqual([
2931
+ "Completed",
2932
+ "In Progress",
2933
+ ]);
2934
+ });
2935
+ });
2936
+
2937
+ /*
2938
+ * OneUptime issue #3642: a discovery scan found 909 routers, the operator ran
2939
+ * the auto-import rule, "500+" arrived, and the remaining 400-odd were
2940
+ * "simply missing, with no error shown".
2941
+ *
2942
+ * The per-run caps were doing their job. What made them read as silence is
2943
+ * that a capped run used to STOP EVALUATING at the cap, so every number it
2944
+ * reported described only the part of the estate it happened to reach: 909
2945
+ * became "matched 500 out of the 500 discovered hosts it looked at", and
2946
+ * nothing in the answer said how many were left or that the monitor half of
2947
+ * the work has its own separate ceiling.
2948
+ *
2949
+ * These pin the fix at the engine: writes stop at the cap, counting does not.
2950
+ */
2951
+ describe("a capped run reports the remainder (issue #3642)", () => {
2952
+ // A fleet whose hosts all match, addressed so no two collide.
2953
+ function makeFleet(size: number): Array<DiscoveredNetworkDevice> {
2954
+ return Array.from(
2955
+ { length: size },
2956
+ (_value: unknown, index: number): DiscoveredNetworkDevice => {
2957
+ return makeHost({
2958
+ ipAddress: `10.5.${Math.floor(index / 256)}.${index % 256}`,
2959
+ sysName: `WANRTR-${index}`,
2960
+ });
2961
+ },
2962
+ );
2963
+ }
2964
+
2965
+ function makeInventoryFor(
2966
+ hosts: Array<DiscoveredNetworkDevice>,
2967
+ ): Array<NetworkDevice> {
2968
+ return hosts.map((host: DiscoveredNetworkDevice): NetworkDevice => {
2969
+ return makeExistingDevice({
2970
+ id: ObjectID.generate(),
2971
+ hostname: host.ipAddress,
2972
+ name: host.sysName,
2973
+ });
2974
+ });
2975
+ }
2976
+
2977
+ it("counts the hosts the device cap left un-imported, and still evaluates them all", async () => {
2978
+ const hosts: Array<DiscoveredNetworkDevice> = makeFleet(909);
2979
+
2980
+ scanFindOneByMock.mockResolvedValue(makeScan({ discoveredDevices: hosts }));
2981
+ ruleFindByMock.mockResolvedValue([
2982
+ makeRule({ ipMatchTarget: "10.0.0.0/8" }),
2983
+ ]);
2984
+
2985
+ const result: AutoImportRuleRunResult | null = await processScan();
2986
+
2987
+ // Writes stop at the cap…
2988
+ expect(createMock).toHaveBeenCalledTimes(MAX_DEVICES_PER_AUTO_IMPORT_RUN);
2989
+ expect(result).toMatchObject({
2990
+ devicesCreated: MAX_DEVICES_PER_AUTO_IMPORT_RUN,
2991
+ isTruncated: true,
2992
+ // …counting does not: the whole scan is accounted for.
2993
+ hostsEvaluated: 909,
2994
+ hostsMatched: 909,
2995
+ hostsPendingImport: 909 - MAX_DEVICES_PER_AUTO_IMPORT_RUN,
2996
+ monitorsPendingCreation: 0,
2997
+ });
2998
+ });
2999
+
3000
+ /*
3001
+ * The reporter's own shape: 909 devices already in the inventory, zero
3002
+ * monitors, and a rule that has since had a Monitor Template attached. No
3003
+ * device is created at all, so the DEVICE cap is never even approached —
3004
+ * only the separate monitor cap stops the run.
3005
+ */
3006
+ it("counts the devices the monitor cap left unmonitored", async () => {
3007
+ const hosts: Array<DiscoveredNetworkDevice> = makeFleet(909);
3008
+
3009
+ scanFindOneByMock.mockResolvedValue(makeScan({ discoveredDevices: hosts }));
3010
+ deviceFindByMock.mockResolvedValue(makeInventoryFor(hosts));
3011
+ ruleFindByMock.mockResolvedValue([
3012
+ makeRule({ ipMatchTarget: "10.0.0.0/8", monitorTemplateId: TEMPLATE_ID }),
3013
+ ]);
3014
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
3015
+
3016
+ const result: AutoImportRuleRunResult | null = await processScan();
3017
+
3018
+ expect(monitorCreateMock).toHaveBeenCalledTimes(
3019
+ MAX_MONITORS_PER_AUTO_IMPORT_RUN,
3020
+ );
3021
+ expect(result).toMatchObject({
3022
+ devicesCreated: 0,
3023
+ monitorsCreated: MAX_MONITORS_PER_AUTO_IMPORT_RUN,
3024
+ isTruncated: true,
3025
+ hostsEvaluated: 909,
3026
+ hostsMatched: 909,
3027
+ hostsSkippedAlreadyRegistered: 909,
3028
+ // The number the report was missing.
3029
+ monitorsPendingCreation: 909 - MAX_MONITORS_PER_AUTO_IMPORT_RUN,
3030
+ hostsPendingImport: 0,
3031
+ });
3032
+ });
3033
+
3034
+ /*
3035
+ * A device cap reached first must not be reported as a monitor shortfall:
3036
+ * the hosts past it have no device yet, so what they are waiting on is the
3037
+ * import, not a monitor.
3038
+ */
3039
+ it("does not double-count un-imported hosts as unmonitored devices", async () => {
3040
+ const hosts: Array<DiscoveredNetworkDevice> = makeFleet(600);
3041
+
3042
+ scanFindOneByMock.mockResolvedValue(makeScan({ discoveredDevices: hosts }));
3043
+ ruleFindByMock.mockResolvedValue([
3044
+ makeRule({ ipMatchTarget: "10.0.0.0/8", monitorTemplateId: TEMPLATE_ID }),
3045
+ ]);
3046
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
3047
+
3048
+ const result: AutoImportRuleRunResult | null = await processScan();
3049
+
3050
+ expect(result).toMatchObject({
3051
+ devicesCreated: MAX_DEVICES_PER_AUTO_IMPORT_RUN,
3052
+ hostsPendingImport: 600 - MAX_DEVICES_PER_AUTO_IMPORT_RUN,
3053
+ monitorsPendingCreation: 0,
3054
+ isTruncated: true,
3055
+ });
3056
+ });
3057
+
3058
+ /*
3059
+ * Where the issue actually lands for an operator: pressing "Run Rule"
3060
+ * twice must finish the 909, and the second press must report nothing
3061
+ * left. (The dashboard now chains these passes itself — see
3062
+ * AutoImportRunChain — so this is the server half of one button press.)
3063
+ */
3064
+ it("finishes the 909-device monitor backfill over two manual runs", async () => {
3065
+ const hosts: Array<DiscoveredNetworkDevice> = makeFleet(909);
3066
+ const devices: Array<NetworkDevice> = makeInventoryFor(hosts);
3067
+ const scan: NetworkDeviceDiscoveryScan = makeScan({
3068
+ discoveredDevices: hosts,
3069
+ });
3070
+
3071
+ /*
3072
+ * Monitors accumulate across the two runs the way the database would:
3073
+ * the second run's project-wide snapshot sees what the first created,
3074
+ * and its provenance keys are what make the resume idempotent.
3075
+ */
3076
+ const provisioned: Array<Monitor> = [];
3077
+ monitorCreateMock.mockImplementation(
3078
+ ({ data }: { data: Monitor }): Promise<Monitor> => {
3079
+ data.id = data.id || ObjectID.generate();
3080
+ provisioned.push(data);
3081
+ return Promise.resolve(data);
3082
+ },
3083
+ );
3084
+ monitorFindByMock.mockImplementation((): Promise<Array<Monitor>> => {
3085
+ return Promise.resolve(provisioned);
3086
+ });
3087
+
3088
+ ruleFindOneByMock.mockResolvedValue(
3089
+ makeRule({ ipMatchTarget: "10.0.0.0/8", monitorTemplateId: TEMPLATE_ID }),
3090
+ );
3091
+ ruleFindByMock.mockResolvedValue([]);
3092
+ deviceFindByMock.mockResolvedValue(devices);
3093
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
3094
+
3095
+ mockRunNowScans([scan]);
3096
+ const firstRun: AutoImportRuleRunResult = await runRule(false);
3097
+
3098
+ expect(firstRun).toMatchObject({
3099
+ monitorsCreated: MAX_MONITORS_PER_AUTO_IMPORT_RUN,
3100
+ monitorsPendingCreation: 909 - MAX_MONITORS_PER_AUTO_IMPORT_RUN,
3101
+ isTruncated: true,
3102
+ });
3103
+
3104
+ mockRunNowScans([scan]);
3105
+ const secondRun: AutoImportRuleRunResult = await runRule(false);
3106
+
3107
+ expect(secondRun).toMatchObject({
3108
+ monitorsCreated: 909 - MAX_MONITORS_PER_AUTO_IMPORT_RUN,
3109
+ monitorsSkippedAlreadyExisting: MAX_MONITORS_PER_AUTO_IMPORT_RUN,
3110
+ monitorsPendingCreation: 0,
3111
+ isTruncated: false,
3112
+ });
3113
+
3114
+ // Every router ends up monitored exactly once.
3115
+ expect(provisioned).toHaveLength(909);
3116
+ expect(
3117
+ new Set(
3118
+ provisioned.map((monitor: Monitor): string => {
3119
+ return monitor.autoProvisionedNetworkDeviceId!.toString();
3120
+ }),
3121
+ ).size,
3122
+ ).toBe(909);
3123
+ });
3124
+
3125
+ /*
3126
+ * A capped manual run stops OPENING scans — each is a multi-megabyte jsonb
3127
+ * read it could not act on anyway — so the remainder it counted is a floor.
3128
+ * Reporting it as a total would have the operator run again and watch more
3129
+ * than the promised number appear.
3130
+ */
3131
+ it("says the remainder is a floor when a cap left scans unread", async () => {
3132
+ const hosts: Array<DiscoveredNetworkDevice> = makeFleet(600);
3133
+
3134
+ ruleFindOneByMock.mockResolvedValue(
3135
+ makeRule({ ipMatchTarget: "10.0.0.0/8" }),
3136
+ );
3137
+ ruleFindByMock.mockResolvedValue([]);
3138
+ mockRunNowScans([
3139
+ makeScan({ discoveredDevices: hosts }),
3140
+ makeScan({ discoveredDevices: makeFleet(5) }),
3141
+ ]);
3142
+
3143
+ const result: AutoImportRuleRunResult = await runRule(false);
3144
+
3145
+ expect(result).toMatchObject({
3146
+ isTruncated: true,
3147
+ hostsPendingImport: 600 - MAX_DEVICES_PER_AUTO_IMPORT_RUN,
3148
+ hasUnevaluatedScans: true,
3149
+ });
3150
+ // The second scan was never opened.
3151
+ expect(scanFindOneByMock).toHaveBeenCalledTimes(1);
3152
+ });
3153
+
3154
+ it("reports an exact remainder when the capped scan was the last one", async () => {
3155
+ ruleFindOneByMock.mockResolvedValue(
3156
+ makeRule({ ipMatchTarget: "10.0.0.0/8" }),
3157
+ );
3158
+ ruleFindByMock.mockResolvedValue([]);
3159
+ mockRunNowScans([makeScan({ discoveredDevices: makeFleet(600) })]);
3160
+
3161
+ const result: AutoImportRuleRunResult = await runRule(false);
3162
+
3163
+ expect(result).toMatchObject({
3164
+ isTruncated: true,
3165
+ hostsPendingImport: 600 - MAX_DEVICES_PER_AUTO_IMPORT_RUN,
3166
+ hasUnevaluatedScans: false,
3167
+ });
3168
+ });
3169
+
3170
+ /*
3171
+ * The dry run predicts the real run, so it must predict the leftovers too
3172
+ * — otherwise "what would this rule import" answers with the cap instead
3173
+ * of with the estate.
3174
+ */
3175
+ it("lets a dry run predict what a real run would leave over", async () => {
3176
+ ruleFindOneByMock.mockResolvedValue(
3177
+ makeRule({ ipMatchTarget: "10.0.0.0/8" }),
3178
+ );
3179
+ ruleFindByMock.mockResolvedValue([]);
3180
+ mockRunNowScans([makeScan({ discoveredDevices: makeFleet(909) })]);
3181
+
3182
+ const result: AutoImportRuleRunResult = await runRule(true);
3183
+
3184
+ expect(result).toMatchObject({
3185
+ isDryRun: true,
3186
+ isTruncated: true,
3187
+ hostsEvaluated: 909,
3188
+ hostsMatched: 909,
3189
+ hostsPendingImport: 909 - MAX_DEVICES_PER_AUTO_IMPORT_RUN,
3190
+ });
3191
+ expect(createMock).not.toHaveBeenCalled();
3192
+ });
3193
+
3194
+ /*
3195
+ * Counting past the cap must not start issuing per-device queries again:
3196
+ * the unindexed monitor JSON search is exactly what the budget check
3197
+ * guards, and running it for every host past the cap would turn a capped
3198
+ * pass into the full-table walk the cap exists to avoid.
3199
+ */
3200
+ it("issues no per-device monitor searches for the hosts it only counted", async () => {
3201
+ const hosts: Array<DiscoveredNetworkDevice> = makeFleet(600);
3202
+
3203
+ scanFindOneByMock.mockResolvedValue(makeScan({ discoveredDevices: hosts }));
3204
+ deviceFindByMock.mockResolvedValue(makeInventoryFor(hosts));
3205
+ ruleFindByMock.mockResolvedValue([
3206
+ makeRule({ ipMatchTarget: "10.0.0.0/8", monitorTemplateId: TEMPLATE_ID }),
3207
+ ]);
3208
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
3209
+
3210
+ await processScan();
3211
+
3212
+ /*
3213
+ * One project-wide snapshot plus one race-check per device the run
3214
+ * actually attempted — and none for the 100 it merely counted.
3215
+ */
3216
+ expect(monitorFindByMock).toHaveBeenCalledTimes(
3217
+ 1 + MAX_MONITORS_PER_AUTO_IMPORT_RUN,
3218
+ );
3219
+ });
3220
+
3221
+ /*
3222
+ * A stored result lists duplicate rows often enough that the import path
3223
+ * has a bucket for them. Counting rows rather than distinct work would
3224
+ * inflate the very number this fix exists to make trustworthy.
3225
+ */
3226
+ it("counts a duplicated un-imported host once", async () => {
3227
+ const overflow: DiscoveredNetworkDevice = makeHost({
3228
+ ipAddress: "10.9.9.9",
3229
+ sysName: "WANRTR-overflow",
3230
+ });
3231
+ const hosts: Array<DiscoveredNetworkDevice> = [
3232
+ ...makeFleet(MAX_DEVICES_PER_AUTO_IMPORT_RUN),
3233
+ overflow,
3234
+ { ...overflow },
3235
+ { ...overflow },
3236
+ ];
3237
+
3238
+ scanFindOneByMock.mockResolvedValue(makeScan({ discoveredDevices: hosts }));
3239
+ ruleFindByMock.mockResolvedValue([
3240
+ makeRule({ ipMatchTarget: "10.0.0.0/8" }),
3241
+ ]);
3242
+
3243
+ const result: AutoImportRuleRunResult | null = await processScan();
3244
+
3245
+ expect(result).toMatchObject({
3246
+ isTruncated: true,
3247
+ // Three rows, one address, one host still to import.
3248
+ hostsPendingImport: 1,
3249
+ hostsEvaluated: MAX_DEVICES_PER_AUTO_IMPORT_RUN + 3,
3250
+ });
3251
+ });
3252
+
3253
+ it("counts a duplicated unmonitored device once", async () => {
3254
+ const hosts: Array<DiscoveredNetworkDevice> = makeFleet(
3255
+ MAX_MONITORS_PER_AUTO_IMPORT_RUN,
3256
+ );
3257
+ const overflow: DiscoveredNetworkDevice = makeHost({
3258
+ ipAddress: "10.9.9.9",
3259
+ sysName: "WANRTR-overflow",
3260
+ });
3261
+ const withDuplicates: Array<DiscoveredNetworkDevice> = [
3262
+ ...hosts,
3263
+ overflow,
3264
+ { ...overflow },
3265
+ ];
3266
+
3267
+ scanFindOneByMock.mockResolvedValue(
3268
+ makeScan({ discoveredDevices: withDuplicates }),
3269
+ );
3270
+ deviceFindByMock.mockResolvedValue(makeInventoryFor(withDuplicates));
3271
+ ruleFindByMock.mockResolvedValue([
3272
+ makeRule({ ipMatchTarget: "10.0.0.0/8", monitorTemplateId: TEMPLATE_ID }),
3273
+ ]);
3274
+ monitorTemplateFindByMock.mockResolvedValue([makeTemplate()]);
3275
+
3276
+ const result: AutoImportRuleRunResult | null = await processScan();
3277
+
3278
+ expect(result).toMatchObject({
3279
+ isTruncated: true,
3280
+ monitorsPendingCreation: 1,
3281
+ });
3282
+ });
3283
+
3284
+ /*
3285
+ * The resume protocol is unchanged by the new accounting: a capped pass
3286
+ * that made progress still leaves the marker NULL so the next worker tick
3287
+ * continues, and the counting past the cap must not accidentally stamp it.
3288
+ */
3289
+ it("still leaves a capped pass's marker unstamped so the sweep resumes", async () => {
3290
+ const hosts: Array<DiscoveredNetworkDevice> = makeFleet(600);
3291
+
3292
+ scanFindOneByMock.mockResolvedValue(makeScan({ discoveredDevices: hosts }));
3293
+ ruleFindByMock.mockResolvedValue([
3294
+ makeRule({ ipMatchTarget: "10.0.0.0/8" }),
3295
+ ]);
3296
+
3297
+ await processScan();
3298
+
3299
+ expect(scanUpdateMock).toHaveBeenCalledTimes(1);
3300
+ const updateCall: { data: JSONObject } = scanUpdateMock.mock
3301
+ .calls[0]![0] as { data: JSONObject };
3302
+ expect(Object.keys(updateCall.data)).not.toContain("autoImportProcessedAt");
3303
+ });
3304
+ });