@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.
- package/Models/AnalyticsModels/RumSession.ts +117 -1
- package/Models/AnalyticsModels/RumSessionChunk.ts +13 -0
- package/Models/DatabaseModels/AlertCustomField.ts +77 -3
- package/Models/DatabaseModels/ApiKeyPermission.ts +3 -27
- package/Models/DatabaseModels/GlobalConfig.ts +12 -12
- package/Models/DatabaseModels/IncidentCustomField.ts +77 -3
- package/Models/DatabaseModels/InventoryItem.ts +44 -1
- package/Models/DatabaseModels/InventoryItemCustomField.ts +77 -3
- package/Models/DatabaseModels/MonitorCustomField.ts +77 -3
- package/Models/DatabaseModels/NetworkDevice.ts +126 -0
- package/Models/DatabaseModels/OnCallDutyPolicyCustomField.ts +77 -3
- package/Models/DatabaseModels/RumApplication.ts +6 -6
- package/Models/DatabaseModels/ScheduledMaintenanceCustomField.ts +77 -3
- package/Models/DatabaseModels/StatusPageCustomField.ts +77 -3
- package/Models/DatabaseModels/TeamCustomField.ts +77 -3
- package/Models/DatabaseModels/TeamMember.ts +0 -9
- package/Models/DatabaseModels/TeamMemberCustomField.ts +77 -3
- package/Models/DatabaseModels/TeamPermission.ts +3 -24
- package/Models/DatabaseModels/TelemetryIngestionKey.ts +260 -0
- package/Models/DatabaseModels/UserTelegram.ts +0 -4
- package/Scripts/benchmark-fanin-capacity.js +181 -0
- package/Server/API/TelemetryAPI.ts +1284 -164
- package/Server/API/UserNotificationSettingAPI.ts +55 -0
- package/Server/API/UserTelegramAPI.ts +48 -4
- package/Server/EnvironmentConfig.ts +82 -18
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791300000000-AddTelemetryIngestionKeyType.ts +91 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791400000000-SessionReplayRecordEverySessionByDefault.ts +67 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791500000000-WidenCustomFieldDropdownOptions.ts +139 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791600000000-AddCustomFieldValueMapping.ts +121 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.ts +25 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Infrastructure/QueueWorker.ts +20 -14
- package/Server/Infrastructure/Redis.ts +8 -8
- package/Server/Infrastructure/Semaphore.ts +2 -0
- package/Server/Infrastructure/Status.ts +10 -4
- package/Server/Middleware/TelemetryIngest.ts +453 -7
- package/Server/Services/AccessTokenService.ts +1 -0
- package/Server/Services/AlertCustomFieldService.ts +98 -0
- package/Server/Services/AlertService.ts +37 -0
- package/Server/Services/ApiKeyPermissionService.ts +461 -29
- package/Server/Services/CustomFieldMappingService.ts +879 -0
- package/Server/Services/DatabaseService.ts +65 -13
- package/Server/Services/GlobalConfigService.ts +194 -0
- package/Server/Services/IncidentCustomFieldService.ts +98 -0
- package/Server/Services/IncidentService.ts +35 -0
- package/Server/Services/InventoryItemService.ts +31 -1
- package/Server/Services/MetricService.ts +194 -0
- package/Server/Services/MonitorService.ts +32 -0
- package/Server/Services/MonitorTemplateService.ts +140 -19
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +427 -66
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +87 -2
- package/Server/Services/NetworkDeviceService.ts +69 -0
- package/Server/Services/ProjectService.ts +22 -0
- package/Server/Services/RoutineEmailSettingsService.ts +73 -0
- package/Server/Services/RumSessionReplayViewService.ts +104 -24
- package/Server/Services/ScheduledMaintenanceCustomFieldService.ts +98 -0
- package/Server/Services/ScheduledMaintenanceService.ts +32 -0
- package/Server/Services/TeamMemberService.ts +78 -0
- package/Server/Services/TeamPermissionService.ts +235 -3
- package/Server/Services/TelemetryIngestionKeyService.ts +722 -19
- package/Server/Services/UserNotificationRuleService.ts +76 -25
- package/Server/Services/UserNotificationSettingService.ts +34 -6
- package/Server/Services/UserTelegramService.ts +326 -5
- package/Server/Types/Database/Permissions/ReadPermission.ts +45 -5
- package/Server/Types/Database/QueryHelper.ts +2 -0
- package/Server/Types/Database/QueryUtil.ts +56 -0
- package/Server/Types/Markdown.ts +93 -1
- package/Server/Utils/APIKey/AccessPermission.ts +5 -2
- package/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.ts +54 -0
- package/Server/Utils/CustomField/CustomFieldMappingRegistry.ts +415 -0
- package/Server/Utils/CustomField/CustomFieldMappingValidator.ts +335 -0
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +82 -17
- package/Server/Utils/DataSource/EgressGuard.ts +177 -8
- package/Server/Utils/EmailRollup/EmailRollupConstants.ts +38 -5
- package/Server/Utils/EmailRollup/EmailRollupFlushRunner.ts +67 -5
- package/Server/Utils/EmailRollup/EmailRollupRenderer.ts +2 -2
- package/Server/Utils/FrontendEnvironment.ts +40 -0
- package/Server/Utils/LogRedaction.ts +20 -0
- package/Server/Utils/Logger.ts +11 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +394 -86
- package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +30 -4
- package/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.ts +31 -5
- package/Server/Utils/Monitor/MonitorAlert.ts +29 -6
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +312 -52
- package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +82 -9
- package/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.ts +16 -7
- package/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.ts +29 -17
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +107 -48
- package/Server/Utils/Monitor/MonitorIncident.ts +22 -6
- package/Server/Utils/Monitor/MonitorTemplateUtil.ts +29 -0
- package/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.ts +158 -0
- package/Server/Utils/Monitor/NetworkInventoryUtil.ts +27 -0
- package/Server/Utils/Monitor/SeriesContextEnricher.ts +304 -0
- package/Server/Utils/SSRFProtection.ts +217 -10
- package/Server/Utils/SessionReplay/SessionReplayGateCache.ts +239 -138
- package/Server/Utils/SessionReplay/SessionReplayHealthCounters.ts +305 -0
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +1430 -289
- package/Server/Utils/StartServer.ts +2 -17
- package/Server/Utils/TelegramVerificationToken.ts +185 -0
- package/Server/Utils/Telemetry/EntityRegistry.ts +11 -1
- package/Server/Utils/Telemetry/PinServiceName.ts +197 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +83 -9
- package/Server/Utils/Telemetry/TelemetryFanInWriter.ts +38 -16
- package/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.ts +92 -0
- package/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.ts +217 -0
- package/Server/Utils/VM/VMAPI.ts +1 -0
- package/Server/Utils/VM/VMRunner.ts +975 -95
- package/Tests/App/AdminDashboard/AdminHeaderSmallScreens.test.tsx +202 -0
- package/Tests/App/Dashboard/AddNeighborToMonitoringModal.test.tsx +32 -0
- package/Tests/App/Dashboard/AdoptedDeviceDrawer.test.tsx +374 -0
- package/Tests/App/Dashboard/DashboardHeaderSmallScreens.test.tsx +404 -0
- package/Tests/App/Dashboard/DashboardLabelVariables.test.tsx +580 -0
- package/Tests/App/Dashboard/DashboardMonitorLabelVariable.test.tsx +438 -0
- package/Tests/App/Dashboard/DashboardVariableAllContract.test.tsx +5 -2
- package/Tests/App/Dashboard/DeviceAttachmentCard.test.tsx +273 -0
- package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +505 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +468 -0
- package/Tests/App/Dashboard/EntityDetailPanel.test.tsx +163 -0
- package/Tests/App/Dashboard/InfrastructureExplorer.test.tsx +401 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +232 -0
- package/Tests/App/Dashboard/InventoryItems.test.tsx +201 -0
- package/Tests/App/Dashboard/InventoryTypeAndStatusFacets.test.tsx +798 -0
- package/Tests/App/Dashboard/NetworkTopologyLiveView.test.tsx +320 -0
- package/Tests/App/Dashboard/NetworkTopologyToolbar.test.tsx +275 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedEmptyState.test.tsx +975 -0
- package/Tests/App/Dashboard/OnCallCalendarFeedPlanGate.test.tsx +63 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +626 -0
- package/Tests/App/Dashboard/TopologyDataLoading.test.tsx +348 -0
- package/Tests/App/Dashboard/TopologyPageNavigation.test.tsx +331 -0
- package/Tests/App/Dashboard/UserSettingsEmailPreferences.test.tsx +603 -0
- package/Tests/App/Dashboard/UserSettingsNotificationSettings.test.tsx +324 -0
- package/Tests/App/PublicDashboard/DashboardVariableSelector.test.tsx +13 -4
- package/Tests/App/StatusPage/StatusPageLastUpdated.test.tsx +263 -0
- package/Tests/App/StatusPage/StatusPageOidcOrigin.test.tsx +223 -0
- package/Tests/App/StatusPage/StatusPageOverviewLiveAndSearch.test.tsx +753 -0
- package/Tests/App/StatusPage/StatusPageResourceSearchBox.test.tsx +224 -0
- package/Tests/Models/AnalyticsModels/RumSessionReplayColumns.test.ts +262 -0
- package/Tests/Models/CustomFieldMappingColumns.test.ts +166 -0
- package/Tests/Models/DatabaseModels/SessionReplayModels.test.ts +20 -6
- package/Tests/Models/InventoryItemStatus.test.ts +147 -0
- package/Tests/Models/NetworkDeviceMacAddressColumn.test.ts +294 -0
- package/Tests/ResponsiveVisibility.test.ts +115 -0
- package/Tests/ResponsiveVisibility.ts +183 -0
- package/Tests/Server/API/DashboardPublicResourceListAPI.test.ts +129 -0
- package/Tests/Server/API/SessionReplayAPI.test.ts +1940 -216
- package/Tests/Server/API/UserNotificationSettingAPI.test.ts +186 -0
- package/Tests/Server/API/UserTelegramAPISecurity.test.ts +231 -0
- package/Tests/Server/EnvironmentConfigFrontendSecurity.test.ts +234 -0
- package/Tests/Server/EnvironmentConfigValkey.test.ts +227 -0
- package/Tests/Server/Infrastructure/Postgres/AddMacAddressToNetworkDeviceMigration.test.ts +365 -0
- package/Tests/Server/Infrastructure/Postgres/CustomFieldValueMappingMigration.test.ts +241 -0
- package/Tests/Server/Infrastructure/Postgres/InventoryItemArchiveMigration.test.ts +41 -0
- package/Tests/Server/Infrastructure/Postgres/SessionReplayRecordEverySessionByDefaultMigration.test.ts +169 -0
- package/Tests/Server/Infrastructure/QueueWorkerTimeout.test.ts +210 -0
- package/Tests/Server/Infrastructure/TelemetryExporterDeploymentConfig.test.ts +130 -0
- package/Tests/Server/Infrastructure/ValkeyDeploymentConfig.test.ts +265 -0
- package/Tests/Server/Middleware/HttpMetricsMiddleware.test.ts +382 -0
- package/Tests/Server/Middleware/ProjectAuthorizationApiKeyMiddleware.test.ts +4 -1
- package/Tests/Server/Middleware/SCIMAuthorization.test.ts +525 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +1163 -0
- package/Tests/Server/Middleware/TelemetryIngestTokenLog.test.ts +5 -3
- package/Tests/Server/Services/AddTelemetryIngestionKeyTypeMigration.test.ts +555 -0
- package/Tests/Server/Services/ApiKeyPermissionSecurity.test.ts +678 -0
- package/Tests/Server/Services/ApiKeyPermissionService.test.ts +276 -28
- package/Tests/Server/Services/CustomFieldDropdownOptionsColumnWidth.test.ts +431 -0
- package/Tests/Server/Services/CustomFieldMappingService.test.ts +850 -0
- package/Tests/Server/Services/DatabaseServiceAggregateBy.test.ts +291 -56
- package/Tests/Server/Services/DatabaseServiceSortTiebreaker.test.ts +151 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +32 -0
- package/Tests/Server/Services/GlobalConfigService.test.ts +414 -1
- package/Tests/Server/Services/InventoryItemManualCreate.test.ts +105 -0
- package/Tests/Server/Services/InventoryItemPromotionGate.test.ts +178 -0
- package/Tests/Server/Services/MetricRawEntityKeyPrune.test.ts +592 -0
- package/Tests/Server/Services/MonitorTemplateServiceCustomFieldSync.test.ts +502 -0
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +1351 -74
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanRegistration.test.ts +520 -0
- package/Tests/Server/Services/NetworkDeviceMacAddressNormalization.test.ts +572 -0
- package/Tests/Server/Services/RoutineEmailSettingsPostgres.test.ts +329 -0
- package/Tests/Server/Services/RumSessionReplayViewService.test.ts +267 -0
- package/Tests/Server/Services/TeamMemberAutoAcceptInvitation.test.ts +9 -0
- package/Tests/Server/Services/TeamMemberInviteRegistrationToken.test.ts +9 -0
- package/Tests/Server/Services/TeamPrivilegeEscalation.test.ts +834 -0
- package/Tests/Server/Services/TelemetryIngestionKeyPolicyResolution.test.ts +814 -0
- package/Tests/Server/Services/TelemetryIngestionKeyValidation.test.ts +998 -0
- package/Tests/Server/Services/UserNotificationSettingRollupRouting.test.ts +141 -0
- package/Tests/Server/Services/UserNotificationSettingWorkspaceChannels.test.ts +18 -10
- package/Tests/Server/Services/UserTelegramVerificationSecurity.test.ts +688 -0
- package/Tests/Server/Types/Database/InventoryStatusPostgres.test.ts +368 -0
- package/Tests/Server/Types/Database/Permissions/ReadBlockPermission.test.ts +261 -14
- package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroups.test.ts +169 -0
- package/Tests/Server/Types/Database/QueryUtilIncludesAnyOfGroupsPostgres.test.ts +297 -0
- package/Tests/Server/Types/Markdown.test.ts +194 -1
- package/Tests/Server/Types/Workflow/Components/IncomingWebhookUtils.test.ts +279 -0
- package/Tests/Server/Types/Workflow/Components/TextToJson.test.ts +212 -0
- package/Tests/Server/Utils/AI/SRE/Insights/FixRouting.test.ts +497 -0
- package/Tests/Server/Utils/AI/Toolbox/Serializer.test.ts +383 -0
- package/Tests/Server/Utils/APIKey/AccessPermission.test.ts +7 -3
- package/Tests/Server/Utils/AnalyticsDatabase/ClusterConfig.test.ts +315 -0
- package/Tests/Server/Utils/CustomField/CustomFieldMappingRegistry.test.ts +436 -0
- package/Tests/Server/Utils/CustomField/CustomFieldMappingValidator.test.ts +437 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.test.ts +214 -0
- package/Tests/Server/Utils/DataSource/EgressGuard.test.ts +300 -11
- package/Tests/Server/Utils/EmailRollup/EmailRollupBurstWindow.test.ts +83 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerBehaviour.test.ts +2 -2
- package/Tests/Server/Utils/EmailRollup/EmailRollupFlushRunnerPreferences.test.ts +362 -0
- package/Tests/Server/Utils/EmailRollup/EmailRollupRenderer.test.ts +10 -3
- package/Tests/Server/Utils/EmailRollup/EmailRollupTestHarness.ts +77 -1
- package/Tests/Server/Utils/EventLoop.test.ts +359 -0
- package/Tests/Server/Utils/FrontendEnvironment.test.ts +190 -0
- package/Tests/Server/Utils/JsonWebToken.test.ts +30 -0
- package/Tests/Server/Utils/LogRedaction.test.ts +26 -0
- package/Tests/Server/Utils/LoggerCredentialLeak.test.ts +23 -9
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteria.test.ts +242 -0
- package/Tests/Server/Utils/Monitor/Criteria/CompareCriteriaAggregation.test.ts +590 -0
- package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +29 -0
- package/Tests/Server/Utils/Monitor/Criteria/IncomingRequestHeaderCriteria.test.ts +236 -0
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.test.ts +30 -6
- package/Tests/Server/Utils/Monitor/Criteria/MetricMonitorCriteriaAnomalyUnits.test.ts +246 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEmailEvalLogAgreement.test.ts +280 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluator.test.ts +659 -8
- package/Tests/Server/Utils/Monitor/MonitorCriteriaEvaluatorMetricUnits.test.ts +570 -0
- package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilderUnits.test.ts +30 -5
- package/Tests/Server/Utils/Monitor/MonitorCriteriaMessageBuilderUnits.test.ts +68 -21
- package/Tests/Server/Utils/Monitor/MonitorCriteriaObservationBuilderUnits.test.ts +36 -19
- package/Tests/Server/Utils/Monitor/MonitorTemplateUtilSeriesContext.test.ts +176 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.test.ts +573 -0
- package/Tests/Server/Utils/Monitor/NetworkInventoryUtil.test.ts +317 -0
- package/Tests/Server/Utils/Monitor/SeriesContextEnricher.test.ts +356 -0
- package/Tests/Server/Utils/SSRFProtectionCloudServiceAddresses.test.ts +595 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayGateCachePolicy.test.ts +198 -1
- package/Tests/Server/Utils/SessionReplay/SessionReplayHealthCounters.test.ts +453 -0
- package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +1378 -0
- package/Tests/Server/Utils/SessionReplayOriginAllowListRefactor.test.ts +420 -0
- package/Tests/Server/Utils/TelegramVerificationToken.test.ts +253 -0
- package/Tests/Server/Utils/Telemetry/ContextSpanProcessor.test.ts +281 -0
- package/Tests/Server/Utils/Telemetry/InventoryDuplicateIdentity.test.ts +316 -0
- package/Tests/Server/Utils/Telemetry/PinServiceName.test.ts +666 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +111 -8
- package/Tests/Server/Utils/Telemetry/TelemetryFanInWriterCapacity.test.ts +458 -0
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +424 -0
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.test.ts +528 -0
- package/Tests/Server/Utils/TelemetryExporterEnvironment.test.ts +56 -0
- package/Tests/Server/Utils/VM/VMRunnerHostBridgeLatency.test.ts +74 -0
- package/Tests/Server/Utils/VM/VMRunnerPrivateNetworkWiring.test.ts +100 -7
- package/Tests/Server/Utils/VM/VMRunnerSsrf.test.ts +1176 -6
- package/Tests/Types/CustomField/CustomFieldMappingCatalog.test.ts +220 -0
- package/Tests/Types/CustomField/CustomFieldValueMapping.test.ts +348 -0
- package/Tests/Types/Database/IncludesAnyOfGroups.test.ts +106 -0
- package/Tests/Types/JSONFunctions.test.ts +300 -0
- package/Tests/Types/Monitor/CephAlertTemplates.test.ts +422 -53
- package/Tests/Types/Monitor/DockerAlertTemplates.test.ts +495 -15
- package/Tests/Types/Monitor/DockerSwarmAlertTemplates.test.ts +191 -45
- package/Tests/Types/Monitor/HostAlertTemplates.test.ts +370 -42
- package/Tests/Types/Monitor/IotAlertTemplates.test.ts +375 -20
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +636 -42
- package/Tests/Types/Monitor/KubernetesMetricCatalog.test.ts +189 -0
- package/Tests/Types/Monitor/KubernetesTemplateGroupByKeys.test.ts +95 -14
- package/Tests/Types/Monitor/PodmanAlertTemplates.test.ts +190 -21
- package/Tests/Types/Monitor/ProxmoxAlertTemplates.test.ts +352 -22
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationAlertDebuggability.test.ts +363 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +93 -18
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +259 -0
- package/Tests/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.test.ts +476 -0
- package/Tests/Types/Monitor/RumAlertTemplates.test.ts +136 -0
- package/Tests/Types/Monitor/SeriesContext/SeriesDebugHints.test.ts +661 -0
- package/Tests/Types/Monitor/SeriesContext/SeriesLabelDisplay.test.ts +507 -0
- package/Tests/Types/Monitor/ServiceAlertTemplates.test.ts +270 -0
- package/Tests/Types/Monitor/TemplateGroupByKeys.test.ts +38 -10
- package/Tests/Types/Monitor/Utils/RecommendationCriteriaAssertions.ts +102 -0
- package/Tests/Types/NetworkAutomation/AutoImportRunMerge.test.ts +235 -0
- package/Tests/Types/NetworkAutomation/RuleRunResult.test.ts +66 -0
- package/Tests/Types/NetworkAutomation/RuleRunResultDescribe.test.ts +309 -1
- package/Tests/Types/NotificationSetting/RoutineEmailEvents.test.ts +47 -0
- package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +608 -0
- package/Tests/Types/Rum/SessionReplayCustomEvents.test.ts +434 -0
- package/Tests/Types/Telemetry/InventoryLiveness.test.ts +99 -0
- package/Tests/Types/WebsiteRequest.test.ts +285 -3
- package/Tests/UI/Components/Charts/ChartBucketIdentity.test.ts +16 -3
- package/Tests/UI/Components/Charts/ChartTrailingBucketGap.test.ts +691 -0
- package/Tests/UI/Components/ComponentsModal.test.tsx +12 -4
- package/Tests/UI/Components/ComponentsModalUsability.test.tsx +518 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +160 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetailMapping.test.tsx +324 -0
- package/Tests/UI/Components/CustomFields/MapFromCustomFieldInput.test.tsx +176 -0
- package/Tests/UI/Components/Graphs/DayUptimeGraph.test.tsx +484 -0
- package/Tests/UI/Components/HeaderRightRail.test.tsx +157 -0
- package/Tests/UI/Components/IconDropdownItem.test.tsx +102 -0
- package/Tests/UI/Components/JSONTablePrototypePollution.test.tsx +117 -0
- package/Tests/UI/Components/KeyboardShortcutsModal.test.tsx +214 -0
- package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +561 -0
- package/Tests/UI/Components/MonitorGraphs/UptimeBarDayModal.test.tsx +302 -0
- package/Tests/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.test.ts +28 -12
- package/Tests/UI/Components/ShortcutDialogGuard.test.tsx +74 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupSectionAutoExpand.test.tsx +248 -0
- package/Tests/UI/Components/TableCellWrapping.test.tsx +625 -0
- package/Tests/UI/Components/TableLoadingStates.test.tsx +205 -0
- package/Tests/UI/Components/Workflow/NodePlacement.test.ts +148 -0
- package/Tests/UI/Components/Workflow/Workflow.test.tsx +981 -0
- package/Tests/UI/ConfigBrowserTelemetry.test.ts +70 -0
- package/Tests/UI/ReactRouterSingletonBuild.test.ts +133 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +1036 -2
- package/Tests/UI/Rum/FidelityNotices.test.ts +187 -0
- package/Tests/UI/Rum/InactivityMap.test.ts +341 -0
- package/Tests/UI/Rum/PrivacySummaryCard.test.tsx +259 -0
- package/Tests/UI/Rum/RecordingHealthCard.test.tsx +855 -0
- package/Tests/UI/Rum/RecordingHealthStrip.test.tsx +594 -0
- package/Tests/UI/Rum/ReplayCard.test.tsx +451 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +536 -0
- package/Tests/UI/Rum/ReplayEngine.test.ts +2503 -0
- package/Tests/UI/Rum/ReplayEngineTypes.test.ts +171 -0
- package/Tests/UI/Rum/ReplayHeader.test.tsx +719 -0
- package/Tests/UI/Rum/ReplayLink.test.tsx +119 -0
- package/Tests/UI/Rum/ReplayPinControl.test.tsx +408 -0
- package/Tests/UI/Rum/ReplayPlaybackIntent.test.ts +152 -0
- package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +514 -0
- package/Tests/UI/Rum/ReplayRail.test.tsx +1296 -0
- package/Tests/UI/Rum/ReplayRailDetail.test.tsx +900 -0
- package/Tests/UI/Rum/ReplayScrubber.test.tsx +763 -147
- package/Tests/UI/Rum/ReplaySignalTypes.test.ts +189 -0
- package/Tests/UI/Rum/ReplaySignals.test.ts +1579 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +396 -473
- package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +918 -0
- package/Tests/UI/Rum/ReplayTimeFormat.test.ts +94 -0
- package/Tests/UI/Rum/ReplayTimeline.test.tsx +696 -0
- package/Tests/UI/Rum/ReplayUi.test.tsx +559 -0
- package/Tests/UI/Rum/SessionReplayEmptyState.test.tsx +499 -0
- package/Tests/UI/Rum/SessionReplaySearchBar.test.tsx +346 -0
- package/Tests/UI/Rum/SessionReplaySetupGuide.test.tsx +545 -0
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +991 -0
- package/Tests/UI/Rum/TargetedCapturePanel.test.tsx +254 -0
- package/Tests/UI/Telemetry/BrowserExporterIsolation.test.ts +71 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealBreadcrumbTrails.ts +5 -0
- package/Tests/UI/Utils/Breadcrumb/fixtures/RealRoutePatterns.ts +1 -0
- package/Tests/UI/Utils/GlobalKeyboardShortcut.test.ts +344 -0
- package/Tests/Utils/API.test.ts +308 -0
- package/Tests/Utils/Dashboard/Components/DashboardMonitorListComponent.test.ts +11 -1
- package/Tests/Utils/Dashboard/LabelVariable.test.ts +257 -0
- package/Tests/Utils/Dashboard/VariableUrlState.test.ts +144 -14
- package/Tests/Utils/HTTPResponseBodyReader.test.ts +323 -0
- package/Tests/Utils/MetricUnitUtil.test.ts +81 -0
- package/Tests/Utils/Monitor/DeviceMacLearningUtil.test.ts +497 -0
- package/Tests/Utils/Monitor/MetricValueFormatter.test.ts +546 -0
- package/Tests/Utils/Monitor/MonitorTemplateCustomFieldUtil.test.ts +218 -0
- package/Tests/Utils/Monitor/NetworkTopologyEndpointAdoption.test.ts +1755 -0
- package/Tests/Utils/NetworkAutomation/AutoImportRunChain.test.ts +320 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveryScanStatus.test.ts +117 -0
- package/Tests/Utils/Rum/ChunkMath.test.ts +83 -0
- package/Tests/Utils/Rum/SessionReplayHealthDiagnosis.test.ts +993 -0
- package/Tests/Utils/Rum/SessionReplayStringMap.test.ts +245 -0
- package/Tests/Utils/StatusPage/ResourceSearch.test.ts +631 -0
- package/Tests/Utils/Telemetry/OriginAllowList.test.ts +633 -0
- package/Tests/Utils/Uptime/DayUptimeGraphUtil.test.ts +461 -0
- package/Tests/Utils/ValueFormatter.test.ts +148 -0
- package/Types/BaseDatabase/IncludesAnyOfGroups.ts +77 -0
- package/Types/CustomField/CustomFieldMappingCatalog.ts +161 -0
- package/Types/CustomField/CustomFieldMappingSourceResource.ts +19 -0
- package/Types/CustomField/CustomFieldValueMapping.ts +288 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +1 -0
- package/Types/Dashboard/DashboardComponents/DashboardMonitorListComponent.ts +1 -0
- package/Types/Dashboard/DashboardVariable.ts +8 -0
- package/Types/Icon/IconProp.ts +1 -0
- package/Types/JSON.ts +4 -0
- package/Types/JSONFunctions.ts +205 -43
- package/Types/Monitor/CephAlertTemplates.ts +157 -195
- package/Types/Monitor/DockerAlertTemplates.ts +338 -128
- package/Types/Monitor/DockerSwarmAlertTemplates.ts +95 -112
- package/Types/Monitor/HostAlertTemplates.ts +302 -130
- package/Types/Monitor/IotAlertTemplates.ts +112 -104
- package/Types/Monitor/KubernetesAlertTemplates.ts +415 -224
- package/Types/Monitor/KubernetesMetricCatalog.ts +23 -19
- package/Types/Monitor/PodmanAlertTemplates.ts +116 -163
- package/Types/Monitor/ProxmoxAlertTemplates.ts +217 -99
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +98 -6
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +44 -7
- package/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.ts +383 -0
- package/Types/Monitor/RumAlertTemplates.ts +46 -7
- package/Types/Monitor/SeriesContext/SeriesDebugHints.ts +596 -0
- package/Types/Monitor/SeriesContext/SeriesLabelDisplay.ts +554 -0
- package/Types/Monitor/ServiceAlertTemplates.ts +98 -11
- package/Types/Monitor/SnmpMonitor/NetworkTopology.ts +13 -0
- package/Types/Monitor/UptimeHistoryLabels.ts +56 -0
- package/Types/NetworkAutomation/RuleRunResult.ts +402 -13
- package/Types/NotificationSetting/RoutineEmailEvents.ts +30 -0
- package/Types/Rum/SessionReplay.ts +209 -0
- package/Types/Rum/SessionReplayApi.ts +722 -0
- package/Types/Rum/SessionReplayCaptureTrigger.ts +21 -12
- package/Types/Rum/SessionReplayConsentMode.ts +11 -7
- package/Types/Rum/SessionReplayCustomEvents.ts +549 -0
- package/Types/Rum/SessionReplayHealth.ts +238 -0
- package/Types/SerializableObjectDictionary.ts +4 -0
- package/Types/Telemetry/InventoryLiveness.ts +56 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +95 -0
- package/Types/Telemetry/TelemetryIngestionKeyPolicy.ts +75 -0
- package/Types/Telemetry/TelemetryIngestionKeyType.ts +29 -0
- package/Types/WebsiteRequest.ts +85 -3
- package/UI/Components/CSVFileUpload/CSVFileUpload.tsx +9 -2
- package/UI/Components/Charts/Area/AreaChart.tsx +13 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +6 -2
- package/UI/Components/Charts/Line/LineChart.tsx +13 -2
- package/UI/Components/Charts/Types/XAxis/XAxis.ts +29 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +22 -3
- package/UI/Components/Charts/Utils/TimeAnnotation.ts +76 -9
- package/UI/Components/Charts/Utils/XAxis.ts +217 -0
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +189 -46
- package/UI/Components/CustomFields/MapFromCustomFieldInput.tsx +216 -0
- package/UI/Components/Dashboard/DashboardVariableControl.tsx +285 -0
- package/UI/Components/Graphs/DayUptimeGraph.tsx +173 -8
- package/UI/Components/Graphs/UptimeBarTooltip.tsx +22 -278
- package/UI/Components/Graphs/UptimeDaySummary.tsx +327 -0
- package/UI/Components/Header/Header.tsx +25 -9
- package/UI/Components/Header/IconDropdown/IconDropdownItem.tsx +10 -1
- package/UI/Components/Header/IconDropdown/IconDropdownMenu.tsx +6 -1
- package/UI/Components/Header/ProjectPicker/ProjectPicker.tsx +6 -1
- package/UI/Components/HeaderAlert/NotificationBell/NotificationBellDropdown.tsx +6 -1
- package/UI/Components/Icon/Icon.tsx +11 -0
- package/UI/Components/JSONTable/JSONTable.tsx +2 -2
- package/UI/Components/KeyboardShortcut/KeyboardShortcutsModal.tsx +125 -0
- package/UI/Components/KeyboardShortcut/Screenshots/README.md +19 -0
- package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-command-palette.png +0 -0
- package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-dialog-dark.png +0 -0
- package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-dialog.png +0 -0
- package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-help-menu.png +0 -0
- package/UI/Components/KeyboardShortcut/Screenshots/keyboard-shortcuts-narrow.png +0 -0
- package/UI/Components/ModelTable/Column.ts +15 -0
- package/UI/Components/Monitor/SeriesDebugCommandsViewer.tsx +64 -0
- package/UI/Components/Monitor/SeriesLabelsViewer.tsx +93 -0
- package/UI/Components/MonitorGraphs/Uptime.tsx +14 -2
- package/UI/Components/MonitorGraphs/UptimeBarDayModal.tsx +63 -10
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.ts +44 -0
- package/UI/Components/StatusPage/ResourceGroupSection.tsx +40 -0
- package/UI/Components/Table/CellClassName.ts +81 -0
- package/UI/Components/Table/TableRow.tsx +20 -14
- package/UI/Components/Table/TableSkeletonRows.tsx +9 -9
- package/UI/Components/Table/Types/Column.ts +36 -0
- package/UI/Components/Tooltip/Tooltip.tsx +11 -1
- package/UI/Components/Workflow/ComponentsModal.tsx +60 -39
- package/UI/Components/Workflow/NodePlacement.ts +57 -0
- package/UI/Components/Workflow/Workflow.tsx +47 -18
- package/UI/Config.ts +15 -33
- package/UI/Utils/GlobalKeyboardShortcut.ts +208 -0
- package/UI/Utils/Telemetry/BrowserTelemetryConfig.ts +28 -0
- package/UI/Utils/Telemetry/Telemetry.ts +10 -8
- package/UI/esbuild-config.js +6 -0
- package/Utils/API.ts +129 -3
- package/Utils/Dashboard/Components/DashboardMonitorListComponent.ts +12 -1
- package/Utils/Dashboard/LabelVariable.ts +152 -0
- package/Utils/Dashboard/VariableUrlState.ts +27 -5
- package/Utils/HTTPResponseBodyReader.ts +221 -0
- package/Utils/MetricUnitUtil.ts +34 -0
- package/Utils/Monitor/DeviceMacLearningUtil.ts +177 -0
- package/Utils/Monitor/MetricValueFormatter.ts +342 -0
- package/Utils/Monitor/MonitorTemplateCustomFieldUtil.ts +124 -0
- package/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.ts +7 -2
- package/Utils/Monitor/NetworkTopologyUtil.ts +693 -0
- package/Utils/NetworkAutomation/AutoImportRunChain.ts +138 -0
- package/Utils/NetworkDiscovery/DiscoveryScanStatus.ts +77 -0
- package/Utils/Rum/ChunkMath.ts +106 -0
- package/Utils/Rum/SessionReplayHealth.ts +732 -0
- package/Utils/Rum/SessionReplayStringMap.ts +226 -0
- package/Utils/Schema/ModelSchema.ts +1 -0
- package/Utils/StatusPage/ResourceSearch.ts +290 -0
- package/Utils/Telemetry/OriginAllowList.ts +355 -0
- package/Utils/Uptime/DayUptimeGraphUtil.ts +205 -0
- package/Utils/ValueFormatter.ts +35 -1
- package/build/dist/Models/AnalyticsModels/RumSession.js +102 -19
- package/build/dist/Models/AnalyticsModels/RumSession.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js +11 -9
- package/build/dist/Models/AnalyticsModels/RumSessionChunk.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/AlertCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ApiKeyPermission.js +3 -27
- package/build/dist/Models/DatabaseModels/ApiKeyPermission.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +12 -12
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/IncidentCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js +34 -1
- package/build/dist/Models/DatabaseModels/InventoryItem.js.map +1 -1
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/InventoryItemCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MonitorCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/MonitorCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +128 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RumApplication.js +6 -6
- package/build/dist/Models/DatabaseModels/RumApplication.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/StatusPageCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/TeamCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamMember.js +0 -9
- package/build/dist/Models/DatabaseModels/TeamMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamMemberCustomField.js +79 -3
- package/build/dist/Models/DatabaseModels/TeamMemberCustomField.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamPermission.js +3 -24
- package/build/dist/Models/DatabaseModels/TeamPermission.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js +267 -0
- package/build/dist/Models/DatabaseModels/TelemetryIngestionKey.js.map +1 -1
- package/build/dist/Models/DatabaseModels/UserTelegram.js +0 -4
- package/build/dist/Models/DatabaseModels/UserTelegram.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +769 -109
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/API/UserNotificationSettingAPI.js +35 -0
- package/build/dist/Server/API/UserNotificationSettingAPI.js.map +1 -0
- package/build/dist/Server/API/UserTelegramAPI.js +28 -6
- package/build/dist/Server/API/UserTelegramAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +73 -14
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791300000000-AddTelemetryIngestionKeyType.js +60 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791300000000-AddTelemetryIngestionKeyType.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791400000000-SessionReplayRecordEverySessionByDefault.js +53 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791400000000-SessionReplayRecordEverySessionByDefault.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791500000000-WidenCustomFieldDropdownOptions.js +82 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791500000000-WidenCustomFieldDropdownOptions.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791600000000-AddCustomFieldValueMapping.js +46 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791600000000-AddCustomFieldValueMapping.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1791700000000-AddMacAddressToNetworkDevice.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Infrastructure/QueueWorker.js +19 -7
- package/build/dist/Server/Infrastructure/QueueWorker.js.map +1 -1
- package/build/dist/Server/Infrastructure/Redis.js +8 -8
- package/build/dist/Server/Infrastructure/Redis.js.map +1 -1
- package/build/dist/Server/Infrastructure/Semaphore.js +1 -0
- package/build/dist/Server/Infrastructure/Semaphore.js.map +1 -1
- package/build/dist/Server/Infrastructure/Status.js +10 -4
- package/build/dist/Server/Infrastructure/Status.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +315 -8
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AccessTokenService.js +1 -0
- package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
- package/build/dist/Server/Services/AlertCustomFieldService.js +93 -0
- package/build/dist/Server/Services/AlertCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +34 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/ApiKeyPermissionService.js +331 -28
- package/build/dist/Server/Services/ApiKeyPermissionService.js.map +1 -1
- package/build/dist/Server/Services/CustomFieldMappingService.js +614 -0
- package/build/dist/Server/Services/CustomFieldMappingService.js.map +1 -0
- package/build/dist/Server/Services/DatabaseService.js +58 -13
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/GlobalConfigService.js +133 -0
- package/build/dist/Server/Services/GlobalConfigService.js.map +1 -1
- package/build/dist/Server/Services/IncidentCustomFieldService.js +93 -0
- package/build/dist/Server/Services/IncidentCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +32 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/InventoryItemService.js +28 -1
- package/build/dist/Server/Services/InventoryItemService.js.map +1 -1
- package/build/dist/Server/Services/MetricService.js +163 -0
- package/build/dist/Server/Services/MetricService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +29 -0
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/MonitorTemplateService.js +116 -17
- package/build/dist/Server/Services/MonitorTemplateService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +349 -64
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +65 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +54 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +22 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RoutineEmailSettingsService.js +69 -0
- package/build/dist/Server/Services/RoutineEmailSettingsService.js.map +1 -0
- package/build/dist/Server/Services/RumSessionReplayViewService.js +86 -24
- package/build/dist/Server/Services/RumSessionReplayViewService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceCustomFieldService.js +93 -0
- package/build/dist/Server/Services/ScheduledMaintenanceCustomFieldService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +29 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/TeamMemberService.js +62 -2
- package/build/dist/Server/Services/TeamMemberService.js.map +1 -1
- package/build/dist/Server/Services/TeamPermissionService.js +166 -3
- package/build/dist/Server/Services/TeamPermissionService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js +562 -18
- package/build/dist/Server/Services/TelemetryIngestionKeyService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationRuleService.js +60 -25
- package/build/dist/Server/Services/UserNotificationRuleService.js.map +1 -1
- package/build/dist/Server/Services/UserNotificationSettingService.js +26 -6
- package/build/dist/Server/Services/UserNotificationSettingService.js.map +1 -1
- package/build/dist/Server/Services/UserTelegramService.js +257 -4
- package/build/dist/Server/Services/UserTelegramService.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js +29 -4
- package/build/dist/Server/Types/Database/Permissions/ReadPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryUtil.js +36 -0
- package/build/dist/Server/Types/Database/QueryUtil.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +75 -0
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/APIKey/AccessPermission.js +2 -2
- package/build/dist/Server/Utils/APIKey/AccessPermission.js.map +1 -1
- package/build/dist/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.js +27 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldDefinitionMappingHooks.js.map +1 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldMappingRegistry.js +226 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldMappingRegistry.js.map +1 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldMappingValidator.js +189 -0
- package/build/dist/Server/Utils/CustomField/CustomFieldMappingValidator.js.map +1 -0
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +53 -18
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/DataSource/EgressGuard.js +121 -7
- package/build/dist/Server/Utils/DataSource/EgressGuard.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js +38 -5
- package/build/dist/Server/Utils/EmailRollup/EmailRollupConstants.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js +54 -3
- package/build/dist/Server/Utils/EmailRollup/EmailRollupFlushRunner.js.map +1 -1
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js +2 -2
- package/build/dist/Server/Utils/EmailRollup/EmailRollupRenderer.js.map +1 -1
- package/build/dist/Server/Utils/FrontendEnvironment.js +33 -0
- package/build/dist/Server/Utils/FrontendEnvironment.js.map +1 -0
- package/build/dist/Server/Utils/LogRedaction.js +20 -0
- package/build/dist/Server/Utils/LogRedaction.js.map +1 -1
- package/build/dist/Server/Utils/Logger.js +10 -0
- package/build/dist/Server/Utils/Logger.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +323 -81
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +20 -4
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js +29 -5
- package/build/dist/Server/Utils/Monitor/Criteria/MetricMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +29 -6
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +254 -48
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +58 -9
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js +9 -3
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js +26 -12
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaMessageFormatter.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +74 -33
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +22 -6
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js +22 -0
- package/build/dist/Server/Utils/Monitor/MonitorTemplateUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js +114 -0
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMacLearningUtil.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js +25 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/SeriesContextEnricher.js +237 -0
- package/build/dist/Server/Utils/Monitor/SeriesContextEnricher.js.map +1 -0
- package/build/dist/Server/Utils/SSRFProtection.js +136 -9
- package/build/dist/Server/Utils/SSRFProtection.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js +174 -89
- package/build/dist/Server/Utils/SessionReplay/SessionReplayGateCache.js.map +1 -1
- package/build/dist/Server/Utils/SessionReplay/SessionReplayHealthCounters.js +223 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayHealthCounters.js.map +1 -0
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +945 -164
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +3 -14
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/TelegramVerificationToken.js +129 -0
- package/build/dist/Server/Utils/TelegramVerificationToken.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js +9 -1
- package/build/dist/Server/Utils/Telemetry/EntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/PinServiceName.js +163 -0
- package/build/dist/Server/Utils/Telemetry/PinServiceName.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +70 -13
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js +28 -15
- package/build/dist/Server/Utils/Telemetry/TelemetryFanInWriter.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js +64 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.js +171 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryIngestionKeyRateLimiter.js.map +1 -0
- package/build/dist/Server/Utils/VM/VMAPI.js.map +1 -1
- package/build/dist/Server/Utils/VM/VMRunner.js +780 -69
- package/build/dist/Server/Utils/VM/VMRunner.js.map +1 -1
- package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js +54 -0
- package/build/dist/Types/BaseDatabase/IncludesAnyOfGroups.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js +60 -0
- package/build/dist/Types/CustomField/CustomFieldMappingCatalog.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldMappingSourceResource.js +20 -0
- package/build/dist/Types/CustomField/CustomFieldMappingSourceResource.js.map +1 -0
- package/build/dist/Types/CustomField/CustomFieldValueMapping.js +141 -0
- package/build/dist/Types/CustomField/CustomFieldValueMapping.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardVariable.js +1 -0
- package/build/dist/Types/Dashboard/DashboardVariable.js.map +1 -1
- package/build/dist/Types/Icon/IconProp.js +1 -0
- package/build/dist/Types/Icon/IconProp.js.map +1 -1
- package/build/dist/Types/JSON.js +1 -0
- package/build/dist/Types/JSON.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +131 -37
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Monitor/CephAlertTemplates.js +130 -171
- package/build/dist/Types/Monitor/CephAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/DockerAlertTemplates.js +286 -121
- package/build/dist/Types/Monitor/DockerAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/DockerSwarmAlertTemplates.js +84 -105
- package/build/dist/Types/Monitor/DockerSwarmAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/HostAlertTemplates.js +250 -122
- package/build/dist/Types/Monitor/HostAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/IotAlertTemplates.js +81 -81
- package/build/dist/Types/Monitor/IotAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +375 -215
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesMetricCatalog.js +19 -19
- package/build/dist/Types/Monitor/KubernetesMetricCatalog.js.map +1 -1
- package/build/dist/Types/Monitor/PodmanAlertTemplates.js +104 -153
- package/build/dist/Types/Monitor/PodmanAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +156 -91
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +67 -6
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +38 -7
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js +261 -0
- package/build/dist/Types/Monitor/Recommendation/RecommendationCriteriaBuilder.js.map +1 -0
- package/build/dist/Types/Monitor/RumAlertTemplates.js +46 -7
- package/build/dist/Types/Monitor/RumAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/SeriesContext/SeriesDebugHints.js +422 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesDebugHints.js.map +1 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js +441 -0
- package/build/dist/Types/Monitor/SeriesContext/SeriesLabelDisplay.js.map +1 -0
- package/build/dist/Types/Monitor/ServiceAlertTemplates.js +79 -15
- package/build/dist/Types/Monitor/ServiceAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/UptimeHistoryLabels.js +17 -0
- package/build/dist/Types/Monitor/UptimeHistoryLabels.js.map +1 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js +291 -6
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
- package/build/dist/Types/NotificationSetting/RoutineEmailEvents.js +29 -0
- package/build/dist/Types/NotificationSetting/RoutineEmailEvents.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplay.js +117 -0
- package/build/dist/Types/Rum/SessionReplay.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayApi.js +181 -0
- package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayCaptureTrigger.js +21 -12
- package/build/dist/Types/Rum/SessionReplayCaptureTrigger.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayConsentMode.js +11 -7
- package/build/dist/Types/Rum/SessionReplayConsentMode.js.map +1 -1
- package/build/dist/Types/Rum/SessionReplayCustomEvents.js +172 -0
- package/build/dist/Types/Rum/SessionReplayCustomEvents.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayHealth.js +37 -0
- package/build/dist/Types/Rum/SessionReplayHealth.js.map +1 -0
- package/build/dist/Types/SerializableObjectDictionary.js +4 -0
- package/build/dist/Types/SerializableObjectDictionary.js.map +1 -1
- package/build/dist/Types/Telemetry/InventoryLiveness.js +37 -0
- package/build/dist/Types/Telemetry/InventoryLiveness.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +85 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestionKeyPolicy.js +15 -0
- package/build/dist/Types/Telemetry/TelemetryIngestionKeyPolicy.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestionKeyType.js +30 -0
- package/build/dist/Types/Telemetry/TelemetryIngestionKeyType.js.map +1 -0
- package/build/dist/Types/WebsiteRequest.js +55 -3
- package/build/dist/Types/WebsiteRequest.js.map +1 -1
- package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js +7 -0
- package/build/dist/UI/Components/CSVFileUpload/CSVFileUpload.js.map +1 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +13 -2
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +6 -2
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js +13 -2
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Types/XAxis/XAxis.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -3
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js +40 -9
- package/build/dist/UI/Components/Charts/Utils/TimeAnnotation.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +186 -0
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +89 -8
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/MapFromCustomFieldInput.js +118 -0
- package/build/dist/UI/Components/CustomFields/MapFromCustomFieldInput.js.map +1 -0
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js +137 -0
- package/build/dist/UI/Components/Dashboard/DashboardVariableControl.js.map +1 -0
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js +112 -7
- package/build/dist/UI/Components/Graphs/DayUptimeGraph.js.map +1 -1
- package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js +5 -162
- package/build/dist/UI/Components/Graphs/UptimeBarTooltip.js.map +1 -1
- package/build/dist/UI/Components/Graphs/UptimeDaySummary.js +181 -0
- package/build/dist/UI/Components/Graphs/UptimeDaySummary.js.map +1 -0
- package/build/dist/UI/Components/Header/Header.js +2 -3
- package/build/dist/UI/Components/Header/Header.js.map +1 -1
- package/build/dist/UI/Components/Header/IconDropdown/IconDropdownItem.js +2 -1
- package/build/dist/UI/Components/Header/IconDropdown/IconDropdownItem.js.map +1 -1
- package/build/dist/UI/Components/Header/IconDropdown/IconDropdownMenu.js +7 -1
- package/build/dist/UI/Components/Header/IconDropdown/IconDropdownMenu.js.map +1 -1
- package/build/dist/UI/Components/Header/ProjectPicker/ProjectPicker.js +7 -1
- package/build/dist/UI/Components/Header/ProjectPicker/ProjectPicker.js.map +1 -1
- package/build/dist/UI/Components/HeaderAlert/NotificationBell/NotificationBellDropdown.js +7 -1
- package/build/dist/UI/Components/HeaderAlert/NotificationBell/NotificationBellDropdown.js.map +1 -1
- package/build/dist/UI/Components/Icon/Icon.js +5 -0
- package/build/dist/UI/Components/Icon/Icon.js.map +1 -1
- package/build/dist/UI/Components/JSONTable/JSONTable.js +2 -2
- package/build/dist/UI/Components/JSONTable/JSONTable.js.map +1 -1
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcutsModal.js +35 -0
- package/build/dist/UI/Components/KeyboardShortcut/KeyboardShortcutsModal.js.map +1 -0
- package/build/dist/UI/Components/Monitor/SeriesDebugCommandsViewer.js +34 -0
- package/build/dist/UI/Components/Monitor/SeriesDebugCommandsViewer.js.map +1 -0
- package/build/dist/UI/Components/Monitor/SeriesLabelsViewer.js +49 -0
- package/build/dist/UI/Components/Monitor/SeriesLabelsViewer.js.map +1 -0
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js +1 -1
- package/build/dist/UI/Components/MonitorGraphs/Uptime.js.map +1 -1
- package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js +32 -6
- package/build/dist/UI/Components/MonitorGraphs/UptimeBarDayModal.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js +38 -0
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesCatalog.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +22 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -1
- package/build/dist/UI/Components/Table/CellClassName.js +55 -0
- package/build/dist/UI/Components/Table/CellClassName.js.map +1 -0
- package/build/dist/UI/Components/Table/TableRow.js +16 -13
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/Table/TableSkeletonRows.js +9 -8
- package/build/dist/UI/Components/Table/TableSkeletonRows.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +25 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/UI/Components/Workflow/ComponentsModal.js +47 -30
- package/build/dist/UI/Components/Workflow/ComponentsModal.js.map +1 -1
- package/build/dist/UI/Components/Workflow/NodePlacement.js +37 -0
- package/build/dist/UI/Components/Workflow/NodePlacement.js.map +1 -0
- package/build/dist/UI/Components/Workflow/Workflow.js +35 -18
- package/build/dist/UI/Components/Workflow/Workflow.js.map +1 -1
- package/build/dist/UI/Config.js +10 -20
- package/build/dist/UI/Config.js.map +1 -1
- package/build/dist/UI/Utils/GlobalKeyboardShortcut.js +138 -0
- package/build/dist/UI/Utils/GlobalKeyboardShortcut.js.map +1 -0
- package/build/dist/UI/Utils/Telemetry/BrowserTelemetryConfig.js +14 -0
- package/build/dist/UI/Utils/Telemetry/BrowserTelemetryConfig.js.map +1 -0
- package/build/dist/UI/Utils/Telemetry/Telemetry.js +7 -5
- package/build/dist/UI/Utils/Telemetry/Telemetry.js.map +1 -1
- package/build/dist/Utils/API.js +67 -5
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js +9 -1
- package/build/dist/Utils/Dashboard/Components/DashboardMonitorListComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/LabelVariable.js +96 -0
- package/build/dist/Utils/Dashboard/LabelVariable.js.map +1 -0
- package/build/dist/Utils/Dashboard/VariableUrlState.js +19 -4
- package/build/dist/Utils/Dashboard/VariableUrlState.js.map +1 -1
- package/build/dist/Utils/HTTPResponseBodyReader.js +157 -0
- package/build/dist/Utils/HTTPResponseBodyReader.js.map +1 -0
- package/build/dist/Utils/MetricUnitUtil.js +26 -0
- package/build/dist/Utils/MetricUnitUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js +112 -0
- package/build/dist/Utils/Monitor/DeviceMacLearningUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/MetricValueFormatter.js +276 -0
- package/build/dist/Utils/Monitor/MetricValueFormatter.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js +101 -0
- package/build/dist/Utils/Monitor/MonitorTemplateCustomFieldUtil.js.map +1 -0
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js +7 -2
- package/build/dist/Utils/Monitor/NetworkDeviceMonitorTemplateUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js +484 -0
- package/build/dist/Utils/Monitor/NetworkTopologyUtil.js.map +1 -1
- package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js +59 -0
- package/build/dist/Utils/NetworkAutomation/AutoImportRunChain.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js +69 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryScanStatus.js.map +1 -0
- package/build/dist/Utils/Rum/ChunkMath.js +76 -0
- package/build/dist/Utils/Rum/ChunkMath.js.map +1 -1
- package/build/dist/Utils/Rum/SessionReplayHealth.js +534 -0
- package/build/dist/Utils/Rum/SessionReplayHealth.js.map +1 -0
- package/build/dist/Utils/Rum/SessionReplayStringMap.js +165 -0
- package/build/dist/Utils/Rum/SessionReplayStringMap.js.map +1 -0
- package/build/dist/Utils/Schema/ModelSchema.js +1 -0
- package/build/dist/Utils/Schema/ModelSchema.js.map +1 -1
- package/build/dist/Utils/StatusPage/ResourceSearch.js +200 -0
- package/build/dist/Utils/StatusPage/ResourceSearch.js.map +1 -0
- package/build/dist/Utils/Telemetry/OriginAllowList.js +299 -0
- package/build/dist/Utils/Telemetry/OriginAllowList.js.map +1 -0
- package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js +147 -0
- package/build/dist/Utils/Uptime/DayUptimeGraphUtil.js.map +1 -0
- package/build/dist/Utils/ValueFormatter.js +32 -1
- package/build/dist/Utils/ValueFormatter.js.map +1 -1
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/test-setup.sh +9 -9
- 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
|
-
|
|
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:
|
|
1326
|
-
|
|
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
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
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
|
-
*
|
|
1364
|
-
*
|
|
1365
|
-
*
|
|
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("
|
|
1369
|
-
|
|
1370
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
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
|
-
|
|
1391
|
-
|
|
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
|
-
*
|
|
1398
|
-
*
|
|
1399
|
-
*
|
|
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("
|
|
1402
|
-
|
|
1403
|
-
|
|
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
|
-
|
|
1406
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1416
|
-
|
|
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
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
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
|
-
|
|
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
|
+
});
|