@oneuptime/common 13.0.5 → 13.0.6
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/AuditLog.ts +81 -1
- package/Models/AnalyticsModels/SecurityEvent.ts +4 -4
- package/Models/DatabaseModels/Alert.ts +62 -0
- package/Models/DatabaseModels/GlobalConfig.ts +1 -1
- package/Models/DatabaseModels/Incident.ts +66 -0
- package/Models/DatabaseModels/IncidentEpisodePublicNote.ts +74 -0
- package/Models/DatabaseModels/IncidentPublicNote.ts +74 -0
- package/Models/DatabaseModels/Index.ts +6 -4
- package/Models/DatabaseModels/NetworkDevice.ts +67 -0
- package/Models/DatabaseModels/NetworkDeviceDiagnostic.ts +746 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +151 -0
- package/Models/DatabaseModels/NetworkSiteAssignmentRule.ts +1 -1
- package/Models/DatabaseModels/Project.ts +41 -0
- package/Models/DatabaseModels/ScheduledMaintenancePublicNote.ts +74 -0
- package/Models/DatabaseModels/SecurityEventConnection.ts +11 -11
- package/Models/DatabaseModels/ServiceLevelObjective.ts +185 -8
- package/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.ts +746 -43
- package/Models/DatabaseModels/ServiceLevelObjectiveFeed.ts +575 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.ts +582 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveOwnerTeam.ts +16 -0
- package/Models/DatabaseModels/ServiceLevelObjectiveOwnerUser.ts +16 -0
- package/Models/DatabaseModels/StatusPageAnnouncement.ts +75 -0
- package/Models/DatabaseModels/ThreatIntelFeed.ts +1 -1
- package/Server/API/BaseAPI.ts +17 -0
- package/Server/API/BillingInvoiceAPI.ts +343 -30
- package/Server/API/BillingPaymentMethodAPI.ts +126 -5
- package/Server/API/DashboardAPI.ts +96 -2
- package/Server/API/SecurityEventConnectionAPI.ts +141 -11
- package/Server/API/StatusPageAPI.ts +308 -199
- package/Server/Infrastructure/Postgres/SchemaMigrations/1792800000000-AddNetworkDeviceDiagnostic.ts +83 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1792900000000-AddNetworkDeviceDnsNameAndShortDeviceNames.ts +36 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1793000000000-AddNetbiosLookupToNetworkDeviceDiscoveryScan.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1793100000000-SloProductOverhaul.ts +516 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1793200000000-BackfillSloMonitorRulesAndAffectedResources.ts +98 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1793300000000-AddDataResidencyToProject.ts +27 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1793400000000-AddSubscriberUpdateNotificationStatus.ts +97 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +14 -0
- package/Server/Services/AlertEpisodeLabelRuleEngineService.ts +138 -67
- package/Server/Services/AlertEpisodeOwnerRuleEngineService.ts +213 -84
- package/Server/Services/AlertEpisodePrivacyRuleEngineService.ts +112 -35
- package/Server/Services/AlertLabelRuleEngineService.ts +299 -217
- package/Server/Services/AlertOwnerRuleEngineService.ts +572 -396
- package/Server/Services/AlertPrivacyRuleEngineService.ts +116 -40
- package/Server/Services/AlertService.ts +111 -4
- package/Server/Services/AuditLogService.ts +612 -59
- package/Server/Services/BillingInvoiceService.ts +112 -102
- package/Server/Services/BillingService.ts +539 -11
- package/Server/Services/CephClusterLabelRuleEngineService.ts +170 -109
- package/Server/Services/CephClusterOwnerRuleEngineService.ts +233 -115
- package/Server/Services/CloudResourceLabelRuleEngineService.ts +163 -100
- package/Server/Services/CloudResourceOwnerRuleEngineService.ts +236 -115
- package/Server/Services/DashboardLabelRuleEngineService.ts +145 -84
- package/Server/Services/DashboardOwnerRuleEngineService.ts +213 -95
- package/Server/Services/DatabaseService.ts +171 -21
- package/Server/Services/DockerHostLabelRuleEngineService.ts +171 -110
- package/Server/Services/DockerHostOwnerRuleEngineService.ts +233 -115
- package/Server/Services/DockerSwarmClusterLabelRuleEngineService.ts +181 -112
- package/Server/Services/DockerSwarmClusterOwnerRuleEngineService.ts +243 -118
- package/Server/Services/HostLabelRuleEngineService.ts +167 -106
- package/Server/Services/HostOwnerRuleEngineService.ts +229 -111
- package/Server/Services/IncidentEpisodeLabelRuleEngineService.ts +141 -67
- package/Server/Services/IncidentEpisodeOwnerRuleEngineService.ts +272 -72
- package/Server/Services/IncidentEpisodePrivacyRuleEngineService.ts +112 -35
- package/Server/Services/IncidentEpisodePublicNoteService.ts +24 -0
- package/Server/Services/IncidentLabelRuleEngineService.ts +279 -204
- package/Server/Services/IncidentOwnerRuleEngineService.ts +595 -425
- package/Server/Services/IncidentPrivacyRuleEngineService.ts +117 -44
- package/Server/Services/IncidentPublicNoteService.ts +24 -0
- package/Server/Services/IncidentService.ts +114 -3
- package/Server/Services/IncomingCallPolicyLabelRuleEngineService.ts +147 -83
- package/Server/Services/IncomingCallPolicyOwnerRuleEngineService.ts +221 -100
- package/Server/Services/Index.ts +6 -4
- package/Server/Services/IoTFleetLabelRuleEngineService.ts +145 -84
- package/Server/Services/IoTFleetOwnerRuleEngineService.ts +213 -95
- package/Server/Services/KubernetesClusterLabelRuleEngineService.ts +181 -112
- package/Server/Services/KubernetesClusterOwnerRuleEngineService.ts +243 -118
- package/Server/Services/LabelService.ts +23 -28
- package/Server/Services/MonitorLabelRuleEngineService.ts +152 -92
- package/Server/Services/MonitorOwnerRuleEngineService.ts +220 -104
- package/Server/Services/MonitorProbeService.ts +20 -2
- package/Server/Services/MonitorService.ts +11 -9
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +376 -5
- package/Server/Services/NetworkDeviceDiagnosticService.ts +417 -0
- package/Server/Services/NetworkDeviceLabelRuleEngineService.ts +147 -83
- package/Server/Services/NetworkDeviceOwnerRuleEngineService.ts +218 -97
- package/Server/Services/NetworkDeviceService.ts +39 -2
- package/Server/Services/OnCallDutyPolicyLabelRuleEngineService.ts +151 -80
- package/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.ts +222 -97
- package/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.ts +154 -83
- package/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.ts +225 -100
- package/Server/Services/PodmanHostLabelRuleEngineService.ts +171 -110
- package/Server/Services/PodmanHostOwnerRuleEngineService.ts +235 -115
- package/Server/Services/ProjectService.ts +111 -10
- package/Server/Services/ProxmoxClusterLabelRuleEngineService.ts +174 -110
- package/Server/Services/ProxmoxClusterOwnerRuleEngineService.ts +239 -116
- package/Server/Services/RumApplicationLabelRuleEngineService.ts +137 -73
- package/Server/Services/RumApplicationOwnerRuleEngineService.ts +200 -83
- package/Server/Services/RunbookLabelRuleEngineService.ts +146 -84
- package/Server/Services/RunbookOwnerRuleEngineService.ts +216 -95
- package/Server/Services/ScheduledMaintenanceLabelRuleEngineService.ts +324 -230
- package/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.ts +613 -428
- package/Server/Services/ScheduledMaintenancePublicNoteService.ts +24 -0
- package/Server/Services/ScheduledMaintenanceService.ts +43 -14
- package/Server/Services/SecurityEventConnectionService.ts +56 -4
- package/Server/Services/ServerlessFunctionLabelRuleEngineService.ts +143 -74
- package/Server/Services/ServerlessFunctionOwnerRuleEngineService.ts +206 -85
- package/Server/Services/ServiceLabelRuleEngineService.ts +172 -110
- package/Server/Services/ServiceLevelObjectiveBurnRateRuleService.ts +1057 -18
- package/Server/Services/ServiceLevelObjectiveFeedService.ts +108 -0
- package/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.ts +793 -97
- package/Server/Services/ServiceLevelObjectiveMonitorRuleService.ts +1069 -0
- package/Server/Services/ServiceLevelObjectiveOwnerTeamService.ts +240 -1
- package/Server/Services/ServiceLevelObjectiveOwnerUserService.ts +257 -1
- package/Server/Services/ServiceLevelObjectiveService.ts +1356 -56
- package/Server/Services/ServiceOwnerRuleEngineService.ts +238 -116
- package/Server/Services/StatusPageAnnouncementService.ts +13 -0
- package/Server/Services/StatusPageLabelRuleEngineService.ts +146 -85
- package/Server/Services/StatusPageOwnerRuleEngineService.ts +210 -99
- package/Server/Services/StatusPageSubscriberNotificationTemplateService.ts +28 -232
- package/Server/Services/StatusPageSubscriberService.ts +6 -0
- package/Server/Services/TelemetryUsageBillingService.ts +38 -7
- package/Server/Services/ThreatIntelFeedService.ts +1 -1
- package/Server/Services/VMwareVCenterLabelRuleEngineService.ts +173 -110
- package/Server/Services/VMwareVCenterOwnerRuleEngineService.ts +238 -116
- package/Server/Services/WorkflowLabelRuleEngineService.ts +145 -84
- package/Server/Services/WorkflowOwnerRuleEngineService.ts +215 -95
- package/Server/Types/Database/UpdateByID.ts +3 -0
- package/Server/Types/Database/UpdateOneBy.ts +7 -0
- package/Server/Utils/AI/AlertAIContextBuilder.ts +19 -0
- package/Server/Utils/AI/IncidentAIContextBuilder.ts +39 -0
- package/Server/Utils/AI/Toolbox/AlertTools.ts +37 -1
- package/Server/Utils/AI/Toolbox/IncidentTools.ts +22 -1
- package/Server/Utils/AI/Toolbox/SloTools.ts +416 -51
- package/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.ts +151 -13
- package/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.ts +17 -4
- package/Server/Utils/Dashboard/PublicDashboardSloWidget.ts +155 -8
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +16 -0
- package/Server/Utils/Database/RelationValueUtil.ts +106 -0
- package/Server/Utils/Monitor/Criteria/CompareCriteria.ts +6 -1
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +22 -27
- package/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.ts +3 -1
- package/Server/Utils/Monitor/MonitorStepsProjectValidator.ts +50 -20
- package/Server/Utils/Monitor/NetworkDeviceHydrationUtil.ts +13 -1
- package/Server/Utils/Monitor/NetworkDeviceMetricUtil.ts +6 -11
- package/Server/Utils/Rules/MonitorRulePatternValidator.ts +51 -0
- package/Server/Utils/Rules/OwnerRuleAssignment.ts +110 -0
- package/Server/Utils/Rules/RuleRun/RuleApplication.ts +81 -0
- package/Server/Utils/Rules/RuleRun/RuleRunPermission.ts +188 -0
- package/Server/Utils/Rules/RuleRun/RuleRunRegistry.ts +803 -0
- package/Server/Utils/Rules/RuleRun/RuleRunner.ts +341 -0
- package/Server/Utils/SecurityEvent/ConnectorErrorMessage.ts +3 -2
- package/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.ts +6 -4
- package/Server/Utils/SecurityEvent/{GoogleSecOps → Connectors/GoogleSecOps}/GoogleSecOpsClient.ts +263 -16
- package/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnector.ts +2101 -0
- package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.ts +194 -14
- package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.ts +1 -2
- package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.ts +38 -7
- package/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.ts +2 -0
- package/Server/Utils/SecurityEvent/Connectors/Types.ts +242 -2
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +1 -1
- package/Server/Utils/SecurityEvent/SecurityEventDedupe.ts +3 -3
- package/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.ts +2 -2
- package/Server/Utils/SessionReplay/SessionReplayReadService.ts +217 -56
- package/Server/Utils/Slo/SloFeedUtil.ts +200 -0
- package/Server/Utils/Slo/SloLegacyMonitorLabelAdoption.ts +173 -0
- package/Server/Utils/Slo/SloMetricUtil.ts +327 -0
- package/Server/Utils/Slo/SloOwnerReferenceValidator.ts +208 -0
- package/Server/Utils/Slo/SloRecordReferenceValidator.ts +169 -0
- package/Server/Utils/StatusPage/MonitorRulePatternValidator.ts +6 -43
- package/Server/Utils/StatusPageResource.ts +50 -5
- package/Tests/App/Dashboard/AIInvestigationHeaderStatus.test.tsx +664 -15
- package/Tests/App/Dashboard/AddWidgetModal.test.tsx +4 -1
- package/Tests/App/Dashboard/AffectedResourcesSlo.test.tsx +307 -0
- package/Tests/App/Dashboard/ArchiveResourceCard.test.tsx +289 -0
- package/Tests/App/Dashboard/AuditLogsTable.test.tsx +598 -0
- package/Tests/App/Dashboard/BillingDefaultPaymentMethod.test.tsx +1046 -0
- package/Tests/App/Dashboard/BillingPaidUsageFlow.test.tsx +3 -0
- package/Tests/App/Dashboard/BillingPaymentMethodForm.test.tsx +201 -7
- package/Tests/App/Dashboard/BooleanSeriesCriteriaFilterText.test.ts +82 -0
- package/Tests/App/Dashboard/BreadcrumbCoverage.test.tsx +16 -1
- package/Tests/App/Dashboard/ChangeAlertStateAIHeader.test.tsx +116 -8
- package/Tests/App/Dashboard/ChatActivityFeed.test.tsx +168 -1
- package/Tests/App/Dashboard/ConnectionTestModal.test.tsx +1 -1
- package/Tests/App/Dashboard/ConnectorTestReportView.test.tsx +20 -3
- package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +1095 -1
- package/Tests/App/Dashboard/CorrelateFilterChips.test.tsx +910 -0
- package/Tests/App/Dashboard/CorrelateGraph.test.tsx +3747 -25
- package/Tests/App/Dashboard/CorrelateGraphHelpers.test.ts +1085 -0
- package/Tests/App/Dashboard/CorrelateInspector.test.tsx +1534 -0
- package/Tests/App/Dashboard/CorrelateNodeCard.test.tsx +965 -0
- package/Tests/App/Dashboard/CorrelateResultSummary.test.tsx +1525 -0
- package/Tests/App/Dashboard/CorrelationGraph.test.ts +1030 -0
- package/Tests/App/Dashboard/CorrelationGraphLayout.test.ts +526 -0
- package/Tests/App/Dashboard/DeviceDiagnostics.test.tsx +787 -0
- package/Tests/App/Dashboard/DeviceLatencyTrend.test.tsx +278 -0
- package/Tests/App/Dashboard/DiscoveryReviewInventoryRefresh.test.tsx +279 -0
- package/Tests/App/Dashboard/DiscoveryScanEditForm.test.tsx +87 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +443 -8
- package/Tests/App/Dashboard/EntityFilterDropdownArchivedSlos.test.tsx +340 -0
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +98 -2
- package/Tests/App/Dashboard/ExceptionSegmentedControl.test.tsx +1020 -0
- package/Tests/App/Dashboard/InvestigationEvidenceList.test.tsx +268 -47
- package/Tests/App/Dashboard/InvestigationPanel.test.tsx +1114 -76
- package/Tests/App/Dashboard/InvestigationPanelStatus.test.tsx +124 -2
- package/Tests/App/Dashboard/InvestigationReportView.test.tsx +279 -176
- package/Tests/App/Dashboard/InvestigationRunDetails.test.tsx +1166 -0
- package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +437 -0
- package/Tests/App/Dashboard/NetworkDeviceBulkDelete.test.tsx +65 -0
- package/Tests/App/Dashboard/NetworkTopologyLiveView.test.tsx +5 -0
- package/Tests/App/Dashboard/ResourceAuditLogsSideMenus.test.tsx +74 -0
- package/Tests/App/Dashboard/ResourceFeed.test.tsx +125 -5
- package/Tests/App/Dashboard/ResourceFeedPageWiring.test.ts +8 -1
- package/Tests/App/Dashboard/SecurityEventConnectionDiagnostics.test.tsx +1764 -0
- package/Tests/App/Dashboard/SecurityEventConnectionFormModal.test.tsx +1000 -64
- package/Tests/App/Dashboard/SecurityEventConnectionsEmptyState.test.tsx +448 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +739 -189
- package/Tests/App/Dashboard/SecurityEventSeverityPill.test.tsx +233 -0
- package/Tests/App/Dashboard/SecurityEventsEmptyState.test.tsx +282 -0
- package/Tests/App/Dashboard/SloActiveBurnEventsCard.test.tsx +538 -0
- package/Tests/App/Dashboard/SloBulkActionsArchive.test.tsx +252 -0
- package/Tests/App/Dashboard/SloDashboardTemplateWidgets.test.tsx +322 -518
- package/Tests/App/Dashboard/SloElement.test.tsx +194 -0
- package/Tests/App/Dashboard/SloFeed.test.tsx +289 -0
- package/Tests/App/Dashboard/SloHistoryCharts.test.tsx +471 -0
- package/Tests/App/Dashboard/SloHistorySeries.test.tsx +607 -0
- package/Tests/App/Dashboard/SloListWidget.test.tsx +783 -0
- package/Tests/App/Dashboard/SloMetricsPage.test.tsx +346 -0
- package/Tests/App/Dashboard/SloMonitorsPage.test.tsx +661 -0
- package/Tests/App/Dashboard/SloMonitorsSummaryCard.test.tsx +489 -0
- package/Tests/App/Dashboard/SloNavigationWiring.test.tsx +437 -0
- package/Tests/App/Dashboard/SloNoticeBanner.test.tsx +305 -0
- package/Tests/App/Dashboard/SloOverviewData.test.tsx +563 -0
- package/Tests/App/Dashboard/SloOverviewHero.test.tsx +311 -0
- package/Tests/App/Dashboard/SloOverviewKpiStrip.test.tsx +426 -0
- package/Tests/App/Dashboard/SloOverviewSidebarCards.test.tsx +493 -0
- package/Tests/App/Dashboard/SloSettings.test.tsx +633 -0
- package/Tests/App/Dashboard/SloStatusSummaryCards.test.tsx +403 -0
- package/Tests/App/Dashboard/SloViewSideMenuCounts.test.tsx +373 -0
- package/Tests/App/Dashboard/SloWidgetVariableBinding.test.tsx +689 -0
- package/Tests/App/Dashboard/TopologyConnectivitySection.test.tsx +206 -0
- package/Tests/App/Dashboard/TracesEntityNames.test.tsx +47 -15
- package/Tests/App/Dashboard/UseBulkShortenDeviceNames.test.tsx +1312 -0
- package/Tests/App/Dashboard/WidgetCatalog.test.ts +9 -1
- package/Tests/Models/AnalyticsModels/RumSessionReplayColumns.test.ts +14 -16
- package/Tests/Models/DatabaseModels/AuditLogModelConfiguration.test.ts +442 -0
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +9 -10
- package/Tests/Models/DatabaseModels/ResourceFeedModels.test.ts +48 -1
- package/Tests/Models/DatabaseModels/SloOverhaulModels.test.ts +835 -0
- package/Tests/Models/DatabaseModels/StaticRuleCriteriaInheritance.test.ts +3 -2
- package/Tests/Models/DiscoveryScanNetbiosLookupColumn.test.ts +328 -0
- package/Tests/Models/DiscoveryScanShortDeviceNamesColumn.test.ts +335 -0
- package/Tests/Models/NetworkDeviceDiagnosticContract.test.ts +194 -0
- package/Tests/Models/NetworkDeviceDnsNameColumn.test.ts +321 -0
- package/Tests/Models/ProjectDataResidencyColumn.test.ts +154 -0
- package/Tests/Models/SecurityDomainAccessControl.test.ts +181 -50
- package/Tests/Server/API/BaseAPI.test.ts +6 -0
- package/Tests/Server/API/BaseAPIUpdateMiscDataProps.test.ts +217 -0
- package/Tests/Server/API/BillingInvoiceAPI.test.ts +7 -0
- package/Tests/Server/API/BillingInvoiceAPIPaymentStates.test.ts +990 -0
- package/Tests/Server/API/BillingPaymentMethodAPI.test.ts +498 -0
- package/Tests/Server/API/DashboardPublicSloAPI.test.ts +405 -0
- package/Tests/Server/API/SecurityEventConnectionAPI.test.ts +901 -7
- package/Tests/Server/API/SessionReplayAPI.test.ts +116 -0
- package/Tests/Server/API/StatusPageManageSubscriptionRecipient.test.ts +857 -0
- package/Tests/Server/API/StatusPageManageSubscriptionTemplateVariables.test.ts +1084 -0
- package/Tests/Server/Infrastructure/Postgres/AddConfigurableRuleCriteriaMigration.test.ts +33 -3
- package/Tests/Server/Infrastructure/Postgres/AddDataResidencyToProjectMigration.test.ts +202 -0
- package/Tests/Server/Infrastructure/Postgres/AddNetbiosLookupToNetworkDeviceDiscoveryScanMigration.test.ts +330 -0
- package/Tests/Server/Infrastructure/Postgres/AddNetworkDeviceDnsNameAndShortDeviceNamesMigration.test.ts +340 -0
- package/Tests/Server/Infrastructure/Postgres/BackfillSloMonitorRulesAndAffectedResourcesMigration.test.ts +343 -0
- package/Tests/Server/Infrastructure/Postgres/SloProductOverhaulMigration.test.ts +801 -0
- package/Tests/Server/Services/AddSubscriberUpdateNotificationStatusMigration.test.ts +307 -0
- package/Tests/Server/Services/AuditLogService.test.ts +1124 -0
- package/Tests/Server/Services/AutoImportScanCredentialSelect.test.ts +200 -1
- package/Tests/Server/Services/BillingInvoiceServiceMutexRelease.test.ts +254 -0
- package/Tests/Server/Services/BillingService.test.ts +70 -0
- package/Tests/Server/Services/BillingServicePayInvoiceStates.test.ts +723 -0
- package/Tests/Server/Services/BillingServicePaymentMethodReads.test.ts +14 -2
- package/Tests/Server/Services/BillingServiceSubscriptionPaymentMethodSync.test.ts +1634 -0
- package/Tests/Server/Services/DatabaseServiceAuditRelationDiff.test.ts +492 -0
- package/Tests/Server/Services/DatabaseServicePasswordFieldSecrets.test.ts +629 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +431 -0
- package/Tests/Server/Services/IncidentAlertSloReferenceGuard.test.ts +358 -0
- package/Tests/Server/Services/MonitorProbeServicePastDueMonitoring.test.ts +362 -0
- package/Tests/Server/Services/NetworkDeviceAutoImportRuleEngineService.test.ts +1460 -1
- package/Tests/Server/Services/NetworkDeviceDiagnosticService.test.ts +962 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +106 -0
- package/Tests/Server/Services/NetworkDeviceRuleEngines.test.ts +10 -1
- package/Tests/Server/Services/NetworkDeviceServicePastDueMonitoring.test.ts +335 -0
- package/Tests/Server/Services/NetworkDeviceSiteRuleDnsName.test.ts +568 -0
- package/Tests/Server/Services/ProjectDataResidencyPostgres.test.ts +344 -0
- package/Tests/Server/Services/ProjectServiceAuditLogSettingsCache.test.ts +187 -0
- package/Tests/Server/Services/ProjectServiceDataResidency.test.ts +369 -0
- package/Tests/Server/Services/ProjectServiceDataResidencySelfHosted.test.ts +176 -0
- package/Tests/Server/Services/ProjectServicePastDueMonitoring.test.ts +289 -0
- package/Tests/Server/Services/ResourceFeedServices.test.ts +24 -8
- package/Tests/Server/Services/RuleRun/IncidentAlertMaintenanceRuleEnginesRun.test.ts +1396 -0
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupARun.test.ts +917 -0
- package/Tests/Server/Services/RuleRun/LabelRuleEnginesGroupBRun.test.ts +1262 -0
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupARun.test.ts +902 -0
- package/Tests/Server/Services/RuleRun/OwnerRuleEnginesGroupBRun.test.ts +1163 -0
- package/Tests/Server/Services/RuleRun/PrivacyRuleEnginesRun.test.ts +843 -0
- package/Tests/Server/Services/ScheduledMaintenanceSubscriberTemplateFormats.test.ts +465 -0
- package/Tests/Server/Services/SecurityEventConnectionService.test.ts +525 -4
- package/Tests/Server/Services/SecurityEventLastErrorColumnWidth.test.ts +77 -23
- package/Tests/Server/Services/ServiceLevelObjectiveBurnRateRuleFeedWrites.test.ts +777 -0
- package/Tests/Server/Services/ServiceLevelObjectiveBurnRateRuleOptions.test.ts +1399 -0
- package/Tests/Server/Services/ServiceLevelObjectiveBurnRateRuleService.test.ts +67 -74
- package/Tests/Server/Services/ServiceLevelObjectiveChildRowTenancy.test.ts +906 -0
- package/Tests/Server/Services/ServiceLevelObjectiveDeleteTenancy.test.ts +1029 -0
- package/Tests/Server/Services/ServiceLevelObjectiveFeedService.test.ts +313 -0
- package/Tests/Server/Services/ServiceLevelObjectiveFeedWrites.test.ts +1214 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMarkdownLink.test.ts +289 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.test.ts +1455 -430
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleLegacyLabelAdoption.test.ts +634 -0
- package/Tests/Server/Services/ServiceLevelObjectiveMonitorRuleService.test.ts +1193 -0
- package/Tests/Server/Services/{SloMonitorLabelRuleHooks.test.ts → ServiceLevelObjectiveMonitorRuleTriggers.test.ts} +135 -41
- package/Tests/Server/Services/ServiceLevelObjectiveOwnerFeedWrites.test.ts +671 -0
- package/Tests/Server/Services/ServiceLevelObjectiveService.test.ts +180 -85
- package/Tests/Server/Services/ServiceLevelObjectiveServiceArchive.test.ts +537 -0
- package/Tests/Server/Services/ServiceLevelObjectiveServiceLegacyMonitorLabelWrite.test.ts +870 -0
- package/Tests/Server/Services/ServiceLevelObjectiveServiceMonitorRuleGuard.test.ts +343 -0
- package/Tests/Server/Services/ServiceLevelObjectiveServiceReEvaluation.test.ts +246 -0
- package/Tests/Server/Services/SloAffectedResourceCreatedFeed.test.ts +501 -0
- package/Tests/Server/Services/SloAffectedResourceLinkContract.test.ts +86 -0
- package/Tests/Server/Services/SloAffectedResourceMetricAttributes.test.ts +547 -0
- package/Tests/Server/Services/StaticRuleCriteriaRuntimeCoverage.test.ts +10 -4
- package/Tests/Server/Services/StatusPageMonitorRuleHooks.test.ts +7 -2
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateCompile.test.ts +174 -0
- package/Tests/Server/Services/StatusPageSubscriberNotificationTemplateServiceVariables.test.ts +40 -0
- package/Tests/Server/Services/SubscriberUpdateNotificationHooks.test.ts +541 -0
- package/Tests/Server/Services/TelemetryUsageBillingSloExclusion.test.ts +288 -0
- package/Tests/Server/TestingUtils/PastDueMonitoringClaimSql.ts +177 -0
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +51 -10
- package/Tests/Server/TestingUtils/__mocks__/Stripe.mock.ts +16 -1
- package/Tests/Server/Types/Database/Permissions/ProjectDataResidencyColumn.test.ts +386 -0
- package/Tests/Server/Types/Database/Permissions/SecurityRoleAccess.test.ts +26 -36
- package/Tests/Server/Utils/AI/SloAffectedResourceAIContext.test.ts +445 -0
- package/Tests/Server/Utils/AI/SloTools.test.ts +423 -1
- package/Tests/Server/Utils/AI/SloToolsArchive.test.ts +294 -0
- package/Tests/Server/Utils/AI/SloToolsRuleReadPermissions.test.ts +297 -0
- package/Tests/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.test.ts +255 -10
- package/Tests/Server/Utils/Dashboard/SloTemplatePublicDashboard.test.ts +404 -750
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +45 -0
- package/Tests/Server/Utils/Database/RelationValueUtil.test.ts +157 -0
- package/Tests/Server/Utils/Monitor/Criteria/BooleanSeriesEvaluateOverTime.test.ts +260 -0
- package/Tests/Server/Utils/Monitor/Criteria/DatabaseMonitorCriteria.test.ts +181 -0
- package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +147 -30
- package/Tests/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.test.ts +17 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidator.test.ts +136 -0
- package/Tests/Server/Utils/Monitor/MonitorStepsProjectValidatorLoadOrder.test.ts +472 -0
- package/Tests/Server/Utils/Monitor/NetworkDeviceHydrationUtil.test.ts +48 -2
- package/Tests/Server/Utils/Monitor/SloTemplateMetricEmitterContract.test.ts +435 -475
- package/Tests/Server/Utils/Rules/MonitorRulePatternValidator.test.ts +77 -0
- package/Tests/Server/Utils/Rules/OwnerRuleAssignment.test.ts +177 -0
- package/Tests/Server/Utils/Rules/RuleRun/RuleRunPermission.test.ts +365 -0
- package/Tests/Server/Utils/Rules/RuleRun/RuleRunRegistry.test.ts +173 -0
- package/Tests/Server/Utils/Rules/RuleRun/RuleRunner.test.ts +675 -0
- package/Tests/Server/Utils/SecurityEvent/ConnectorErrorMessage.test.ts +8 -8
- package/Tests/Server/Utils/SecurityEvent/Connectors/ConnectorFetchSummary.test.ts +261 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.test.ts +68 -13
- package/Tests/Server/Utils/SecurityEvent/{GoogleSecOpsAuth.test.ts → Connectors/GoogleSecOps/GoogleSecOpsAuth.test.ts} +5 -5
- package/Tests/Server/Utils/SecurityEvent/{GoogleSecOpsClient.test.ts → Connectors/GoogleSecOps/GoogleSecOpsClient.test.ts} +4 -4
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnector.test.ts +483 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorCuratedAndLateAlerts.test.ts +676 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorFetchEvents.test.ts +1994 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorFixtures.ts +332 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorHttpIntegration.test.ts +680 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorRequestContract.test.ts +1206 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnectorTestConnection.test.ts +1136 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsCuratedRuleCounts.test.ts +407 -0
- package/Tests/Server/Utils/SecurityEvent/{GoogleSecOpsErrorHandling.test.ts → Connectors/GoogleSecOps/GoogleSecOpsErrorHandling.test.ts} +5 -5
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsPollingAdaptiveCatchUp.test.ts +730 -0
- package/Tests/Server/Utils/SecurityEvent/{GoogleSecOpsClickhouseIntegration.test.ts → Connectors/GoogleSecOps/GoogleSecOpsPollingClickhouseIntegration.test.ts} +130 -35
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsPollingFailureTaxonomy.test.ts +772 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsPollingFixtures.ts +648 -0
- package/Tests/Server/Utils/SecurityEvent/{GoogleSecOpsHttpIntegration.test.ts → Connectors/GoogleSecOps/GoogleSecOpsPollingHttpIntegration.test.ts} +230 -225
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsPollingIntegration.test.ts +1390 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsPollingLoop.test.ts +981 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsPollingNothingImportedRegression.test.ts +290 -0
- package/Tests/Server/Utils/SecurityEvent/{GoogleSecOpsRequestContract.test.ts → Connectors/GoogleSecOps/GoogleSecOpsRequestContract.test.ts} +2 -2
- package/Tests/Server/Utils/SecurityEvent/{GoogleSecOpsResponseParsing.test.ts → Connectors/GoogleSecOps/GoogleSecOpsResponseParsing.test.ts} +5 -5
- package/Tests/Server/Utils/SecurityEvent/{GoogleSecOpsSearchDetections.test.ts → Connectors/GoogleSecOps/GoogleSecOpsSearchDetections.test.ts} +38 -5
- package/Tests/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsTesterIntegration.test.ts +421 -0
- package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.test.ts +1445 -3
- package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.test.ts +14 -4
- package/Tests/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.test.ts +313 -4
- package/Tests/Server/Utils/SessionReplay/SessionReplayReadServiceQueries.test.ts +352 -0
- package/Tests/Server/Utils/Slo/SloFeedUtil.test.ts +326 -0
- package/Tests/Server/Utils/Slo/SloLegacyMonitorLabelAdoption.test.ts +260 -0
- package/Tests/Server/Utils/Slo/SloMetricUtil.test.ts +592 -0
- package/Tests/Server/Utils/Slo/SloRecordReferenceValidator.test.ts +311 -0
- package/Tests/Server/Utils/StatusPageResource.test.ts +91 -3
- package/Tests/Types/Billing/SubscriptionStatus.test.ts +176 -0
- package/Tests/Types/Billing/SubscriptionStatusPastDueMonitoring.test.ts +187 -0
- package/Tests/Types/Dashboard/DashboardTemplateInvariants.test.ts +53 -7
- package/Tests/Types/Dashboard/SloDashboardTemplate.test.ts +816 -792
- package/Tests/Types/Monitor/CriteriaFilter.test.ts +216 -24
- package/Tests/Types/Monitor/MonitorStepDnssecMonitor.test.ts +14 -1
- package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +14 -1
- package/Tests/Types/Monitor/MonitorStepExternalStatusPageMonitor.test.ts +14 -1
- package/Tests/Types/Monitor/MonitorStepRetriesParsing.test.ts +201 -0
- package/Tests/Types/Rules/RuleRun.test.ts +315 -0
- package/Tests/Types/Rum/SessionReplayApiContracts.test.ts +60 -0
- package/Tests/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.test.ts +29 -4
- package/Tests/Types/StatusPage/SubscriberNotificationTemplateVariables.test.ts +217 -0
- package/Tests/Types/StatusPage/SubscriberUpdateNotification.test.ts +200 -0
- package/Tests/UI/Components/ActiveFilterChipsLockedFilters.test.tsx +141 -87
- package/Tests/UI/Components/AlertBanner.test.tsx +319 -0
- package/Tests/UI/Components/BulkArchiveActionsCopy.test.tsx +310 -0
- package/Tests/UI/Components/Detail/DetailIdIsUnique.test.tsx +87 -0
- package/Tests/UI/Components/Dropdown/DropdownClearable.test.tsx +393 -0
- package/Tests/UI/Components/Dropdown/DropdownMultiSelectChipAccessibleName.test.tsx +64 -0
- package/Tests/UI/Components/EmptyState/EmptyState.test.tsx +335 -4
- package/Tests/UI/Components/EmptyState/EmptyStateGuideLinks.test.tsx +337 -0
- package/Tests/UI/Components/Forms/ModelFormJsonColumns.test.tsx +21 -20
- package/Tests/UI/Components/LabelRuleImportExport.test.tsx +15 -2
- package/Tests/UI/Components/LockedFilterChip.test.tsx +281 -144
- package/Tests/UI/Components/MoreMenu.test.tsx +133 -0
- package/Tests/UI/Components/MoreMenuMotion.test.tsx +84 -3
- package/Tests/UI/Components/RuleCriteria/RuleCriteriaModelTable.test.tsx +3 -3
- package/Tests/UI/Components/RuleRun/RuleDetailFields.test.tsx +336 -0
- package/Tests/UI/Components/RuleRun/RuleTable.test.tsx +304 -0
- package/Tests/UI/Components/RuleRun/RunRuleNowModal.test.tsx +231 -0
- package/Tests/UI/Components/RuleRun/RunRulesBulkAction.test.ts +213 -0
- package/Tests/UI/Components/TelemetryActiveFilterChipsLockedFilters.test.tsx +124 -61
- package/Tests/UI/Rum/ReplayEngine.test.ts +154 -0
- package/Tests/UI/Rum/ReplayHeader.test.tsx +234 -123
- package/Tests/UI/Rum/ReplayPinControl.test.tsx +122 -0
- package/Tests/UI/Rum/ReplayPlayerChrome.test.tsx +245 -0
- package/Tests/UI/Rum/ReplayRail.test.tsx +294 -0
- package/Tests/UI/Rum/ReplayScrubber.test.tsx +213 -1
- package/Tests/UI/Rum/ReplayStage.test.tsx +565 -172
- package/Tests/UI/Rum/ReplayStageOverlays.test.tsx +260 -3
- package/Tests/UI/Rum/ReplayTabSwitcher.test.tsx +1391 -0
- package/Tests/UI/Rum/ReplayTimeline.test.tsx +222 -0
- package/Tests/UI/Utils/DashboardStackingLayers.test.ts +8 -1
- package/Tests/UI/Utils/RuleRunClient.test.ts +303 -0
- package/Tests/UI/Utils/Telemetry/TelemetryEntityNames.test.ts +1 -0
- package/Tests/Utils/Dashboard/Components/DashboardComponentDefaults.test.ts +2 -0
- package/Tests/Utils/Dashboard/Components/DashboardSloListComponent.test.ts +206 -0
- package/Tests/Utils/Dashboard/DashboardSloComponent.test.ts +36 -2
- package/Tests/Utils/Dashboard/SloTemplateVariableScoping.test.ts +409 -588
- package/Tests/Utils/Dashboard/SloWidgetSource.test.ts +351 -0
- package/Tests/Utils/Markdown/MarkdownEscape.test.ts +361 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +1425 -118
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +188 -3
- package/Tests/Utils/NetworkDiscovery/DiscoveryReverseDnsChain.test.ts +661 -13
- package/Tests/Utils/NetworkDiscovery/NetbiosNameUtil.test.ts +349 -0
- package/Tests/Utils/NetworkDiscovery/RunImportedDeviceNaming.test.ts +656 -0
- package/Tests/Utils/NetworkDiscovery/ShortDeviceNamePlanner.test.ts +1020 -0
- package/Tests/Utils/NetworkDiscovery/ShortHostnameUtil.test.ts +619 -0
- package/Tests/Utils/NetworkSite/CidrMatchUtil.test.ts +317 -0
- package/Tests/Utils/Project/DataResidency.test.ts +198 -0
- package/Tests/Utils/Rules/RuleRunSummary.test.ts +394 -0
- package/Tests/Utils/Slo/SloAffectedResourceMarkdown.test.ts +302 -0
- package/Tests/Utils/Slo/SloBurnRateTemplate.test.ts +468 -0
- package/Tests/Utils/Slo/SloEvaluation.test.ts +20 -4
- package/Tests/Utils/Slo/SloFeedMarkdown.test.ts +1185 -0
- package/Tests/Utils/Slo/SloHealth.test.ts +262 -0
- package/Tests/Utils/Slo/SloHistoryMetricName.test.ts +102 -0
- package/Tests/Utils/Slo/SloListWidgetFormat.test.ts +522 -0
- package/Tests/Utils/Slo/SloMetricType.test.ts +355 -0
- package/Tests/Utils/Slo/SloMonitorRuleCriteria.test.ts +387 -0
- package/Tests/Utils/Slo/SloMonitorStatusSummary.test.ts +210 -0
- package/Tests/Utils/Slo/SloOverviewText.test.ts +226 -0
- package/Tests/Utils/Slo/SloProjection.test.ts +1039 -0
- package/Tests/Utils/Slo/SloWidgetFormat.test.ts +47 -0
- package/Tests/Utils/Telemetry/LockedFilterSearch.test.ts +1 -80
- package/Types/BaseDatabase/EnableAuditLogOn.ts +41 -0
- package/Types/Billing/SubscriptionStatus.ts +39 -4
- package/Types/Dashboard/DashboardComponentType.ts +1 -0
- package/Types/Dashboard/DashboardComponents/ComponentArgument.ts +5 -0
- package/Types/Dashboard/DashboardComponents/DashboardSloComponent.ts +10 -0
- package/Types/Dashboard/DashboardComponents/DashboardSloListComponent.ts +29 -0
- package/Types/Dashboard/DashboardTemplates.ts +290 -297
- package/Types/Database/EnableAuditLog.ts +23 -1
- package/Types/Email/EmailTemplateType.ts +4 -0
- package/Types/Monitor/CriteriaFilter.ts +55 -8
- package/Types/Monitor/MonitorStep.ts +5 -2
- package/Types/Monitor/MonitorStepDnsMonitor.ts +3 -1
- package/Types/Monitor/MonitorStepDnssecMonitor.ts +3 -1
- package/Types/Monitor/MonitorStepDomainMonitor.ts +3 -1
- package/Types/Monitor/MonitorStepExternalStatusPageMonitor.ts +3 -1
- package/Types/Monitor/MonitorStepRetries.ts +59 -0
- package/Types/Monitor/MonitorStepSnmpMonitor.ts +9 -2
- package/Types/NetworkDevice/NetworkDeviceDiagnosticResult.ts +104 -0
- package/Types/NetworkDevice/NetworkDeviceDiagnosticStatus.ts +47 -0
- package/Types/NetworkDevice/NetworkDeviceDiagnosticType.ts +47 -0
- package/Types/NetworkDevice/NetworkDevicePingMetricNames.ts +20 -0
- package/Types/Permission.ts +127 -0
- package/Types/Rules/RuleCriteriaFieldRegistry.ts +5 -0
- package/Types/Rules/RuleRun.ts +518 -0
- package/Types/Rum/SessionReplayApi.ts +18 -0
- package/Types/SecurityEvent/Connectors/ConnectorDiagnostics.ts +6 -0
- package/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.ts +30 -4
- package/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.ts +108 -1
- package/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.ts +6 -2
- package/Types/ServiceLevelObjective/SloHistoryMetricName.ts +25 -0
- package/Types/ServiceLevelObjective/SloMetricType.ts +49 -0
- package/Types/StatusPage/StatusPageSubscriberNotificationEventType.ts +4 -0
- package/Types/StatusPage/SubscriberNotificationTemplateVariables.ts +331 -0
- package/Types/StatusPage/SubscriberNotificationTrigger.ts +12 -0
- package/Types/StatusPage/SubscriberUpdateNotification.ts +89 -0
- package/Types/Telemetry/LockedFilterDetail.ts +7 -28
- package/Types/Telemetry/ServiceType.ts +7 -0
- package/Typings/Index.d.ts +53 -3
- package/UI/Components/AlertBanner/AlertBanner.tsx +85 -27
- package/UI/Components/BulkUpdate/BulkArchiveActions.tsx +21 -2
- package/UI/Components/Detail/Detail.tsx +9 -2
- package/UI/Components/Dropdown/Dropdown.tsx +64 -1
- package/UI/Components/EmptyState/EmptyState.tsx +27 -3
- package/UI/Components/EmptyState/EmptyStateGuideLinks.tsx +124 -0
- package/UI/Components/Forms/ModelForm.tsx +6 -5
- package/UI/Components/LabelRule/LabelRuleTable.tsx +8 -2
- package/UI/Components/LogsViewer/LogsViewer.tsx +2 -6
- package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +2 -13
- package/UI/Components/LogsViewer/types.ts +2 -2
- package/UI/Components/MoreMenu/MoreMenu.tsx +14 -3
- package/UI/Components/RuleRun/RuleDetailFields.tsx +359 -0
- package/UI/Components/RuleRun/RuleTable.tsx +183 -0
- package/UI/Components/RuleRun/RuleView.tsx +170 -0
- package/UI/Components/RuleRun/RunRuleNowModal.tsx +147 -0
- package/UI/Components/RuleRun/RunRulesBulkAction.ts +103 -0
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +2 -6
- package/UI/Components/TelemetryViewer/components/LockedFilterChip.tsx +38 -104
- package/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.tsx +2 -18
- package/UI/Components/TelemetryViewer/types.ts +2 -2
- package/UI/Utils/Rules/RuleRunClient.ts +178 -0
- package/UI/Utils/Telemetry/TelemetryEntityNames.ts +8 -0
- package/Utils/Dashboard/Components/DashboardSloComponent.ts +19 -2
- package/Utils/Dashboard/Components/DashboardSloListComponent.ts +126 -0
- package/Utils/Dashboard/Components/Index.ts +7 -0
- package/Utils/Dashboard/SloWidgetSource.ts +188 -0
- package/Utils/Markdown/MarkdownEscape.ts +63 -0
- package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +161 -36
- package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +25 -0
- package/Utils/NetworkDiscovery/NetbiosNameUtil.ts +146 -0
- package/Utils/NetworkDiscovery/RunImportedDeviceNaming.ts +390 -0
- package/Utils/NetworkDiscovery/ShortDeviceNamePlanner.ts +291 -0
- package/Utils/NetworkDiscovery/ShortHostnameUtil.ts +91 -0
- package/Utils/NetworkSite/CidrMatchUtil.ts +37 -12
- package/Utils/Project/DataResidency.ts +63 -0
- package/Utils/Rules/RuleRunSummary.ts +323 -0
- package/Utils/Slo/SloAffectedResourceMarkdown.ts +123 -0
- package/Utils/Slo/SloBurnRateTemplate.ts +373 -0
- package/Utils/Slo/SloFeedMarkdown.ts +1507 -0
- package/Utils/Slo/SloHealth.ts +96 -9
- package/Utils/Slo/SloListWidgetFormat.ts +370 -0
- package/Utils/Slo/SloMetricType.ts +237 -0
- package/Utils/Slo/SloMonitorRuleCriteria.ts +364 -0
- package/Utils/Slo/SloMonitorStatusSummary.ts +210 -0
- package/Utils/Slo/SloOverviewText.ts +265 -0
- package/Utils/Slo/SloProjection.ts +967 -0
- package/Utils/Slo/SloWidgetFormat.ts +12 -2
- package/Utils/Telemetry/LockedFilterSearch.ts +3 -49
- package/build/dist/Models/AnalyticsModels/AuditLog.js +70 -1
- package/build/dist/Models/AnalyticsModels/AuditLog.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/SecurityEvent.js +4 -4
- package/build/dist/Models/DatabaseModels/Alert.js +60 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js +1 -1
- package/build/dist/Models/DatabaseModels/GlobalConfig.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Incident.js +64 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js +75 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js +75 -0
- package/build/dist/Models/DatabaseModels/IncidentPublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +6 -4
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +68 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiagnostic.js +772 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiagnostic.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +140 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js +1 -1
- package/build/dist/Models/DatabaseModels/NetworkSiteAssignmentRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +42 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js +75 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenancePublicNote.js.map +1 -1
- package/build/dist/Models/DatabaseModels/SecurityEventConnection.js +11 -11
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js +192 -10
- package/build/dist/Models/DatabaseModels/ServiceLevelObjective.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.js +798 -96
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveBurnRateRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js +596 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveFeed.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js +590 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveMonitorRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerTeam.js +16 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerTeam.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerUser.js +16 -0
- package/build/dist/Models/DatabaseModels/ServiceLevelObjectiveOwnerUser.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js +75 -0
- package/build/dist/Models/DatabaseModels/StatusPageAnnouncement.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ThreatIntelFeed.js +1 -1
- package/build/dist/Server/API/BaseAPI.js +14 -0
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/BillingInvoiceAPI.js +233 -19
- package/build/dist/Server/API/BillingInvoiceAPI.js.map +1 -1
- package/build/dist/Server/API/BillingPaymentMethodAPI.js +86 -5
- package/build/dist/Server/API/BillingPaymentMethodAPI.js.map +1 -1
- package/build/dist/Server/API/DashboardAPI.js +72 -2
- package/build/dist/Server/API/DashboardAPI.js.map +1 -1
- package/build/dist/Server/API/SecurityEventConnectionAPI.js +102 -10
- package/build/dist/Server/API/SecurityEventConnectionAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +236 -154
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792800000000-AddNetworkDeviceDiagnostic.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792800000000-AddNetworkDeviceDiagnostic.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792900000000-AddNetworkDeviceDnsNameAndShortDeviceNames.js +24 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1792900000000-AddNetworkDeviceDnsNameAndShortDeviceNames.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793000000000-AddNetbiosLookupToNetworkDeviceDiscoveryScan.js +21 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793000000000-AddNetbiosLookupToNetworkDeviceDiscoveryScan.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793100000000-SloProductOverhaul.js +207 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793100000000-SloProductOverhaul.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793200000000-BackfillSloMonitorRulesAndAffectedResources.js +72 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793200000000-BackfillSloMonitorRulesAndAffectedResources.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793300000000-AddDataResidencyToProject.js +20 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793300000000-AddDataResidencyToProject.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793400000000-AddSubscriberUpdateNotificationStatus.js +46 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1793400000000-AddSubscriberUpdateNotificationStatus.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +14 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js +112 -56
- package/build/dist/Server/Services/AlertEpisodeLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js +167 -68
- package/build/dist/Server/Services/AlertEpisodeOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js +87 -26
- package/build/dist/Server/Services/AlertEpisodePrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertLabelRuleEngineService.js +270 -203
- package/build/dist/Server/Services/AlertLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertOwnerRuleEngineService.js +498 -354
- package/build/dist/Server/Services/AlertOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js +97 -34
- package/build/dist/Server/Services/AlertPrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +91 -8
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AuditLogService.js +408 -44
- package/build/dist/Server/Services/AuditLogService.js.map +1 -1
- package/build/dist/Server/Services/BillingInvoiceService.js +94 -85
- package/build/dist/Server/Services/BillingInvoiceService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +406 -14
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js +141 -92
- package/build/dist/Server/Services/CephClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js +189 -101
- package/build/dist/Server/Services/CephClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js +135 -86
- package/build/dist/Server/Services/CloudResourceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js +189 -100
- package/build/dist/Server/Services/CloudResourceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DashboardLabelRuleEngineService.js +117 -69
- package/build/dist/Server/Services/DashboardLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js +171 -83
- package/build/dist/Server/Services/DashboardOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +125 -18
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js +141 -92
- package/build/dist/Server/Services/DockerHostLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js +189 -101
- package/build/dist/Server/Services/DockerHostOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js +145 -91
- package/build/dist/Server/Services/DockerSwarmClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js +191 -101
- package/build/dist/Server/Services/DockerSwarmClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/HostLabelRuleEngineService.js +141 -92
- package/build/dist/Server/Services/HostLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/HostOwnerRuleEngineService.js +190 -102
- package/build/dist/Server/Services/HostOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js +112 -56
- package/build/dist/Server/Services/IncidentEpisodeLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js +220 -63
- package/build/dist/Server/Services/IncidentEpisodeOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js +87 -26
- package/build/dist/Server/Services/IncidentEpisodePrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentEpisodePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentLabelRuleEngineService.js +258 -192
- package/build/dist/Server/Services/IncidentLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js +519 -376
- package/build/dist/Server/Services/IncidentOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js +95 -32
- package/build/dist/Server/Services/IncidentPrivacyRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncidentPublicNoteService.js +24 -0
- package/build/dist/Server/Services/IncidentPublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +90 -5
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js +113 -65
- package/build/dist/Server/Services/IncomingCallPolicyLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js +171 -83
- package/build/dist/Server/Services/IncomingCallPolicyOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +6 -4
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js +117 -69
- package/build/dist/Server/Services/IoTFleetLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js +171 -83
- package/build/dist/Server/Services/IoTFleetOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js +145 -91
- package/build/dist/Server/Services/KubernetesClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js +191 -101
- package/build/dist/Server/Services/KubernetesClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/LabelService.js +20 -25
- package/build/dist/Server/Services/LabelService.js.map +1 -1
- package/build/dist/Server/Services/MonitorLabelRuleEngineService.js +123 -77
- package/build/dist/Server/Services/MonitorLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js +167 -82
- package/build/dist/Server/Services/MonitorOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/MonitorProbeService.js +18 -2
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +11 -9
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +287 -5
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiagnosticService.js +333 -0
- package/build/dist/Server/Services/NetworkDeviceDiagnosticService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js +117 -69
- package/build/dist/Server/Services/NetworkDeviceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js +171 -83
- package/build/dist/Server/Services/NetworkDeviceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +36 -2
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js +117 -64
- package/build/dist/Server/Services/OnCallDutyPolicyLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js +173 -83
- package/build/dist/Server/Services/OnCallDutyPolicyOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js +116 -65
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js +171 -83
- package/build/dist/Server/Services/OnCallDutyPolicyScheduleOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js +141 -92
- package/build/dist/Server/Services/PodmanHostLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js +189 -101
- package/build/dist/Server/Services/PodmanHostOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +82 -10
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js +141 -92
- package/build/dist/Server/Services/ProxmoxClusterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js +189 -101
- package/build/dist/Server/Services/ProxmoxClusterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js +111 -63
- package/build/dist/Server/Services/RumApplicationLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js +161 -77
- package/build/dist/Server/Services/RumApplicationOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RunbookLabelRuleEngineService.js +117 -69
- package/build/dist/Server/Services/RunbookLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js +171 -83
- package/build/dist/Server/Services/RunbookOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js +280 -209
- package/build/dist/Server/Services/ScheduledMaintenanceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js +521 -376
- package/build/dist/Server/Services/ScheduledMaintenanceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js +24 -0
- package/build/dist/Server/Services/ScheduledMaintenancePublicNoteService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +24 -8
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/SecurityEventConnectionService.js +43 -3
- package/build/dist/Server/Services/SecurityEventConnectionService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js +115 -62
- package/build/dist/Server/Services/ServerlessFunctionLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js +163 -77
- package/build/dist/Server/Services/ServerlessFunctionOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLabelRuleEngineService.js +141 -92
- package/build/dist/Server/Services/ServiceLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveBurnRateRuleService.js +769 -18
- package/build/dist/Server/Services/ServiceLevelObjectiveBurnRateRuleService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveFeedService.js +96 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveFeedService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js +614 -87
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js +844 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveMonitorRuleService.js.map +1 -0
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerTeamService.js +207 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerTeamService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerUserService.js +221 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveOwnerUserService.js.map +1 -1
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js +997 -50
- package/build/dist/Server/Services/ServiceLevelObjectiveService.js.map +1 -1
- package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js +191 -102
- package/build/dist/Server/Services/ServiceOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageAnnouncementService.js +12 -0
- package/build/dist/Server/Services/StatusPageAnnouncementService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js +118 -70
- package/build/dist/Server/Services/StatusPageLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js +158 -76
- package/build/dist/Server/Services/StatusPageOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js +19 -215
- package/build/dist/Server/Services/StatusPageSubscriberNotificationTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageSubscriberService.js +6 -0
- package/build/dist/Server/Services/StatusPageSubscriberService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +27 -5
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/ThreatIntelFeedService.js +1 -1
- package/build/dist/Server/Services/VMwareVCenterLabelRuleEngineService.js +141 -92
- package/build/dist/Server/Services/VMwareVCenterLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js +189 -101
- package/build/dist/Server/Services/VMwareVCenterOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js +117 -69
- package/build/dist/Server/Services/WorkflowLabelRuleEngineService.js.map +1 -1
- package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js +171 -83
- package/build/dist/Server/Services/WorkflowOwnerRuleEngineService.js.map +1 -1
- package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js +17 -0
- package/build/dist/Server/Utils/AI/AlertAIContextBuilder.js.map +1 -1
- package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js +31 -0
- package/build/dist/Server/Utils/AI/IncidentAIContextBuilder.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js +24 -1
- package/build/dist/Server/Utils/AI/Toolbox/AlertTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js +21 -1
- package/build/dist/Server/Utils/AI/Toolbox/IncidentTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js +337 -53
- package/build/dist/Server/Utils/AI/Toolbox/SloTools.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js +108 -15
- package/build/dist/Server/Utils/Dashboard/PublicDashboardResourceListPolicy.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js +2 -2
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloHistoryPolicy.js.map +1 -1
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js +115 -6
- package/build/dist/Server/Utils/Dashboard/PublicDashboardSloWidget.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +13 -0
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -1
- package/build/dist/Server/Utils/Database/RelationValueUtil.js +82 -0
- package/build/dist/Server/Utils/Database/RelationValueUtil.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js +10 -6
- package/build/dist/Server/Utils/Monitor/Criteria/CompareCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +17 -21
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js +5 -4
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaExpectationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js +50 -10
- package/build/dist/Server/Utils/Monitor/MonitorStepsProjectValidator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js +13 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceHydrationUtil.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js +6 -10
- package/build/dist/Server/Utils/Monitor/NetworkDeviceMetricUtil.js.map +1 -1
- package/build/dist/Server/Utils/Rules/MonitorRulePatternValidator.js +34 -0
- package/build/dist/Server/Utils/Rules/MonitorRulePatternValidator.js.map +1 -0
- package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js +69 -0
- package/build/dist/Server/Utils/Rules/OwnerRuleAssignment.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleApplication.js +25 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleApplication.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunPermission.js +128 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunPermission.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js +746 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunRegistry.js.map +1 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunner.js +221 -0
- package/build/dist/Server/Utils/Rules/RuleRun/RuleRunner.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/ConnectorErrorMessage.js +3 -2
- package/build/dist/Server/Utils/SecurityEvent/ConnectorErrorMessage.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js +6 -4
- package/build/dist/Server/Utils/SecurityEvent/Connectors/ConnectorPlatformHealth.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/{GoogleSecOps → Connectors/GoogleSecOps}/GoogleSecOpsClient.js +174 -14
- package/build/dist/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsClient.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnector.js +1417 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/GoogleSecOps/GoogleSecOpsConnector.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js +138 -18
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionPoller.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js +1 -2
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionRunExecutor.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js +23 -18
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectionTester.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js +2 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/SecurityEventConnectorRegistry.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js +95 -0
- package/build/dist/Server/Utils/SecurityEvent/Connectors/Types.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +1 -1
- package/build/dist/Server/Utils/SecurityEvent/SecurityEventDedupe.js +3 -3
- package/build/dist/Server/Utils/SecurityEvent/ThreatIntel/ThreatIntelFeedPoller.js +2 -2
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js +118 -40
- package/build/dist/Server/Utils/SessionReplay/SessionReplayReadService.js.map +1 -1
- package/build/dist/Server/Utils/Slo/SloFeedUtil.js +150 -0
- package/build/dist/Server/Utils/Slo/SloFeedUtil.js.map +1 -0
- package/build/dist/Server/Utils/Slo/SloLegacyMonitorLabelAdoption.js +136 -0
- package/build/dist/Server/Utils/Slo/SloLegacyMonitorLabelAdoption.js.map +1 -0
- package/build/dist/Server/Utils/Slo/SloMetricUtil.js +269 -0
- package/build/dist/Server/Utils/Slo/SloMetricUtil.js.map +1 -0
- package/build/dist/Server/Utils/Slo/SloOwnerReferenceValidator.js +134 -0
- package/build/dist/Server/Utils/Slo/SloOwnerReferenceValidator.js.map +1 -0
- package/build/dist/Server/Utils/Slo/SloRecordReferenceValidator.js +137 -0
- package/build/dist/Server/Utils/Slo/SloRecordReferenceValidator.js.map +1 -0
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js +6 -26
- package/build/dist/Server/Utils/StatusPage/MonitorRulePatternValidator.js.map +1 -1
- package/build/dist/Server/Utils/StatusPageResource.js +31 -5
- package/build/dist/Server/Utils/StatusPageResource.js.map +1 -1
- package/build/dist/Types/Billing/SubscriptionStatus.js +37 -3
- package/build/dist/Types/Billing/SubscriptionStatus.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +1 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js +5 -0
- package/build/dist/Types/Dashboard/DashboardComponents/ComponentArgument.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardSloListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardSloListComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardTemplates.js +275 -286
- package/build/dist/Types/Dashboard/DashboardTemplates.js.map +1 -1
- package/build/dist/Types/Database/EnableAuditLog.js +19 -1
- package/build/dist/Types/Database/EnableAuditLog.js.map +1 -1
- package/build/dist/Types/Email/EmailTemplateType.js +4 -0
- package/build/dist/Types/Email/EmailTemplateType.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +45 -6
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +2 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepDnsMonitor.js +3 -1
- package/build/dist/Types/Monitor/MonitorStepDnsMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepDnssecMonitor.js +3 -1
- package/build/dist/Types/Monitor/MonitorStepDnssecMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js +3 -1
- package/build/dist/Types/Monitor/MonitorStepDomainMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepExternalStatusPageMonitor.js +3 -1
- package/build/dist/Types/Monitor/MonitorStepExternalStatusPageMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepRetries.js +47 -0
- package/build/dist/Types/Monitor/MonitorStepRetries.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorStepSnmpMonitor.js +3 -1
- package/build/dist/Types/Monitor/MonitorStepSnmpMonitor.js.map +1 -1
- package/build/dist/Types/NetworkDevice/NetworkDeviceDiagnosticResult.js +29 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceDiagnosticResult.js.map +1 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceDiagnosticStatus.js +37 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceDiagnosticStatus.js.map +1 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceDiagnosticType.js +42 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceDiagnosticType.js.map +1 -0
- package/build/dist/Types/NetworkDevice/NetworkDevicePingMetricNames.js +20 -0
- package/build/dist/Types/NetworkDevice/NetworkDevicePingMetricNames.js.map +1 -0
- package/build/dist/Types/Permission.js +114 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js +5 -0
- package/build/dist/Types/Rules/RuleCriteriaFieldRegistry.js.map +1 -1
- package/build/dist/Types/Rules/RuleRun.js +267 -0
- package/build/dist/Types/Rules/RuleRun.js.map +1 -0
- package/build/dist/Types/Rum/SessionReplayApi.js.map +1 -1
- package/build/dist/Types/SecurityEvent/Connectors/ConnectorDiagnostics.js.map +1 -1
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js +16 -1
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics.js.map +1 -1
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js +65 -0
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog.js.map +1 -1
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js +6 -2
- package/build/dist/Types/SecurityEvent/Connectors/SecurityEventConnectorProvider.js.map +1 -1
- package/build/dist/Types/ServiceLevelObjective/SloHistoryMetricName.js +26 -0
- package/build/dist/Types/ServiceLevelObjective/SloHistoryMetricName.js.map +1 -0
- package/build/dist/Types/ServiceLevelObjective/SloMetricType.js +45 -0
- package/build/dist/Types/ServiceLevelObjective/SloMetricType.js.map +1 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberNotificationEventType.js +4 -0
- package/build/dist/Types/StatusPage/StatusPageSubscriberNotificationEventType.js.map +1 -1
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js +295 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTemplateVariables.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTrigger.js +13 -0
- package/build/dist/Types/StatusPage/SubscriberNotificationTrigger.js.map +1 -0
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js +67 -0
- package/build/dist/Types/StatusPage/SubscriberUpdateNotification.js.map +1 -0
- package/build/dist/Types/Telemetry/LockedFilterDetail.js +7 -7
- package/build/dist/Types/Telemetry/ServiceType.js +7 -0
- package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
- package/build/dist/UI/Components/AlertBanner/AlertBanner.js +48 -19
- package/build/dist/UI/Components/AlertBanner/AlertBanner.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkArchiveActions.js +10 -2
- package/build/dist/UI/Components/BulkUpdate/BulkArchiveActions.js.map +1 -1
- package/build/dist/UI/Components/Detail/Detail.js +12 -3
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/Dropdown/Dropdown.js +33 -5
- package/build/dist/UI/Components/Dropdown/Dropdown.js.map +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyState.js +3 -3
- package/build/dist/UI/Components/EmptyState/EmptyState.js.map +1 -1
- package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js +47 -0
- package/build/dist/UI/Components/EmptyState/EmptyStateGuideLinks.js.map +1 -0
- package/build/dist/UI/Components/Forms/ModelForm.js +6 -5
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/LabelRule/LabelRuleTable.js +6 -2
- package/build/dist/UI/Components/LabelRule/LabelRuleTable.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +0 -2
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +3 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/RuleRun/RuleDetailFields.js +226 -0
- package/build/dist/UI/Components/RuleRun/RuleDetailFields.js.map +1 -0
- package/build/dist/UI/Components/RuleRun/RuleTable.js +97 -0
- package/build/dist/UI/Components/RuleRun/RuleTable.js.map +1 -0
- package/build/dist/UI/Components/RuleRun/RuleView.js +83 -0
- package/build/dist/UI/Components/RuleRun/RuleView.js.map +1 -0
- package/build/dist/UI/Components/RuleRun/RunRuleNowModal.js +74 -0
- package/build/dist/UI/Components/RuleRun/RunRuleNowModal.js.map +1 -0
- package/build/dist/UI/Components/RuleRun/RunRulesBulkAction.js +78 -0
- package/build/dist/UI/Components/RuleRun/RunRulesBulkAction.js.map +1 -0
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js +24 -50
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterChip.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js +0 -4
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Utils/Rules/RuleRunClient.js +98 -0
- package/build/dist/UI/Utils/Rules/RuleRunClient.js.map +1 -0
- package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js +8 -0
- package/build/dist/UI/Utils/Telemetry/TelemetryEntityNames.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardSloComponent.js +16 -2
- package/build/dist/Utils/Dashboard/Components/DashboardSloComponent.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardSloListComponent.js +94 -0
- package/build/dist/Utils/Dashboard/Components/DashboardSloListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +4 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/Dashboard/SloWidgetSource.js +125 -0
- package/build/dist/Utils/Dashboard/SloWidgetSource.js.map +1 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js +49 -0
- package/build/dist/Utils/Markdown/MarkdownEscape.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +112 -34
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +22 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js +129 -0
- package/build/dist/Utils/NetworkDiscovery/NetbiosNameUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/RunImportedDeviceNaming.js +255 -0
- package/build/dist/Utils/NetworkDiscovery/RunImportedDeviceNaming.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ShortDeviceNamePlanner.js +184 -0
- package/build/dist/Utils/NetworkDiscovery/ShortDeviceNamePlanner.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ShortHostnameUtil.js +79 -0
- package/build/dist/Utils/NetworkDiscovery/ShortHostnameUtil.js.map +1 -0
- package/build/dist/Utils/NetworkSite/CidrMatchUtil.js +21 -12
- package/build/dist/Utils/NetworkSite/CidrMatchUtil.js.map +1 -1
- package/build/dist/Utils/Project/DataResidency.js +51 -0
- package/build/dist/Utils/Project/DataResidency.js.map +1 -0
- package/build/dist/Utils/Rules/RuleRunSummary.js +197 -0
- package/build/dist/Utils/Rules/RuleRunSummary.js.map +1 -0
- package/build/dist/Utils/Slo/SloAffectedResourceMarkdown.js +51 -0
- package/build/dist/Utils/Slo/SloAffectedResourceMarkdown.js.map +1 -0
- package/build/dist/Utils/Slo/SloBurnRateTemplate.js +228 -0
- package/build/dist/Utils/Slo/SloBurnRateTemplate.js.map +1 -0
- package/build/dist/Utils/Slo/SloFeedMarkdown.js +934 -0
- package/build/dist/Utils/Slo/SloFeedMarkdown.js.map +1 -0
- package/build/dist/Utils/Slo/SloHealth.js +75 -9
- package/build/dist/Utils/Slo/SloHealth.js.map +1 -1
- package/build/dist/Utils/Slo/SloListWidgetFormat.js +217 -0
- package/build/dist/Utils/Slo/SloListWidgetFormat.js.map +1 -0
- package/build/dist/Utils/Slo/SloMetricType.js +212 -0
- package/build/dist/Utils/Slo/SloMetricType.js.map +1 -0
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js +216 -0
- package/build/dist/Utils/Slo/SloMonitorRuleCriteria.js.map +1 -0
- package/build/dist/Utils/Slo/SloMonitorStatusSummary.js +129 -0
- package/build/dist/Utils/Slo/SloMonitorStatusSummary.js.map +1 -0
- package/build/dist/Utils/Slo/SloOverviewText.js +110 -0
- package/build/dist/Utils/Slo/SloOverviewText.js.map +1 -0
- package/build/dist/Utils/Slo/SloProjection.js +448 -0
- package/build/dist/Utils/Slo/SloProjection.js.map +1 -0
- package/build/dist/Utils/Slo/SloWidgetFormat.js +4 -2
- package/build/dist/Utils/Slo/SloWidgetFormat.js.map +1 -1
- package/build/dist/Utils/Telemetry/LockedFilterSearch.js +1 -26
- package/build/dist/Utils/Telemetry/LockedFilterSearch.js.map +1 -1
- package/package.json +1 -1
- package/Models/DatabaseModels/GoogleSecOpsConnection.ts +0 -447
- package/Models/DatabaseModels/GoogleSecOpsConnectionRun.ts +0 -263
- package/Server/API/GoogleSecOpsConnectionAPI.ts +0 -320
- package/Server/Services/GoogleSecOpsConnectionRunService.ts +0 -10
- package/Server/Services/GoogleSecOpsConnectionService.ts +0 -116
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.ts +0 -777
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +0 -1373
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.ts +0 -512
- package/Tests/App/Dashboard/GoogleSecOpsConnectionForm.test.tsx +0 -838
- package/Tests/App/Dashboard/GoogleSecOpsConnectionsErrors.test.tsx +0 -441
- package/Tests/App/Dashboard/GoogleSecOpsDiagnostics.test.tsx +0 -1001
- package/Tests/Server/API/GoogleSecOpsConnectionAPI.test.ts +0 -657
- package/Tests/Server/Services/GoogleSecOpsDiagnosticsModel.test.ts +0 -153
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.test.ts +0 -784
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.test.ts +0 -823
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsDiagnostics.test.ts +0 -918
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +0 -738
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerFailureTaxonomy.test.ts +0 -874
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPollerHardening.test.ts +0 -1297
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsThreePassPoller.test.ts +0 -1577
- package/Tests/UI/Components/LockedFilterActions.test.tsx +0 -396
- package/Types/SecurityEvent/GoogleSecOpsDiagnostics.ts +0 -111
- package/UI/Components/TelemetryViewer/components/LockedFilterActions.tsx +0 -205
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js +0 -478
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js.map +0 -1
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnectionRun.js +0 -287
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnectionRun.js.map +0 -1
- package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js +0 -231
- package/build/dist/Server/API/GoogleSecOpsConnectionAPI.js.map +0 -1
- package/build/dist/Server/Services/GoogleSecOpsConnectionRunService.js +0 -9
- package/build/dist/Server/Services/GoogleSecOpsConnectionRunService.js.map +0 -1
- package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +0 -70
- package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +0 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +0 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js +0 -537
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsConnectionTester.js.map +0 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +0 -1034
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +0 -1
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js +0 -410
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsRunExecutor.js.map +0 -1
- package/build/dist/Types/SecurityEvent/GoogleSecOpsDiagnostics.js +0 -2
- package/build/dist/Types/SecurityEvent/GoogleSecOpsDiagnostics.js.map +0 -1
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js +0 -88
- package/build/dist/UI/Components/TelemetryViewer/components/LockedFilterActions.js.map +0 -1
|
@@ -0,0 +1,1764 @@
|
|
|
1
|
+
import "@testing-library/jest-dom";
|
|
2
|
+
import {
|
|
3
|
+
afterEach,
|
|
4
|
+
beforeEach,
|
|
5
|
+
describe,
|
|
6
|
+
expect,
|
|
7
|
+
jest,
|
|
8
|
+
test,
|
|
9
|
+
} from "@jest/globals";
|
|
10
|
+
import {
|
|
11
|
+
act,
|
|
12
|
+
cleanup,
|
|
13
|
+
fireEvent,
|
|
14
|
+
render,
|
|
15
|
+
screen,
|
|
16
|
+
waitFor,
|
|
17
|
+
within,
|
|
18
|
+
} from "@testing-library/react";
|
|
19
|
+
import React from "react";
|
|
20
|
+
import { MemoryRouter } from "react-router-dom";
|
|
21
|
+
import SecurityEventConnectionDiagnostics from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventConnectionDiagnostics";
|
|
22
|
+
import SecurityEventConnectionRunDetails from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventConnectionRunDetails";
|
|
23
|
+
import {
|
|
24
|
+
ConnectorProviderDetailGroup,
|
|
25
|
+
connectionEventAttributeKey,
|
|
26
|
+
connectionEventsRoute,
|
|
27
|
+
connectorHealth,
|
|
28
|
+
connectorProviderDetailGroups,
|
|
29
|
+
connectorProviderTitle,
|
|
30
|
+
connectorSampleEventTime,
|
|
31
|
+
connectorSampleTitle,
|
|
32
|
+
connectorScopeImportNoun,
|
|
33
|
+
connectorScopeLabel,
|
|
34
|
+
connectorScopeSummary,
|
|
35
|
+
normalizeConnectorCheckStatus,
|
|
36
|
+
readConnectorProviderDetails,
|
|
37
|
+
validateConnectionRange,
|
|
38
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Components/SecurityEvents/SecurityEventConnectionDiagnosticsUtil";
|
|
39
|
+
import { RouteUtil } from "../../../../App/FeatureSet/Dashboard/src/Utils/RouteMap";
|
|
40
|
+
import SecurityEventConnection from "../../../Models/DatabaseModels/SecurityEventConnection";
|
|
41
|
+
import SecurityEventConnectionRun from "../../../Models/DatabaseModels/SecurityEventConnectionRun";
|
|
42
|
+
import HTTPErrorResponse from "../../../Types/API/HTTPErrorResponse";
|
|
43
|
+
import HTTPResponse from "../../../Types/API/HTTPResponse";
|
|
44
|
+
import Route from "../../../Types/API/Route";
|
|
45
|
+
import InBetween from "../../../Types/BaseDatabase/InBetween";
|
|
46
|
+
import ListResult from "../../../Types/BaseDatabase/ListResult";
|
|
47
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
48
|
+
import JSONFunctions from "../../../Types/JSONFunctions";
|
|
49
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
50
|
+
import {
|
|
51
|
+
SecurityConnectorSample,
|
|
52
|
+
SecurityConnectorTestReport,
|
|
53
|
+
} from "../../../Types/SecurityEvent/Connectors/ConnectorDiagnostics";
|
|
54
|
+
import {
|
|
55
|
+
LEGACY_GOOGLE_SECOPS_CONNECTION_ID_ATTRIBUTE,
|
|
56
|
+
SECURITY_CONNECTION_ID_ATTRIBUTE,
|
|
57
|
+
SecurityEventConnectionRunResult,
|
|
58
|
+
SecurityEventConnectionRunStatus,
|
|
59
|
+
SecurityEventConnectionRunType,
|
|
60
|
+
} from "../../../Types/SecurityEvent/Connectors/SecurityEventConnectionDiagnostics";
|
|
61
|
+
import {
|
|
62
|
+
SecurityEventConnectorDefinition,
|
|
63
|
+
getSecurityEventConnectorDefinition,
|
|
64
|
+
} from "../../../Types/SecurityEvent/Connectors/SecurityEventConnectorCatalog";
|
|
65
|
+
import SecurityEventConnectorProvider from "../../../Types/SecurityEvent/Connectors/SecurityEventConnectorProvider";
|
|
66
|
+
import API from "../../../UI/Utils/API/API";
|
|
67
|
+
import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
|
|
68
|
+
|
|
69
|
+
/*
|
|
70
|
+
* The diagnostics modal, run details and their util, driven by a Google
|
|
71
|
+
* SecOps connection. Most cases are ported from the retired
|
|
72
|
+
* GoogleSecOpsDiagnostics suite, which pinned the same behaviour on the
|
|
73
|
+
* Google-only copies of these components; the rest pin what the merge
|
|
74
|
+
* added: catalog-driven wording (Data to import, Preview detections), the
|
|
75
|
+
* provider diagnostics and alert flag a SecOps run reports, and run history
|
|
76
|
+
* carried over from the retired connector (no provider, legacy sample and
|
|
77
|
+
* check shapes, and events stamped with the legacy connection attribute).
|
|
78
|
+
*/
|
|
79
|
+
|
|
80
|
+
const GOOGLE: string = SecurityEventConnectorProvider.GoogleSecOps;
|
|
81
|
+
const OKTA: string = SecurityEventConnectorProvider.OktaSystemLog;
|
|
82
|
+
const CONNECTION_ID: string = "11111111-1111-4111-8111-111111111111";
|
|
83
|
+
const PROJECT_ID: string = "22222222-2222-4222-8222-222222222222";
|
|
84
|
+
const RUN_ID: string = "33333333-3333-4333-8333-333333333333";
|
|
85
|
+
const NOW: number = new Date("2026-09-10T12:00:00Z").getTime();
|
|
86
|
+
|
|
87
|
+
type SecurityEventConnectionOverrides = {
|
|
88
|
+
[Key in keyof SecurityEventConnection]?:
|
|
89
|
+
| SecurityEventConnection[Key]
|
|
90
|
+
| undefined;
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
function connection(
|
|
94
|
+
overrides: SecurityEventConnectionOverrides = {},
|
|
95
|
+
): SecurityEventConnection {
|
|
96
|
+
const value: SecurityEventConnection = new SecurityEventConnection();
|
|
97
|
+
value._id = CONNECTION_ID;
|
|
98
|
+
value.projectId = new ObjectID(PROJECT_ID);
|
|
99
|
+
value.name = "Customer SecOps";
|
|
100
|
+
value.provider = SecurityEventConnectorProvider.GoogleSecOps;
|
|
101
|
+
value.config = {
|
|
102
|
+
region: "europe",
|
|
103
|
+
instanceResourceName: "projects/acme/locations/europe/instances/i",
|
|
104
|
+
};
|
|
105
|
+
value.isEnabled = true;
|
|
106
|
+
value.pollIntervalInMinutes = 5;
|
|
107
|
+
value.alertingOnly = true;
|
|
108
|
+
value.createdAt = new Date(NOW - 60_000);
|
|
109
|
+
Object.assign(value, overrides);
|
|
110
|
+
return value;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function sample(
|
|
114
|
+
overrides: Partial<SecurityConnectorSample> = {},
|
|
115
|
+
): SecurityConnectorSample {
|
|
116
|
+
return {
|
|
117
|
+
id: "detection-1",
|
|
118
|
+
title: "Successful brute force",
|
|
119
|
+
severity: "High",
|
|
120
|
+
eventTime: "2026-09-09T01:30:30Z",
|
|
121
|
+
createdTime: "2026-09-10T04:16:58.612Z",
|
|
122
|
+
isAlert: true,
|
|
123
|
+
...overrides,
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function result(
|
|
128
|
+
overrides: Partial<SecurityEventConnectionRunResult> = {},
|
|
129
|
+
): SecurityEventConnectionRunResult {
|
|
130
|
+
return {
|
|
131
|
+
type: "poll",
|
|
132
|
+
provider: GOOGLE,
|
|
133
|
+
status: "success",
|
|
134
|
+
startedAt: "2026-09-10T11:59:00Z",
|
|
135
|
+
completedAt: "2026-09-10T12:00:00Z",
|
|
136
|
+
durationMs: 60000,
|
|
137
|
+
windowStart: "2026-09-10T04:00:00Z",
|
|
138
|
+
windowEnd: "2026-09-10T05:00:00Z",
|
|
139
|
+
fetchedCount: 1,
|
|
140
|
+
ingestedCount: 1,
|
|
141
|
+
duplicateCount: 0,
|
|
142
|
+
rejectedCount: 0,
|
|
143
|
+
failedCount: 0,
|
|
144
|
+
complete: true,
|
|
145
|
+
requestCount: 1,
|
|
146
|
+
warnings: [],
|
|
147
|
+
samples: [sample()],
|
|
148
|
+
checks: [
|
|
149
|
+
{
|
|
150
|
+
key: "read-rule-detections",
|
|
151
|
+
name: "Read rule detections by created time",
|
|
152
|
+
status: "pass",
|
|
153
|
+
durationMs: 500,
|
|
154
|
+
message: "Detection read access verified.",
|
|
155
|
+
},
|
|
156
|
+
],
|
|
157
|
+
eventTimeStart: "2026-09-09T01:30:30Z",
|
|
158
|
+
eventTimeEnd: "2026-09-09T01:30:30Z",
|
|
159
|
+
...overrides,
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
// What a synchronous Test connection stores as its run's result.
|
|
164
|
+
function testReport(
|
|
165
|
+
overrides: Partial<SecurityConnectorTestReport> = {},
|
|
166
|
+
): SecurityConnectorTestReport {
|
|
167
|
+
return {
|
|
168
|
+
provider: GOOGLE,
|
|
169
|
+
status: "pass",
|
|
170
|
+
startedAt: "2026-09-10T11:59:58.000Z",
|
|
171
|
+
completedAt: "2026-09-10T12:00:00.000Z",
|
|
172
|
+
durationMs: 2000,
|
|
173
|
+
checks: [
|
|
174
|
+
{
|
|
175
|
+
key: "authentication",
|
|
176
|
+
name: "Authenticate with Google",
|
|
177
|
+
status: "pass",
|
|
178
|
+
durationMs: 300,
|
|
179
|
+
message: "Google accepted the service account credentials.",
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
key: "worker-consumers",
|
|
183
|
+
name: "Background workers",
|
|
184
|
+
status: "pass",
|
|
185
|
+
durationMs: 10,
|
|
186
|
+
message: "2 worker processes are consuming the queue.",
|
|
187
|
+
},
|
|
188
|
+
],
|
|
189
|
+
summary: "Google SecOps is reachable, credentials are accepted.",
|
|
190
|
+
...overrides,
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function testRequestUrls(): Array<string> {
|
|
195
|
+
return jest
|
|
196
|
+
.mocked(API.post)
|
|
197
|
+
.mock.calls.map((call: Array<unknown>): string => {
|
|
198
|
+
return String((call[0] as { url: unknown }).url);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function run(
|
|
203
|
+
status: SecurityEventConnectionRunStatus = "success",
|
|
204
|
+
type: SecurityEventConnectionRunType = "poll",
|
|
205
|
+
value?: SecurityEventConnectionRunResult | JSONObject,
|
|
206
|
+
): SecurityEventConnectionRun {
|
|
207
|
+
const model: SecurityEventConnectionRun = new SecurityEventConnectionRun();
|
|
208
|
+
model._id = RUN_ID;
|
|
209
|
+
model.securityEventConnectionId = new ObjectID(CONNECTION_ID);
|
|
210
|
+
model.type = type;
|
|
211
|
+
model.status = status;
|
|
212
|
+
model.createdAt = new Date(NOW - 60_000);
|
|
213
|
+
model.requestedByUserId = new ObjectID(PROJECT_ID);
|
|
214
|
+
if (status !== "queued" && status !== "running") {
|
|
215
|
+
model.startedAt = new Date(NOW - 60_000);
|
|
216
|
+
model.completedAt = new Date(NOW);
|
|
217
|
+
model.result = (value || result({ type, status })) as unknown as JSONObject;
|
|
218
|
+
}
|
|
219
|
+
return model;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function history(
|
|
223
|
+
data: Array<SecurityEventConnectionRun>,
|
|
224
|
+
): ListResult<SecurityEventConnectionRun> {
|
|
225
|
+
return { data, count: data.length, skip: 0, limit: 20 };
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function renderDiagnostics(
|
|
229
|
+
data: {
|
|
230
|
+
canRun?: boolean;
|
|
231
|
+
initialAction?: "test" | "poll";
|
|
232
|
+
item?: SecurityEventConnection;
|
|
233
|
+
} = {},
|
|
234
|
+
): ReturnType<typeof render> {
|
|
235
|
+
return render(
|
|
236
|
+
<MemoryRouter>
|
|
237
|
+
<SecurityEventConnectionDiagnostics
|
|
238
|
+
connection={data.item || connection()}
|
|
239
|
+
canRun={data.canRun ?? true}
|
|
240
|
+
initialAction={data.initialAction}
|
|
241
|
+
onClose={(): void => {}}
|
|
242
|
+
onUpdated={(): void => {}}
|
|
243
|
+
/>
|
|
244
|
+
</MemoryRouter>,
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function renderRun(
|
|
249
|
+
value: SecurityEventConnectionRun,
|
|
250
|
+
provider?: string,
|
|
251
|
+
): HTMLElement {
|
|
252
|
+
render(
|
|
253
|
+
<MemoryRouter>
|
|
254
|
+
<SecurityEventConnectionRunDetails run={value} provider={provider} />
|
|
255
|
+
</MemoryRouter>,
|
|
256
|
+
);
|
|
257
|
+
return screen.getByRole("region", { name: "Run details" });
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
function scheduledPolling(): HTMLElement {
|
|
261
|
+
return screen.getByRole("region", { name: "Scheduled polling" });
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function eventsFilter(route: Route): JSONObject {
|
|
265
|
+
const text: string = route.toString();
|
|
266
|
+
const params: URLSearchParams = new URLSearchParams(
|
|
267
|
+
text.slice(text.indexOf("?")),
|
|
268
|
+
);
|
|
269
|
+
return JSONFunctions.deserialize(
|
|
270
|
+
JSON.parse(params.get("security-events-table-filter")!) as JSONObject,
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function filterRange(filter: JSONObject): [string, string] {
|
|
275
|
+
const range: InBetween<Date> = filter["time"] as InBetween<Date>;
|
|
276
|
+
expect(range).toBeInstanceOf(InBetween);
|
|
277
|
+
return [
|
|
278
|
+
new Date(range.startValue).toISOString(),
|
|
279
|
+
new Date(range.endValue).toISOString(),
|
|
280
|
+
];
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
describe("SecurityEventConnectionDiagnostics for a Google SecOps connection", () => {
|
|
284
|
+
beforeEach((): void => {
|
|
285
|
+
jest.useFakeTimers();
|
|
286
|
+
jest.setSystemTime(NOW);
|
|
287
|
+
jest.spyOn(ModelAPI, "getList").mockResolvedValue(history([]));
|
|
288
|
+
jest.spyOn(ModelAPI, "getItem").mockResolvedValue(connection());
|
|
289
|
+
jest
|
|
290
|
+
.spyOn(ModelAPI, "getCommonHeaders")
|
|
291
|
+
.mockReturnValue({ "project-id": PROJECT_ID });
|
|
292
|
+
jest
|
|
293
|
+
.spyOn(API, "post")
|
|
294
|
+
.mockResolvedValue(new HTTPResponse(200, { runId: RUN_ID }, {}));
|
|
295
|
+
jest
|
|
296
|
+
.spyOn(RouteUtil, "populateRouteParams")
|
|
297
|
+
.mockImplementation((): Route => {
|
|
298
|
+
/*
|
|
299
|
+
* A fresh Route per call: addQueryParams mutates the Route it is
|
|
300
|
+
* given, so a shared instance would carry one link's query into the
|
|
301
|
+
* next.
|
|
302
|
+
*/
|
|
303
|
+
return new Route(`/dashboard/${PROJECT_ID}/security-events`);
|
|
304
|
+
});
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
afterEach((): void => {
|
|
308
|
+
cleanup();
|
|
309
|
+
jest.restoreAllMocks();
|
|
310
|
+
jest.useRealTimers();
|
|
311
|
+
});
|
|
312
|
+
|
|
313
|
+
test("loads tenant-scoped history and never requests credentials", async (): Promise<void> => {
|
|
314
|
+
renderDiagnostics();
|
|
315
|
+
expect(screen.getByText("Loading run history…")).toBeInTheDocument();
|
|
316
|
+
expect(await screen.findByText(/No runs recorded yet/)).toBeInTheDocument();
|
|
317
|
+
expect(ModelAPI.getList).toHaveBeenCalledWith(
|
|
318
|
+
expect.objectContaining({
|
|
319
|
+
modelType: SecurityEventConnectionRun,
|
|
320
|
+
query: {
|
|
321
|
+
projectId: new ObjectID(PROJECT_ID),
|
|
322
|
+
securityEventConnectionId: new ObjectID(CONNECTION_ID),
|
|
323
|
+
},
|
|
324
|
+
limit: 20,
|
|
325
|
+
skip: 0,
|
|
326
|
+
}),
|
|
327
|
+
);
|
|
328
|
+
expect(ModelAPI.getItem).toHaveBeenCalledWith(
|
|
329
|
+
expect.objectContaining({ id: new ObjectID(CONNECTION_ID) }),
|
|
330
|
+
);
|
|
331
|
+
expect(
|
|
332
|
+
jest.mocked(ModelAPI.getItem).mock.calls[0]?.[0].select,
|
|
333
|
+
).not.toHaveProperty("secrets");
|
|
334
|
+
expect(
|
|
335
|
+
jest.mocked(ModelAPI.getItem).mock.calls[0]?.[0].select,
|
|
336
|
+
).toHaveProperty("alertingOnly", true);
|
|
337
|
+
expect(API.post).not.toHaveBeenCalled();
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
/*
|
|
341
|
+
* google-diagnostics-test-still-queued: Test connection used to POST
|
|
342
|
+
* /:id/run and wait for a worker. It calls the synchronous endpoint and
|
|
343
|
+
* renders the checklist inline.
|
|
344
|
+
*/
|
|
345
|
+
test("runs the synchronous connection test and renders its checklist inline", async (): Promise<void> => {
|
|
346
|
+
jest
|
|
347
|
+
.mocked(API.post)
|
|
348
|
+
.mockResolvedValue(
|
|
349
|
+
new HTTPResponse(200, testReport() as unknown as JSONObject, {}),
|
|
350
|
+
);
|
|
351
|
+
renderDiagnostics();
|
|
352
|
+
await screen.findByText(/No runs recorded yet/);
|
|
353
|
+
fireEvent.click(screen.getByRole("button", { name: "Test connection" }));
|
|
354
|
+
const report: HTMLElement = await screen.findByRole("region", {
|
|
355
|
+
name: "Connection test report",
|
|
356
|
+
});
|
|
357
|
+
expect(within(report).getByText("All checks passed")).toBeVisible();
|
|
358
|
+
expect(within(report).getByText("Background workers")).toBeVisible();
|
|
359
|
+
expect(report).toHaveTextContent("Access to Google SecOps");
|
|
360
|
+
expect(API.post).toHaveBeenCalledTimes(1);
|
|
361
|
+
expect(API.post).toHaveBeenCalledWith(
|
|
362
|
+
expect.objectContaining({
|
|
363
|
+
data: { connectionId: CONNECTION_ID },
|
|
364
|
+
headers: { "project-id": PROJECT_ID },
|
|
365
|
+
}),
|
|
366
|
+
);
|
|
367
|
+
expect(testRequestUrls()[0]).toContain("/security-event-connection/test");
|
|
368
|
+
expect(testRequestUrls()[0]).not.toContain("/run");
|
|
369
|
+
expect(testRequestUrls()[0]).not.toContain("google-secops-connection");
|
|
370
|
+
expect(
|
|
371
|
+
screen.getByText(/It runs immediately and imports nothing/),
|
|
372
|
+
).toBeVisible();
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
test("Test connection stays available while a queued run waits for a worker", async (): Promise<void> => {
|
|
376
|
+
jest.mocked(ModelAPI.getList).mockResolvedValue(history([run("queued")]));
|
|
377
|
+
jest.mocked(API.post).mockResolvedValue(
|
|
378
|
+
new HTTPResponse(
|
|
379
|
+
200,
|
|
380
|
+
testReport({
|
|
381
|
+
status: "fail",
|
|
382
|
+
summary: "1 check failed: Background workers.",
|
|
383
|
+
}) as unknown as JSONObject,
|
|
384
|
+
{},
|
|
385
|
+
),
|
|
386
|
+
);
|
|
387
|
+
renderDiagnostics();
|
|
388
|
+
expect(
|
|
389
|
+
await screen.findByText(/Queued — waiting for a worker/),
|
|
390
|
+
).toBeVisible();
|
|
391
|
+
expect(screen.getByRole("button", { name: "Run now" })).toBeDisabled();
|
|
392
|
+
const testButton: HTMLElement = screen.getByRole("button", {
|
|
393
|
+
name: "Test connection",
|
|
394
|
+
});
|
|
395
|
+
expect(testButton).toBeEnabled();
|
|
396
|
+
fireEvent.click(testButton);
|
|
397
|
+
expect(await screen.findByText("Some checks failed")).toBeVisible();
|
|
398
|
+
expect(testRequestUrls()).toEqual([
|
|
399
|
+
expect.stringContaining("/security-event-connection/test"),
|
|
400
|
+
]);
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
test("a failed re-run clears the previous report instead of showing it beside the error", async (): Promise<void> => {
|
|
404
|
+
jest
|
|
405
|
+
.mocked(API.post)
|
|
406
|
+
.mockResolvedValueOnce(
|
|
407
|
+
new HTTPResponse(200, testReport() as unknown as JSONObject, {}),
|
|
408
|
+
)
|
|
409
|
+
.mockResolvedValueOnce(
|
|
410
|
+
new HTTPErrorResponse(
|
|
411
|
+
500,
|
|
412
|
+
{ message: "Test endpoint unavailable." },
|
|
413
|
+
{},
|
|
414
|
+
),
|
|
415
|
+
);
|
|
416
|
+
renderDiagnostics();
|
|
417
|
+
await screen.findByText(/No runs recorded yet/);
|
|
418
|
+
fireEvent.click(screen.getByRole("button", { name: "Test connection" }));
|
|
419
|
+
expect(await screen.findByText("All checks passed")).toBeVisible();
|
|
420
|
+
fireEvent.click(screen.getByRole("button", { name: "Test connection" }));
|
|
421
|
+
expect(await screen.findByText(/Test endpoint unavailable/)).toBeVisible();
|
|
422
|
+
expect(screen.queryByText("All checks passed")).not.toBeInTheDocument();
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
test("an initial test action runs the synchronous test once", async (): Promise<void> => {
|
|
426
|
+
jest
|
|
427
|
+
.mocked(API.post)
|
|
428
|
+
.mockResolvedValue(
|
|
429
|
+
new HTTPResponse(200, testReport() as unknown as JSONObject, {}),
|
|
430
|
+
);
|
|
431
|
+
renderDiagnostics({ initialAction: "test" });
|
|
432
|
+
expect(await screen.findByText("All checks passed")).toBeVisible();
|
|
433
|
+
await act(async (): Promise<void> => {
|
|
434
|
+
jest.advanceTimersByTime(15000);
|
|
435
|
+
});
|
|
436
|
+
expect(testRequestUrls()).toEqual([
|
|
437
|
+
expect.stringContaining("/security-event-connection/test"),
|
|
438
|
+
]);
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
test("the copy speaks in detections and the Data to import control", async (): Promise<void> => {
|
|
442
|
+
renderDiagnostics();
|
|
443
|
+
await screen.findByText(/No runs recorded yet/);
|
|
444
|
+
|
|
445
|
+
const scheduled: HTMLElement = scheduledPolling();
|
|
446
|
+
const scopeLabel: HTMLElement =
|
|
447
|
+
within(scheduled).getByText("Data to import");
|
|
448
|
+
expect(scopeLabel.tagName).toBe("DT");
|
|
449
|
+
expect(scopeLabel.parentElement).toHaveTextContent(
|
|
450
|
+
"Data to importAlerts only",
|
|
451
|
+
);
|
|
452
|
+
expect(within(scheduled).queryByText("Scope")).not.toBeInTheDocument();
|
|
453
|
+
expect(
|
|
454
|
+
screen.getByText(
|
|
455
|
+
/To import detections too, select Detections under Data to import using Edit connection\./,
|
|
456
|
+
),
|
|
457
|
+
).toBeVisible();
|
|
458
|
+
expect(
|
|
459
|
+
screen.queryByText(/Change settings using Edit connection/),
|
|
460
|
+
).not.toBeInTheDocument();
|
|
461
|
+
expect(
|
|
462
|
+
screen.getByText(
|
|
463
|
+
/what it has available to import with and without Detections, and whether/,
|
|
464
|
+
),
|
|
465
|
+
).toBeVisible();
|
|
466
|
+
expect(
|
|
467
|
+
screen.getByRole("heading", { name: "Find historical detections" }),
|
|
468
|
+
).toBeVisible();
|
|
469
|
+
expect(
|
|
470
|
+
screen.getByText(
|
|
471
|
+
/When Last Error says polling moved past a minute it could not read in full, import that minute here to recover what one run can read\./,
|
|
472
|
+
),
|
|
473
|
+
).toBeVisible();
|
|
474
|
+
expect(
|
|
475
|
+
screen.getByRole("button", { name: "Preview detections" }),
|
|
476
|
+
).toBeVisible();
|
|
477
|
+
expect(
|
|
478
|
+
screen.queryByRole("button", { name: "Preview records" }),
|
|
479
|
+
).not.toBeInTheDocument();
|
|
480
|
+
expect(screen.queryByText(/15 minutes/)).not.toBeInTheDocument();
|
|
481
|
+
});
|
|
482
|
+
|
|
483
|
+
test("a connection importing Detections too reads as Alerts and detections", async (): Promise<void> => {
|
|
484
|
+
const allDetections: SecurityEventConnection = connection({
|
|
485
|
+
alertingOnly: false,
|
|
486
|
+
});
|
|
487
|
+
jest.mocked(ModelAPI.getItem).mockResolvedValue(allDetections);
|
|
488
|
+
renderDiagnostics({ item: allDetections });
|
|
489
|
+
await screen.findByText(/No runs recorded yet/);
|
|
490
|
+
|
|
491
|
+
expect(
|
|
492
|
+
within(scheduledPolling()).getByText("Data to import").parentElement,
|
|
493
|
+
).toHaveTextContent("Data to importAlerts and detections");
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
test("a provider without an alerting distinction keeps the generic wording in its own record name", async (): Promise<void> => {
|
|
497
|
+
const okta: SecurityEventConnection = connection({
|
|
498
|
+
name: "Acme Okta",
|
|
499
|
+
provider: SecurityEventConnectorProvider.OktaSystemLog,
|
|
500
|
+
config: { orgUrl: "https://acme.okta.com" },
|
|
501
|
+
});
|
|
502
|
+
jest.mocked(ModelAPI.getItem).mockResolvedValue(okta);
|
|
503
|
+
renderDiagnostics({ item: okta });
|
|
504
|
+
await screen.findByText(/No runs recorded yet/);
|
|
505
|
+
|
|
506
|
+
expect(
|
|
507
|
+
screen.getByText(/Change settings using Edit connection\./),
|
|
508
|
+
).toBeVisible();
|
|
509
|
+
expect(
|
|
510
|
+
within(scheduledPolling()).queryByText("Data to import"),
|
|
511
|
+
).not.toBeInTheDocument();
|
|
512
|
+
expect(
|
|
513
|
+
within(scheduledPolling()).queryByText("Scope"),
|
|
514
|
+
).not.toBeInTheDocument();
|
|
515
|
+
expect(screen.queryByText(/with and without/)).not.toBeInTheDocument();
|
|
516
|
+
expect(
|
|
517
|
+
screen.getByRole("button", { name: "Preview log events" }),
|
|
518
|
+
).toBeVisible();
|
|
519
|
+
// The forced-advance recovery path is not provider-specific.
|
|
520
|
+
expect(screen.getByText(/import that minute here/)).toBeVisible();
|
|
521
|
+
|
|
522
|
+
fireEvent.click(
|
|
523
|
+
screen.getByRole("button", { name: "Import this time range" }),
|
|
524
|
+
);
|
|
525
|
+
expect(
|
|
526
|
+
screen.getByRole("group", { name: "Confirm historical import" }),
|
|
527
|
+
).toHaveTextContent("Import log events from");
|
|
528
|
+
});
|
|
529
|
+
|
|
530
|
+
test("Run now is available for paused schedules and sends a poll request", async (): Promise<void> => {
|
|
531
|
+
const paused: SecurityEventConnection = connection({ isEnabled: false });
|
|
532
|
+
jest.mocked(ModelAPI.getItem).mockResolvedValue(paused);
|
|
533
|
+
renderDiagnostics({ item: paused });
|
|
534
|
+
await screen.findByText(/No runs recorded yet/);
|
|
535
|
+
expect(screen.getByText(/Schedule paused/)).toBeVisible();
|
|
536
|
+
fireEvent.click(screen.getByRole("button", { name: "Run now" }));
|
|
537
|
+
await waitFor((): void => {
|
|
538
|
+
expect(API.post).toHaveBeenCalledWith(
|
|
539
|
+
expect.objectContaining({ data: { type: "poll" } }),
|
|
540
|
+
);
|
|
541
|
+
});
|
|
542
|
+
expect(testRequestUrls()[0]).toContain(
|
|
543
|
+
`/security-event-connection/${CONNECTION_ID}/run`,
|
|
544
|
+
);
|
|
545
|
+
});
|
|
546
|
+
|
|
547
|
+
test("a direct row action is submitted only once even with refreshes", async (): Promise<void> => {
|
|
548
|
+
jest
|
|
549
|
+
.mocked(ModelAPI.getList)
|
|
550
|
+
.mockResolvedValue(history([run("queued", "poll")]));
|
|
551
|
+
renderDiagnostics({ initialAction: "poll" });
|
|
552
|
+
expect(
|
|
553
|
+
await screen.findByText(/Queued — waiting for a worker/),
|
|
554
|
+
).toBeVisible();
|
|
555
|
+
await act(async (): Promise<void> => {
|
|
556
|
+
jest.advanceTimersByTime(3000);
|
|
557
|
+
});
|
|
558
|
+
expect(API.post).toHaveBeenCalledTimes(1);
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
test("queued and running runs disable duplicate actions and update to completed results", async (): Promise<void> => {
|
|
562
|
+
jest.mocked(ModelAPI.getList).mockResolvedValue(history([run("queued")]));
|
|
563
|
+
renderDiagnostics();
|
|
564
|
+
expect(
|
|
565
|
+
await screen.findByText(/Queued — waiting for a worker/),
|
|
566
|
+
).toBeVisible();
|
|
567
|
+
expect(screen.getByRole("button", { name: "Run now" })).toBeDisabled();
|
|
568
|
+
expect(
|
|
569
|
+
screen.getByRole("button", { name: "Preview detections" }),
|
|
570
|
+
).toBeDisabled();
|
|
571
|
+
jest.mocked(ModelAPI.getList).mockResolvedValue(history([run("running")]));
|
|
572
|
+
await act(async (): Promise<void> => {
|
|
573
|
+
jest.advanceTimersByTime(3000);
|
|
574
|
+
});
|
|
575
|
+
// A running run has no result yet; the connection names the provider.
|
|
576
|
+
expect(
|
|
577
|
+
await screen.findByText(/Running — checking Google SecOps/),
|
|
578
|
+
).toBeVisible();
|
|
579
|
+
jest.mocked(ModelAPI.getList).mockResolvedValue(history([run()]));
|
|
580
|
+
await act(async (): Promise<void> => {
|
|
581
|
+
jest.advanceTimersByTime(3000);
|
|
582
|
+
});
|
|
583
|
+
expect(await screen.findByText("Result: Success")).toBeVisible();
|
|
584
|
+
expect(screen.getByRole("button", { name: "Run now" })).toBeEnabled();
|
|
585
|
+
expect(screen.getByText("Successful brute force")).toBeVisible();
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
test("read-only users can inspect results but cannot start any operation", async (): Promise<void> => {
|
|
589
|
+
jest.mocked(ModelAPI.getList).mockResolvedValue(history([run()]));
|
|
590
|
+
renderDiagnostics({ canRun: false });
|
|
591
|
+
await screen.findByText("Result: Success");
|
|
592
|
+
for (const title of [
|
|
593
|
+
"Test connection",
|
|
594
|
+
"Run now",
|
|
595
|
+
"Preview detections",
|
|
596
|
+
"Import this time range",
|
|
597
|
+
]) {
|
|
598
|
+
expect(screen.getByRole("button", { name: title })).toBeDisabled();
|
|
599
|
+
}
|
|
600
|
+
fireEvent.click(screen.getByRole("button", { name: "View run" }));
|
|
601
|
+
expect(screen.getByRole("region", { name: "Run details" })).toBeVisible();
|
|
602
|
+
expect(API.post).not.toHaveBeenCalled();
|
|
603
|
+
});
|
|
604
|
+
|
|
605
|
+
test("a selected run leaving the recent history does not disable actions", async (): Promise<void> => {
|
|
606
|
+
jest.mocked(ModelAPI.getList).mockResolvedValue(history([run()]));
|
|
607
|
+
renderDiagnostics();
|
|
608
|
+
await screen.findByText("Successful brute force");
|
|
609
|
+
const latest: SecurityEventConnectionRun = run(
|
|
610
|
+
"empty",
|
|
611
|
+
"preview",
|
|
612
|
+
result({
|
|
613
|
+
type: "preview",
|
|
614
|
+
status: "empty",
|
|
615
|
+
fetchedCount: 0,
|
|
616
|
+
ingestedCount: 0,
|
|
617
|
+
samples: [],
|
|
618
|
+
}),
|
|
619
|
+
);
|
|
620
|
+
latest._id = "44444444-4444-4444-8444-444444444444";
|
|
621
|
+
jest.mocked(ModelAPI.getList).mockResolvedValue(history([latest]));
|
|
622
|
+
fireEvent.click(screen.getByRole("button", { name: "Refresh history" }));
|
|
623
|
+
expect(
|
|
624
|
+
await screen.findByText("Result: No records returned"),
|
|
625
|
+
).toBeVisible();
|
|
626
|
+
expect(screen.getByRole("button", { name: "Run now" })).toBeEnabled();
|
|
627
|
+
expect(
|
|
628
|
+
screen.queryByText(/Waiting for the run to appear/),
|
|
629
|
+
).not.toBeInTheDocument();
|
|
630
|
+
});
|
|
631
|
+
|
|
632
|
+
test("previews a selected seven-day range without importing", async (): Promise<void> => {
|
|
633
|
+
renderDiagnostics();
|
|
634
|
+
await screen.findByText(/No runs recorded yet/);
|
|
635
|
+
fireEvent.change(screen.getByLabelText("Time range"), {
|
|
636
|
+
target: { value: "168" },
|
|
637
|
+
});
|
|
638
|
+
fireEvent.click(screen.getByRole("button", { name: "Preview detections" }));
|
|
639
|
+
await waitFor((): void => {
|
|
640
|
+
expect(API.post).toHaveBeenCalledWith(
|
|
641
|
+
expect.objectContaining({
|
|
642
|
+
data: {
|
|
643
|
+
type: "preview",
|
|
644
|
+
startTime: "2026-09-03T12:00:00.000Z",
|
|
645
|
+
endTime: "2026-09-10T12:00:00.000Z",
|
|
646
|
+
},
|
|
647
|
+
}),
|
|
648
|
+
);
|
|
649
|
+
});
|
|
650
|
+
expect(API.post).toHaveBeenCalledTimes(1);
|
|
651
|
+
});
|
|
652
|
+
|
|
653
|
+
test("historical import requires confirmation for the actual range and scope", async (): Promise<void> => {
|
|
654
|
+
const allDetections: SecurityEventConnection = connection({
|
|
655
|
+
alertingOnly: false,
|
|
656
|
+
});
|
|
657
|
+
jest.mocked(ModelAPI.getItem).mockResolvedValue(allDetections);
|
|
658
|
+
renderDiagnostics({ item: allDetections });
|
|
659
|
+
await screen.findByText(/No runs recorded yet/);
|
|
660
|
+
fireEvent.click(
|
|
661
|
+
screen.getByRole("button", { name: "Import this time range" }),
|
|
662
|
+
);
|
|
663
|
+
const confirmation: HTMLElement = screen.getByRole("group", {
|
|
664
|
+
name: "Confirm historical import",
|
|
665
|
+
});
|
|
666
|
+
expect(confirmation).toHaveTextContent("Import alerts and detections from");
|
|
667
|
+
expect(confirmation).toHaveTextContent("2026-09-09 12:00:00 UTC");
|
|
668
|
+
expect(confirmation).toHaveTextContent(
|
|
669
|
+
"Already imported detections are skipped",
|
|
670
|
+
);
|
|
671
|
+
expect(API.post).not.toHaveBeenCalled();
|
|
672
|
+
fireEvent.click(
|
|
673
|
+
within(confirmation).getByRole("button", { name: "Confirm import" }),
|
|
674
|
+
);
|
|
675
|
+
await waitFor((): void => {
|
|
676
|
+
expect(API.post).toHaveBeenCalledWith(
|
|
677
|
+
expect.objectContaining({
|
|
678
|
+
data: {
|
|
679
|
+
type: "backfill",
|
|
680
|
+
startTime: "2026-09-09T12:00:00.000Z",
|
|
681
|
+
endTime: "2026-09-10T12:00:00.000Z",
|
|
682
|
+
},
|
|
683
|
+
}),
|
|
684
|
+
);
|
|
685
|
+
});
|
|
686
|
+
});
|
|
687
|
+
|
|
688
|
+
test("an alerts-only connection confirms importing alerts", async (): Promise<void> => {
|
|
689
|
+
renderDiagnostics();
|
|
690
|
+
await screen.findByText(/No runs recorded yet/);
|
|
691
|
+
fireEvent.click(
|
|
692
|
+
screen.getByRole("button", { name: "Import this time range" }),
|
|
693
|
+
);
|
|
694
|
+
const confirmation: HTMLElement = screen.getByRole("group", {
|
|
695
|
+
name: "Confirm historical import",
|
|
696
|
+
});
|
|
697
|
+
expect(confirmation).toHaveTextContent("Import alerts from");
|
|
698
|
+
expect(confirmation).not.toHaveTextContent("alerts and detections");
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
test("changing the selected range cancels a pending import confirmation", async (): Promise<void> => {
|
|
702
|
+
renderDiagnostics();
|
|
703
|
+
await screen.findByText(/No runs recorded yet/);
|
|
704
|
+
fireEvent.click(
|
|
705
|
+
screen.getByRole("button", { name: "Import this time range" }),
|
|
706
|
+
);
|
|
707
|
+
fireEvent.change(screen.getByLabelText("Time range"), {
|
|
708
|
+
target: { value: "1" },
|
|
709
|
+
});
|
|
710
|
+
expect(
|
|
711
|
+
screen.queryByRole("group", { name: "Confirm historical import" }),
|
|
712
|
+
).not.toBeInTheDocument();
|
|
713
|
+
expect(API.post).not.toHaveBeenCalled();
|
|
714
|
+
});
|
|
715
|
+
|
|
716
|
+
test("invalid dates disable preview and import with a specific explanation", async (): Promise<void> => {
|
|
717
|
+
renderDiagnostics();
|
|
718
|
+
await screen.findByText(/No runs recorded yet/);
|
|
719
|
+
fireEvent.change(screen.getByLabelText("Start (your local time)"), {
|
|
720
|
+
target: { value: "" },
|
|
721
|
+
});
|
|
722
|
+
expect(screen.getByRole("alert")).toHaveTextContent(
|
|
723
|
+
"Choose a valid start and end time",
|
|
724
|
+
);
|
|
725
|
+
expect(
|
|
726
|
+
screen.getByRole("button", { name: "Preview detections" }),
|
|
727
|
+
).toBeDisabled();
|
|
728
|
+
expect(
|
|
729
|
+
screen.getByRole("button", { name: "Import this time range" }),
|
|
730
|
+
).toBeDisabled();
|
|
731
|
+
expect(API.post).not.toHaveBeenCalled();
|
|
732
|
+
});
|
|
733
|
+
|
|
734
|
+
test("submission failures are visible and retry remains available", async (): Promise<void> => {
|
|
735
|
+
jest
|
|
736
|
+
.mocked(API.post)
|
|
737
|
+
.mockResolvedValue(
|
|
738
|
+
new HTTPErrorResponse(
|
|
739
|
+
400,
|
|
740
|
+
{ message: "Another run is already queued for this connection." },
|
|
741
|
+
{},
|
|
742
|
+
),
|
|
743
|
+
);
|
|
744
|
+
renderDiagnostics();
|
|
745
|
+
await screen.findByText(/No runs recorded yet/);
|
|
746
|
+
fireEvent.click(screen.getByRole("button", { name: "Run now" }));
|
|
747
|
+
expect(
|
|
748
|
+
await screen.findByText(/Another run is already queued/),
|
|
749
|
+
).toBeVisible();
|
|
750
|
+
expect(screen.getByRole("button", { name: "Run now" })).toBeEnabled();
|
|
751
|
+
});
|
|
752
|
+
|
|
753
|
+
test("history failures offer refresh and stop polling when the modal unmounts", async (): Promise<void> => {
|
|
754
|
+
jest
|
|
755
|
+
.mocked(ModelAPI.getList)
|
|
756
|
+
.mockRejectedValue(new Error("History unavailable"));
|
|
757
|
+
const view: ReturnType<typeof render> = renderDiagnostics();
|
|
758
|
+
expect(await screen.findByText(/History unavailable/)).toBeVisible();
|
|
759
|
+
jest.mocked(ModelAPI.getList).mockResolvedValue(history([]));
|
|
760
|
+
fireEvent.click(screen.getByRole("button", { name: "Refresh history" }));
|
|
761
|
+
expect(await screen.findByText(/No runs recorded yet/)).toBeVisible();
|
|
762
|
+
const calls: number = jest.mocked(ModelAPI.getList).mock.calls.length;
|
|
763
|
+
view.unmount();
|
|
764
|
+
await act(async (): Promise<void> => {
|
|
765
|
+
jest.advanceTimersByTime(30000);
|
|
766
|
+
});
|
|
767
|
+
expect(ModelAPI.getList).toHaveBeenCalledTimes(calls);
|
|
768
|
+
});
|
|
769
|
+
|
|
770
|
+
test("a run carried over without a provider still reads as Google SecOps in the modal", async (): Promise<void> => {
|
|
771
|
+
const migrated: JSONObject = {
|
|
772
|
+
...(result() as unknown as JSONObject),
|
|
773
|
+
};
|
|
774
|
+
delete migrated["provider"];
|
|
775
|
+
jest
|
|
776
|
+
.mocked(ModelAPI.getList)
|
|
777
|
+
.mockResolvedValue(history([run("success", "poll", migrated)]));
|
|
778
|
+
renderDiagnostics();
|
|
779
|
+
|
|
780
|
+
expect(await screen.findByText("Result: Success")).toBeVisible();
|
|
781
|
+
const details: HTMLElement = screen.getByRole("region", {
|
|
782
|
+
name: "Run details",
|
|
783
|
+
});
|
|
784
|
+
expect(details).toHaveTextContent("Returned by Google SecOps");
|
|
785
|
+
});
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
describe("SecurityEventConnectionRunDetails for Google SecOps runs", () => {
|
|
789
|
+
beforeEach((): void => {
|
|
790
|
+
jest
|
|
791
|
+
.spyOn(RouteUtil, "populateRouteParams")
|
|
792
|
+
.mockImplementation((): Route => {
|
|
793
|
+
/*
|
|
794
|
+
* A fresh Route per call: addQueryParams mutates the Route it is
|
|
795
|
+
* given, so a shared instance would carry one link's query into the
|
|
796
|
+
* next.
|
|
797
|
+
*/
|
|
798
|
+
return new Route(`/dashboard/${PROJECT_ID}/security-events`);
|
|
799
|
+
});
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
afterEach((): void => {
|
|
803
|
+
cleanup();
|
|
804
|
+
jest.restoreAllMocks();
|
|
805
|
+
});
|
|
806
|
+
|
|
807
|
+
test("partial and empty results have different guidance and preserve the source dates", (): void => {
|
|
808
|
+
const partial: SecurityEventConnectionRunResult = result({
|
|
809
|
+
status: "partial",
|
|
810
|
+
complete: false,
|
|
811
|
+
ingestedCount: 0,
|
|
812
|
+
rejectedCount: 1,
|
|
813
|
+
warnings: ["One payload could not be normalized."],
|
|
814
|
+
});
|
|
815
|
+
const view: ReturnType<typeof render> = render(
|
|
816
|
+
<MemoryRouter>
|
|
817
|
+
<SecurityEventConnectionRunDetails
|
|
818
|
+
run={run("partial", "poll", partial)}
|
|
819
|
+
/>
|
|
820
|
+
</MemoryRouter>,
|
|
821
|
+
);
|
|
822
|
+
expect(screen.getByText("Result: Partial")).toBeVisible();
|
|
823
|
+
expect(
|
|
824
|
+
screen.getByText(/This run did not finish processing every record/),
|
|
825
|
+
).toBeVisible();
|
|
826
|
+
expect(
|
|
827
|
+
screen.getByText("One payload could not be normalized."),
|
|
828
|
+
).toBeVisible();
|
|
829
|
+
expect(screen.getByText("2026-09-09 01:30:30 UTC")).toBeVisible();
|
|
830
|
+
expect(screen.getByText("2026-09-10 04:16:58 UTC")).toBeVisible();
|
|
831
|
+
expect(screen.getByText("Returned by Google SecOps")).toBeVisible();
|
|
832
|
+
view.unmount();
|
|
833
|
+
|
|
834
|
+
render(
|
|
835
|
+
<MemoryRouter>
|
|
836
|
+
<SecurityEventConnectionRunDetails
|
|
837
|
+
run={run(
|
|
838
|
+
"empty",
|
|
839
|
+
"preview",
|
|
840
|
+
result({
|
|
841
|
+
type: "preview",
|
|
842
|
+
status: "empty",
|
|
843
|
+
fetchedCount: 0,
|
|
844
|
+
ingestedCount: 0,
|
|
845
|
+
samples: [],
|
|
846
|
+
providerDetails: { includeNonAlertingDetections: false },
|
|
847
|
+
}),
|
|
848
|
+
)}
|
|
849
|
+
/>
|
|
850
|
+
</MemoryRouter>,
|
|
851
|
+
);
|
|
852
|
+
expect(screen.getByText("Result: No records returned")).toBeVisible();
|
|
853
|
+
expect(
|
|
854
|
+
screen.getByText(
|
|
855
|
+
"Google SecOps returned no matching records for this time range. Try a wider range, or use Test connection to see what is available to import. Only alerts are imported with this Data to import selection; select Detections to import the rest.",
|
|
856
|
+
),
|
|
857
|
+
).toBeVisible();
|
|
858
|
+
expect(screen.queryByRole("link")).not.toBeInTheDocument();
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
test("an empty run that already imported Detections gets no Detections advice", (): void => {
|
|
862
|
+
renderRun(
|
|
863
|
+
run(
|
|
864
|
+
"empty",
|
|
865
|
+
"poll",
|
|
866
|
+
result({
|
|
867
|
+
status: "empty",
|
|
868
|
+
fetchedCount: 0,
|
|
869
|
+
ingestedCount: 0,
|
|
870
|
+
samples: [],
|
|
871
|
+
providerDetails: { includeNonAlertingDetections: true },
|
|
872
|
+
}),
|
|
873
|
+
),
|
|
874
|
+
);
|
|
875
|
+
expect(
|
|
876
|
+
screen.getByText(/returned no matching records for this time range/),
|
|
877
|
+
).not.toHaveTextContent("select Detections");
|
|
878
|
+
});
|
|
879
|
+
|
|
880
|
+
test.each(["test", "preview"] as Array<SecurityEventConnectionRunType>)(
|
|
881
|
+
"%s reports duplicate detection as not checked",
|
|
882
|
+
(type: SecurityEventConnectionRunType): void => {
|
|
883
|
+
renderRun(run("success", type, result({ type, duplicateCount: 0 })));
|
|
884
|
+
|
|
885
|
+
const duplicateCount: HTMLElement | null =
|
|
886
|
+
screen.getByText("Already imported").parentElement;
|
|
887
|
+
expect(duplicateCount).toHaveTextContent("Not checked");
|
|
888
|
+
expect(duplicateCount).not.toHaveTextContent("0");
|
|
889
|
+
},
|
|
890
|
+
);
|
|
891
|
+
|
|
892
|
+
test.each(["poll", "backfill"] as Array<SecurityEventConnectionRunType>)(
|
|
893
|
+
"%s reports the actual duplicate detection count",
|
|
894
|
+
(type: SecurityEventConnectionRunType): void => {
|
|
895
|
+
renderRun(run("success", type, result({ type, duplicateCount: 7 })));
|
|
896
|
+
|
|
897
|
+
const duplicateCount: HTMLElement | null =
|
|
898
|
+
screen.getByText("Already imported").parentElement;
|
|
899
|
+
expect(duplicateCount).toHaveTextContent("7");
|
|
900
|
+
expect(duplicateCount).not.toHaveTextContent("Not checked");
|
|
901
|
+
},
|
|
902
|
+
);
|
|
903
|
+
|
|
904
|
+
test("an import failure preserves successful Google read diagnostics without blaming Google", (): void => {
|
|
905
|
+
const failed: SecurityEventConnectionRunResult = result({
|
|
906
|
+
status: "failed",
|
|
907
|
+
complete: false,
|
|
908
|
+
ingestedCount: 0,
|
|
909
|
+
failedCount: 1,
|
|
910
|
+
error: "OneUptime telemetry storage is unavailable.",
|
|
911
|
+
checks: [
|
|
912
|
+
{
|
|
913
|
+
key: "read",
|
|
914
|
+
name: "Read detections from the configured instance",
|
|
915
|
+
status: "pass",
|
|
916
|
+
durationMs: 500,
|
|
917
|
+
message: "Google SecOps detection read succeeded.",
|
|
918
|
+
},
|
|
919
|
+
{
|
|
920
|
+
key: "store",
|
|
921
|
+
name: "Import detections",
|
|
922
|
+
status: "fail",
|
|
923
|
+
durationMs: 500,
|
|
924
|
+
message: "The detection could not be stored in OneUptime.",
|
|
925
|
+
remediation: "Check that the telemetry database is reachable.",
|
|
926
|
+
},
|
|
927
|
+
],
|
|
928
|
+
});
|
|
929
|
+
renderRun(run("failed", "poll", failed));
|
|
930
|
+
|
|
931
|
+
expect(screen.getByText("Result: Failed")).toBeVisible();
|
|
932
|
+
expect(
|
|
933
|
+
screen.getByText(/This run did not finish processing every record/),
|
|
934
|
+
).toHaveTextContent("Review the checks and warnings before retrying.");
|
|
935
|
+
expect(
|
|
936
|
+
screen.getByText("Read detections from the configured instance: Passed"),
|
|
937
|
+
).toHaveClass("text-green-700");
|
|
938
|
+
expect(screen.getByText("Import detections: Failed")).toHaveClass(
|
|
939
|
+
"text-red-700",
|
|
940
|
+
);
|
|
941
|
+
expect(
|
|
942
|
+
screen.getByText("Check that the telemetry database is reachable."),
|
|
943
|
+
).toBeVisible();
|
|
944
|
+
expect(
|
|
945
|
+
screen.getByText("OneUptime telemetry storage is unavailable."),
|
|
946
|
+
).toBeVisible();
|
|
947
|
+
expect(
|
|
948
|
+
screen.queryByText(/Google did not return a complete result/),
|
|
949
|
+
).not.toBeInTheDocument();
|
|
950
|
+
});
|
|
951
|
+
|
|
952
|
+
/*
|
|
953
|
+
* A failed synchronous test stores its summary as the run error and the
|
|
954
|
+
* checklist as the result. The summary is shown once, in the report
|
|
955
|
+
* banner, never again as a red box above it.
|
|
956
|
+
*/
|
|
957
|
+
test("a stored synchronous test report renders as its checklist with its error shown once", (): void => {
|
|
958
|
+
const stored: SecurityEventConnectionRun = run("failed", "test");
|
|
959
|
+
stored.result = testReport({
|
|
960
|
+
status: "fail",
|
|
961
|
+
summary: "1 check failed: Background workers.",
|
|
962
|
+
checks: [
|
|
963
|
+
{
|
|
964
|
+
key: "worker-consumers",
|
|
965
|
+
name: "Background workers",
|
|
966
|
+
status: "fail",
|
|
967
|
+
durationMs: 10,
|
|
968
|
+
message: "No process is consuming the Worker queue.",
|
|
969
|
+
remediation: "Start a worker.",
|
|
970
|
+
},
|
|
971
|
+
],
|
|
972
|
+
}) as unknown as JSONObject;
|
|
973
|
+
stored.error = "1 check failed: Background workers.";
|
|
974
|
+
const details: HTMLElement = renderRun(stored);
|
|
975
|
+
|
|
976
|
+
const report: HTMLElement = screen.getByRole("region", {
|
|
977
|
+
name: "Connection test report",
|
|
978
|
+
});
|
|
979
|
+
expect(within(report).getByText("Some checks failed")).toBeVisible();
|
|
980
|
+
expect(
|
|
981
|
+
within(report).getByText("No process is consuming the Worker queue."),
|
|
982
|
+
).toBeVisible();
|
|
983
|
+
expect(
|
|
984
|
+
screen.getAllByText("1 check failed: Background workers."),
|
|
985
|
+
).toHaveLength(1);
|
|
986
|
+
expect(
|
|
987
|
+
within(details).queryByText("1 check failed: Background workers.", {
|
|
988
|
+
selector: 'p[role="alert"]',
|
|
989
|
+
}),
|
|
990
|
+
).not.toBeInTheDocument();
|
|
991
|
+
expect(screen.queryByText(/Returned by/)).not.toBeInTheDocument();
|
|
992
|
+
});
|
|
993
|
+
|
|
994
|
+
test("a failed run without a report still shows its error, as literal text", (): void => {
|
|
995
|
+
const failed: SecurityEventConnectionRun = run("failed");
|
|
996
|
+
failed.error = "Google rejected access. <script>not markup</script>";
|
|
997
|
+
renderRun(failed);
|
|
998
|
+
expect(screen.getByRole("alert")).toHaveTextContent(failed.error);
|
|
999
|
+
expect(document.querySelector("script")).toBeNull();
|
|
1000
|
+
});
|
|
1001
|
+
|
|
1002
|
+
test("a legacy queued test result still renders as a run result", (): void => {
|
|
1003
|
+
renderRun(run("success", "test", result({ type: "test" })));
|
|
1004
|
+
expect(screen.getByText("Returned by Google SecOps")).toBeVisible();
|
|
1005
|
+
expect(
|
|
1006
|
+
screen.getByText(
|
|
1007
|
+
/does not import events or confirm that scheduled polling is working/,
|
|
1008
|
+
),
|
|
1009
|
+
).toBeVisible();
|
|
1010
|
+
expect(
|
|
1011
|
+
screen.queryByRole("region", { name: "Connection test report" }),
|
|
1012
|
+
).not.toBeInTheDocument();
|
|
1013
|
+
});
|
|
1014
|
+
|
|
1015
|
+
test("a forced advance explains the skipped minute and how to recover it", (): void => {
|
|
1016
|
+
const details: HTMLElement = renderRun(
|
|
1017
|
+
run(
|
|
1018
|
+
"partial",
|
|
1019
|
+
"poll",
|
|
1020
|
+
result({
|
|
1021
|
+
status: "partial",
|
|
1022
|
+
complete: false,
|
|
1023
|
+
chunkMinutes: 1,
|
|
1024
|
+
nextChunkMinutes: 1,
|
|
1025
|
+
forcedAdvance: true,
|
|
1026
|
+
warnings: [
|
|
1027
|
+
"More records were created in the one minute from 2026-09-10T04:59:00.000Z to 2026-09-10T05:00:00.000Z than one poll can read. Polling moved past this minute so newer records keep arriving; use Import this time range in Diagnostics on this minute to recover what one run can read.",
|
|
1028
|
+
],
|
|
1029
|
+
checks: [
|
|
1030
|
+
{
|
|
1031
|
+
key: "read-rule-detections",
|
|
1032
|
+
name: "Read rule detections by created time",
|
|
1033
|
+
status: "warn",
|
|
1034
|
+
durationMs: 500,
|
|
1035
|
+
message: "stopped by the request budget after 20 requests",
|
|
1036
|
+
},
|
|
1037
|
+
],
|
|
1038
|
+
}),
|
|
1039
|
+
),
|
|
1040
|
+
);
|
|
1041
|
+
|
|
1042
|
+
/*
|
|
1043
|
+
* Hoisted: eslint's wrap-regex and prettier disagree about an inline
|
|
1044
|
+
* literal followed by .test().
|
|
1045
|
+
*/
|
|
1046
|
+
const pollingMovedPast: RegExp = /Polling moved past one minute/;
|
|
1047
|
+
const alert: HTMLElement = within(details)
|
|
1048
|
+
.getAllByRole("alert")
|
|
1049
|
+
.find((element: HTMLElement): boolean => {
|
|
1050
|
+
return pollingMovedPast.test(element.textContent || "");
|
|
1051
|
+
}) as HTMLElement;
|
|
1052
|
+
expect(alert).toHaveTextContent(
|
|
1053
|
+
"More detections were created in one minute than one poll can read.",
|
|
1054
|
+
);
|
|
1055
|
+
expect(alert).toHaveTextContent(
|
|
1056
|
+
"use Import this time range under Find historical detections for 2026-09-10 04:59:00 UTC → 2026-09-10 05:00:00 UTC",
|
|
1057
|
+
);
|
|
1058
|
+
expect(details).toHaveTextContent("Window read by this poll1 minute");
|
|
1059
|
+
expect(details).not.toHaveTextContent("did not finish processing");
|
|
1060
|
+
expect(
|
|
1061
|
+
screen.getByText("Read rule detections by created time: Warning"),
|
|
1062
|
+
).toHaveClass("text-amber-700");
|
|
1063
|
+
});
|
|
1064
|
+
|
|
1065
|
+
test("a narrowed partial poll says the next poll re-reads a shorter window", (): void => {
|
|
1066
|
+
const details: HTMLElement = renderRun(
|
|
1067
|
+
run(
|
|
1068
|
+
"partial",
|
|
1069
|
+
"poll",
|
|
1070
|
+
result({
|
|
1071
|
+
status: "partial",
|
|
1072
|
+
complete: false,
|
|
1073
|
+
chunkMinutes: 1440,
|
|
1074
|
+
nextChunkMinutes: 720,
|
|
1075
|
+
warnings: [
|
|
1076
|
+
"This window holds more records than one poll can read; the next poll reads a 720 minute window from the same starting point.",
|
|
1077
|
+
],
|
|
1078
|
+
}),
|
|
1079
|
+
),
|
|
1080
|
+
);
|
|
1081
|
+
expect(details).toHaveTextContent(
|
|
1082
|
+
"This window held more detections than one poll can read, so the cursor stayed where it was. The next scheduled poll reads a window of 12 hours from the same starting point",
|
|
1083
|
+
);
|
|
1084
|
+
expect(details).toHaveTextContent("Window read by this poll24 hours");
|
|
1085
|
+
expect(details).not.toHaveTextContent("Polling moved past one minute");
|
|
1086
|
+
});
|
|
1087
|
+
|
|
1088
|
+
test("provider details show the basis, per-pass counts and creation lag, and the scope as Data to import", (): void => {
|
|
1089
|
+
const details: HTMLElement = renderRun(
|
|
1090
|
+
run(
|
|
1091
|
+
"success",
|
|
1092
|
+
"poll",
|
|
1093
|
+
result({
|
|
1094
|
+
providerDetails: {
|
|
1095
|
+
basis: "created-time",
|
|
1096
|
+
sourceCounts: {
|
|
1097
|
+
ruleDetections: 12,
|
|
1098
|
+
curatedDetections: 3,
|
|
1099
|
+
alertsView: 4,
|
|
1100
|
+
},
|
|
1101
|
+
creationLag: { measured: 18, lateCount: 2, maxLagMinutes: 95 },
|
|
1102
|
+
includeNonAlertingDetections: false,
|
|
1103
|
+
},
|
|
1104
|
+
}),
|
|
1105
|
+
),
|
|
1106
|
+
);
|
|
1107
|
+
|
|
1108
|
+
const provider: HTMLElement = within(details).getByRole("region", {
|
|
1109
|
+
name: "Provider details",
|
|
1110
|
+
});
|
|
1111
|
+
const detail: (key: string) => Element | null = (
|
|
1112
|
+
key: string,
|
|
1113
|
+
): Element | null => {
|
|
1114
|
+
return provider.querySelector(`[data-detail-key="${key}"]`);
|
|
1115
|
+
};
|
|
1116
|
+
expect(detail("basis")).toHaveTextContent("Time basis readCreated time");
|
|
1117
|
+
expect(within(provider).getByText("Returned by each pass")).toBeVisible();
|
|
1118
|
+
expect(detail("sourceCounts.ruleDetections")).toHaveTextContent(
|
|
1119
|
+
"Rule detections12",
|
|
1120
|
+
);
|
|
1121
|
+
expect(detail("sourceCounts.curatedDetections")).toHaveTextContent(
|
|
1122
|
+
"Curated rule detections3",
|
|
1123
|
+
);
|
|
1124
|
+
expect(detail("sourceCounts.alertsView")).toHaveTextContent("Alerts view4");
|
|
1125
|
+
expect(within(provider).getByText("Creation lag")).toBeVisible();
|
|
1126
|
+
expect(detail("creationLag.measured")).toHaveTextContent(
|
|
1127
|
+
"Records measured18",
|
|
1128
|
+
);
|
|
1129
|
+
expect(detail("creationLag.lateCount")).toHaveTextContent(
|
|
1130
|
+
"Created later than the poll interval2",
|
|
1131
|
+
);
|
|
1132
|
+
expect(detail("creationLag.maxLagMinutes")).toHaveTextContent(
|
|
1133
|
+
"Longest lag (minutes)95",
|
|
1134
|
+
);
|
|
1135
|
+
// The scope is its own line in the provider's words, not a raw detail.
|
|
1136
|
+
expect(provider).not.toHaveTextContent("Includes non-alerting records");
|
|
1137
|
+
expect(detail("includeNonAlertingDetections")).toBeNull();
|
|
1138
|
+
expect(
|
|
1139
|
+
within(details).getByText("Data to import").parentElement,
|
|
1140
|
+
).toHaveTextContent("Data to importAlerts only");
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
test("a preview read by detection time with Detections included says so", (): void => {
|
|
1144
|
+
const details: HTMLElement = renderRun(
|
|
1145
|
+
run(
|
|
1146
|
+
"success",
|
|
1147
|
+
"preview",
|
|
1148
|
+
result({
|
|
1149
|
+
type: "preview",
|
|
1150
|
+
providerDetails: {
|
|
1151
|
+
basis: "detection-time",
|
|
1152
|
+
includeNonAlertingDetections: true,
|
|
1153
|
+
},
|
|
1154
|
+
}),
|
|
1155
|
+
),
|
|
1156
|
+
);
|
|
1157
|
+
|
|
1158
|
+
expect(
|
|
1159
|
+
within(details).getByRole("region", { name: "Provider details" }),
|
|
1160
|
+
).toHaveTextContent(
|
|
1161
|
+
"Time basis readDetection time, with created time also read",
|
|
1162
|
+
);
|
|
1163
|
+
expect(
|
|
1164
|
+
within(details).getByText("Data to import").parentElement,
|
|
1165
|
+
).toHaveTextContent("Data to importAlerts and detections");
|
|
1166
|
+
});
|
|
1167
|
+
|
|
1168
|
+
test("a run without provider details renders neither the section nor a scope line", (): void => {
|
|
1169
|
+
const details: HTMLElement = renderRun(run());
|
|
1170
|
+
expect(
|
|
1171
|
+
within(details).queryByRole("region", { name: "Provider details" }),
|
|
1172
|
+
).not.toBeInTheDocument();
|
|
1173
|
+
expect(
|
|
1174
|
+
within(details).queryByText("Data to import"),
|
|
1175
|
+
).not.toBeInTheDocument();
|
|
1176
|
+
});
|
|
1177
|
+
|
|
1178
|
+
test("a provider without the Data to import control lists every detail it reports", (): void => {
|
|
1179
|
+
const details: HTMLElement = renderRun(
|
|
1180
|
+
run(
|
|
1181
|
+
"success",
|
|
1182
|
+
"poll",
|
|
1183
|
+
result({
|
|
1184
|
+
provider: OKTA,
|
|
1185
|
+
providerDetails: {
|
|
1186
|
+
includeNonAlertingDetections: false,
|
|
1187
|
+
pagesRead: 3,
|
|
1188
|
+
},
|
|
1189
|
+
}),
|
|
1190
|
+
),
|
|
1191
|
+
);
|
|
1192
|
+
|
|
1193
|
+
const provider: HTMLElement = within(details).getByRole("region", {
|
|
1194
|
+
name: "Provider details",
|
|
1195
|
+
});
|
|
1196
|
+
expect(
|
|
1197
|
+
provider.querySelector(
|
|
1198
|
+
'[data-detail-key="includeNonAlertingDetections"]',
|
|
1199
|
+
),
|
|
1200
|
+
).toHaveTextContent("Includes non-alerting recordsNo");
|
|
1201
|
+
expect(
|
|
1202
|
+
provider.querySelector('[data-detail-key="pagesRead"]'),
|
|
1203
|
+
).toHaveTextContent("Pages read3");
|
|
1204
|
+
expect(
|
|
1205
|
+
within(details).queryByText("Data to import"),
|
|
1206
|
+
).not.toBeInTheDocument();
|
|
1207
|
+
});
|
|
1208
|
+
|
|
1209
|
+
test("samples get an Alert column only when the source says which records are alerts", (): void => {
|
|
1210
|
+
const details: HTMLElement = renderRun(
|
|
1211
|
+
run(
|
|
1212
|
+
"success",
|
|
1213
|
+
"poll",
|
|
1214
|
+
result({
|
|
1215
|
+
fetchedCount: 3,
|
|
1216
|
+
samples: [
|
|
1217
|
+
sample({ id: "alert", title: "Alerting rule", isAlert: true }),
|
|
1218
|
+
sample({ id: "match", title: "Rule match", isAlert: false }),
|
|
1219
|
+
sample({ id: "unknown", title: "Unlabelled", isAlert: undefined }),
|
|
1220
|
+
],
|
|
1221
|
+
}),
|
|
1222
|
+
),
|
|
1223
|
+
);
|
|
1224
|
+
|
|
1225
|
+
expect(
|
|
1226
|
+
within(details).getByRole("columnheader", { name: "Alert" }),
|
|
1227
|
+
).toBeVisible();
|
|
1228
|
+
const cells: (title: string) => Array<string> = (
|
|
1229
|
+
title: string,
|
|
1230
|
+
): Array<string> => {
|
|
1231
|
+
const rowElement: HTMLElement = within(details)
|
|
1232
|
+
.getByText(title)
|
|
1233
|
+
.closest("tr") as HTMLElement;
|
|
1234
|
+
return Array.from(rowElement.querySelectorAll("td")).map(
|
|
1235
|
+
(cell: HTMLTableCellElement): string => {
|
|
1236
|
+
return cell.textContent || "";
|
|
1237
|
+
},
|
|
1238
|
+
);
|
|
1239
|
+
};
|
|
1240
|
+
expect(cells("Alerting rule")[4]).toBe("Yes");
|
|
1241
|
+
expect(cells("Rule match")[4]).toBe("No");
|
|
1242
|
+
expect(cells("Unlabelled")[4]).toBe("Unknown");
|
|
1243
|
+
cleanup();
|
|
1244
|
+
|
|
1245
|
+
const withoutFlag: HTMLElement = renderRun(
|
|
1246
|
+
run(
|
|
1247
|
+
"success",
|
|
1248
|
+
"poll",
|
|
1249
|
+
result({
|
|
1250
|
+
provider: OKTA,
|
|
1251
|
+
samples: [sample({ isAlert: undefined })],
|
|
1252
|
+
}),
|
|
1253
|
+
),
|
|
1254
|
+
);
|
|
1255
|
+
expect(
|
|
1256
|
+
within(withoutFlag).queryByRole("columnheader", { name: "Alert" }),
|
|
1257
|
+
).not.toBeInTheDocument();
|
|
1258
|
+
expect(
|
|
1259
|
+
within(withoutFlag)
|
|
1260
|
+
.getAllByRole("columnheader")
|
|
1261
|
+
.map((header: HTMLElement): string => {
|
|
1262
|
+
return header.textContent || "";
|
|
1263
|
+
}),
|
|
1264
|
+
).toEqual([
|
|
1265
|
+
"Record",
|
|
1266
|
+
"Severity",
|
|
1267
|
+
"Event time (UTC)",
|
|
1268
|
+
"Created at source (UTC)",
|
|
1269
|
+
]);
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
/*
|
|
1273
|
+
* Poll history carried over from the retired Google SecOps connector: no
|
|
1274
|
+
* provider on the result, the diagnostics at the top level, samples that
|
|
1275
|
+
* name the rule and detection time, and success/failed check statuses.
|
|
1276
|
+
*/
|
|
1277
|
+
test("a run carried over from the retired connector renders truthfully", (): void => {
|
|
1278
|
+
const legacy: JSONObject = {
|
|
1279
|
+
type: "poll",
|
|
1280
|
+
status: "success",
|
|
1281
|
+
startedAt: "2026-09-10T11:59:00Z",
|
|
1282
|
+
completedAt: "2026-09-10T12:00:00Z",
|
|
1283
|
+
durationMs: 60000,
|
|
1284
|
+
windowStart: "2026-09-10T04:00:00Z",
|
|
1285
|
+
windowEnd: "2026-09-10T05:00:00Z",
|
|
1286
|
+
includeNonAlertingDetections: true,
|
|
1287
|
+
basis: "created-time",
|
|
1288
|
+
sourceCounts: { ruleDetections: 2, curatedDetections: 0, alertsView: 1 },
|
|
1289
|
+
creationLag: { measured: 2, lateCount: 1, maxLagMinutes: 75 },
|
|
1290
|
+
fetchedCount: 2,
|
|
1291
|
+
ingestedCount: 2,
|
|
1292
|
+
duplicateCount: 0,
|
|
1293
|
+
rejectedCount: 0,
|
|
1294
|
+
failedCount: 0,
|
|
1295
|
+
complete: true,
|
|
1296
|
+
requestCount: 3,
|
|
1297
|
+
warnings: [],
|
|
1298
|
+
samples: [
|
|
1299
|
+
{
|
|
1300
|
+
id: "legacy-1",
|
|
1301
|
+
ruleName: "Legacy brute force rule",
|
|
1302
|
+
detectionTime: "2026-09-09T01:30:30Z",
|
|
1303
|
+
createdTime: "2026-09-10T04:16:58Z",
|
|
1304
|
+
isAlert: false,
|
|
1305
|
+
},
|
|
1306
|
+
],
|
|
1307
|
+
checks: [
|
|
1308
|
+
{
|
|
1309
|
+
name: "Read detections",
|
|
1310
|
+
status: "success",
|
|
1311
|
+
durationMs: 5,
|
|
1312
|
+
message: "Read two detections.",
|
|
1313
|
+
},
|
|
1314
|
+
{
|
|
1315
|
+
name: "Import detections",
|
|
1316
|
+
status: "failed",
|
|
1317
|
+
durationMs: 5,
|
|
1318
|
+
message: "One detection was not stored.",
|
|
1319
|
+
},
|
|
1320
|
+
],
|
|
1321
|
+
eventTimeStart: "2026-09-09T01:30:30Z",
|
|
1322
|
+
eventTimeEnd: "2026-09-09T01:30:30Z",
|
|
1323
|
+
};
|
|
1324
|
+
const details: HTMLElement = renderRun(
|
|
1325
|
+
run("success", "poll", legacy),
|
|
1326
|
+
GOOGLE,
|
|
1327
|
+
);
|
|
1328
|
+
|
|
1329
|
+
expect(details).toHaveTextContent("Returned by Google SecOps");
|
|
1330
|
+
expect(
|
|
1331
|
+
within(details).getByText("Data to import").parentElement,
|
|
1332
|
+
).toHaveTextContent("Data to importAlerts and detections");
|
|
1333
|
+
const provider: HTMLElement = within(details).getByRole("region", {
|
|
1334
|
+
name: "Provider details",
|
|
1335
|
+
});
|
|
1336
|
+
expect(
|
|
1337
|
+
provider.querySelector('[data-detail-key="sourceCounts.ruleDetections"]'),
|
|
1338
|
+
).toHaveTextContent("Rule detections2");
|
|
1339
|
+
expect(
|
|
1340
|
+
provider.querySelector('[data-detail-key="creationLag.maxLagMinutes"]'),
|
|
1341
|
+
).toHaveTextContent("Longest lag (minutes)75");
|
|
1342
|
+
|
|
1343
|
+
const sampleRow: HTMLElement = within(details)
|
|
1344
|
+
.getByText("Legacy brute force rule")
|
|
1345
|
+
.closest("tr") as HTMLElement;
|
|
1346
|
+
expect(sampleRow).toHaveTextContent("2026-09-09 01:30:30 UTC");
|
|
1347
|
+
expect(sampleRow).not.toHaveTextContent("Untitled");
|
|
1348
|
+
expect(
|
|
1349
|
+
Array.from(sampleRow.querySelectorAll("td")).map(
|
|
1350
|
+
(cell: HTMLTableCellElement): string => {
|
|
1351
|
+
return cell.textContent || "";
|
|
1352
|
+
},
|
|
1353
|
+
)[4],
|
|
1354
|
+
).toBe("No");
|
|
1355
|
+
|
|
1356
|
+
expect(screen.getByText("Read detections: Passed")).toHaveClass(
|
|
1357
|
+
"text-green-700",
|
|
1358
|
+
);
|
|
1359
|
+
expect(screen.getByText("Import detections: Failed")).toHaveClass(
|
|
1360
|
+
"text-red-700",
|
|
1361
|
+
);
|
|
1362
|
+
expect(
|
|
1363
|
+
screen.getByRole("link", { name: "View events in this time range" }),
|
|
1364
|
+
).toBeVisible();
|
|
1365
|
+
|
|
1366
|
+
const filter: JSONObject = eventsFilter(
|
|
1367
|
+
connectionEventsRoute(
|
|
1368
|
+
legacy as unknown as SecurityEventConnectionRunResult,
|
|
1369
|
+
CONNECTION_ID,
|
|
1370
|
+
),
|
|
1371
|
+
);
|
|
1372
|
+
expect(filter["attributes"]).toEqual({
|
|
1373
|
+
[LEGACY_GOOGLE_SECOPS_CONNECTION_ID_ATTRIBUTE]: CONNECTION_ID,
|
|
1374
|
+
});
|
|
1375
|
+
});
|
|
1376
|
+
});
|
|
1377
|
+
|
|
1378
|
+
describe("View events for a connection run", () => {
|
|
1379
|
+
beforeEach((): void => {
|
|
1380
|
+
jest
|
|
1381
|
+
.spyOn(RouteUtil, "populateRouteParams")
|
|
1382
|
+
.mockImplementation((): Route => {
|
|
1383
|
+
/*
|
|
1384
|
+
* A fresh Route per call: addQueryParams mutates the Route it is
|
|
1385
|
+
* given, so a shared instance would carry one link's query into the
|
|
1386
|
+
* next.
|
|
1387
|
+
*/
|
|
1388
|
+
return new Route(`/dashboard/${PROJECT_ID}/security-events`);
|
|
1389
|
+
});
|
|
1390
|
+
});
|
|
1391
|
+
|
|
1392
|
+
afterEach((): void => {
|
|
1393
|
+
jest.restoreAllMocks();
|
|
1394
|
+
});
|
|
1395
|
+
|
|
1396
|
+
test("newly imported events are filtered on the shared connection attribute", (): void => {
|
|
1397
|
+
const filter: JSONObject = eventsFilter(
|
|
1398
|
+
connectionEventsRoute(result(), CONNECTION_ID),
|
|
1399
|
+
);
|
|
1400
|
+
expect(filter["attributes"]).toEqual({
|
|
1401
|
+
"oneuptime.security_connection.id": CONNECTION_ID,
|
|
1402
|
+
});
|
|
1403
|
+
expect(SECURITY_CONNECTION_ID_ATTRIBUTE).toBe(
|
|
1404
|
+
"oneuptime.security_connection.id",
|
|
1405
|
+
);
|
|
1406
|
+
});
|
|
1407
|
+
|
|
1408
|
+
/*
|
|
1409
|
+
* Events a migrated connection imported before the move carry the retired
|
|
1410
|
+
* connector's attribute; its carried-over runs name that key, and the
|
|
1411
|
+
* link has to filter on it or it opens an empty table.
|
|
1412
|
+
*/
|
|
1413
|
+
test("a carried-over run filters on the attribute its events were stamped with", (): void => {
|
|
1414
|
+
const filter: JSONObject = eventsFilter(
|
|
1415
|
+
connectionEventsRoute(
|
|
1416
|
+
result({
|
|
1417
|
+
eventAttributeKey: LEGACY_GOOGLE_SECOPS_CONNECTION_ID_ATTRIBUTE,
|
|
1418
|
+
}),
|
|
1419
|
+
CONNECTION_ID,
|
|
1420
|
+
),
|
|
1421
|
+
);
|
|
1422
|
+
expect(filter["attributes"]).toEqual({
|
|
1423
|
+
"oneuptime.google_secops.connection_id": CONNECTION_ID,
|
|
1424
|
+
});
|
|
1425
|
+
expect(filter["attributes"]).not.toHaveProperty(
|
|
1426
|
+
SECURITY_CONNECTION_ID_ATTRIBUTE,
|
|
1427
|
+
);
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
test("the attribute key: named, recognisably legacy, or the shared default", (): void => {
|
|
1431
|
+
expect(connectionEventAttributeKey(result())).toBe(
|
|
1432
|
+
SECURITY_CONNECTION_ID_ATTRIBUTE,
|
|
1433
|
+
);
|
|
1434
|
+
expect(
|
|
1435
|
+
connectionEventAttributeKey(
|
|
1436
|
+
result({ eventAttributeKey: "custom.connection.id" }),
|
|
1437
|
+
),
|
|
1438
|
+
).toBe("custom.connection.id");
|
|
1439
|
+
expect(
|
|
1440
|
+
connectionEventAttributeKey({
|
|
1441
|
+
...result(),
|
|
1442
|
+
includeNonAlertingDetections: false,
|
|
1443
|
+
} as unknown as SecurityEventConnectionRunResult),
|
|
1444
|
+
).toBe(LEGACY_GOOGLE_SECOPS_CONNECTION_ID_ATTRIBUTE);
|
|
1445
|
+
// The shared poller files the flag under providerDetails, never on top.
|
|
1446
|
+
expect(
|
|
1447
|
+
connectionEventAttributeKey(
|
|
1448
|
+
result({ providerDetails: { includeNonAlertingDetections: false } }),
|
|
1449
|
+
),
|
|
1450
|
+
).toBe(SECURITY_CONNECTION_ID_ATTRIBUTE);
|
|
1451
|
+
// A named key wins even on a legacy-shaped result.
|
|
1452
|
+
expect(
|
|
1453
|
+
connectionEventAttributeKey({
|
|
1454
|
+
...result({ eventAttributeKey: SECURITY_CONNECTION_ID_ATTRIBUTE }),
|
|
1455
|
+
includeNonAlertingDetections: true,
|
|
1456
|
+
} as unknown as SecurityEventConnectionRunResult),
|
|
1457
|
+
).toBe(SECURITY_CONNECTION_ID_ATTRIBUTE);
|
|
1458
|
+
});
|
|
1459
|
+
|
|
1460
|
+
test("View events targets detection time rather than creation or import time", (): void => {
|
|
1461
|
+
const filter: JSONObject = eventsFilter(connectionEventsRoute(result()));
|
|
1462
|
+
expect(filterRange(filter)).toEqual([
|
|
1463
|
+
"2026-09-09T01:30:29.000Z",
|
|
1464
|
+
"2026-09-09T01:30:31.000Z",
|
|
1465
|
+
]);
|
|
1466
|
+
// Without a connection id there is nothing to scope on.
|
|
1467
|
+
expect(filter["attributes"]).toBeUndefined();
|
|
1468
|
+
});
|
|
1469
|
+
|
|
1470
|
+
/*
|
|
1471
|
+
* A duplicate-only run imported nothing, so the link cannot scope to the
|
|
1472
|
+
* connection, and a run without a stored event-time range must still
|
|
1473
|
+
* open the range the detections are stored under: their detection time,
|
|
1474
|
+
* which for a late-created detection is a day before its creation time.
|
|
1475
|
+
*/
|
|
1476
|
+
test("duplicate-only links recover the detection-time range from samples", (): void => {
|
|
1477
|
+
const filter: JSONObject = eventsFilter(
|
|
1478
|
+
connectionEventsRoute(
|
|
1479
|
+
result({
|
|
1480
|
+
ingestedCount: 0,
|
|
1481
|
+
duplicateCount: 1,
|
|
1482
|
+
eventTimeStart: undefined,
|
|
1483
|
+
eventTimeEnd: undefined,
|
|
1484
|
+
}),
|
|
1485
|
+
CONNECTION_ID,
|
|
1486
|
+
),
|
|
1487
|
+
);
|
|
1488
|
+
expect(filterRange(filter)).toEqual([
|
|
1489
|
+
"2026-09-09T01:30:29.000Z",
|
|
1490
|
+
"2026-09-09T01:30:31.000Z",
|
|
1491
|
+
]);
|
|
1492
|
+
expect(filter["attributes"]).toBeUndefined();
|
|
1493
|
+
});
|
|
1494
|
+
|
|
1495
|
+
test("a legacy sample's detection time is used the same way", (): void => {
|
|
1496
|
+
const legacySample: SecurityConnectorSample = {
|
|
1497
|
+
id: "legacy",
|
|
1498
|
+
detectionTime: "2026-09-08T22:00:00Z",
|
|
1499
|
+
createdTime: "2026-09-10T04:00:00Z",
|
|
1500
|
+
} as unknown as SecurityConnectorSample;
|
|
1501
|
+
const filter: JSONObject = eventsFilter(
|
|
1502
|
+
connectionEventsRoute(
|
|
1503
|
+
result({
|
|
1504
|
+
ingestedCount: 0,
|
|
1505
|
+
duplicateCount: 1,
|
|
1506
|
+
eventTimeStart: undefined,
|
|
1507
|
+
eventTimeEnd: undefined,
|
|
1508
|
+
samples: [legacySample],
|
|
1509
|
+
}),
|
|
1510
|
+
),
|
|
1511
|
+
);
|
|
1512
|
+
expect(filterRange(filter)).toEqual([
|
|
1513
|
+
"2026-09-08T21:59:59.000Z",
|
|
1514
|
+
"2026-09-08T22:00:01.000Z",
|
|
1515
|
+
]);
|
|
1516
|
+
});
|
|
1517
|
+
|
|
1518
|
+
test("creation time is the fallback for a sample without an event time, and the window for no samples", (): void => {
|
|
1519
|
+
expect(
|
|
1520
|
+
filterRange(
|
|
1521
|
+
eventsFilter(
|
|
1522
|
+
connectionEventsRoute(
|
|
1523
|
+
result({
|
|
1524
|
+
eventTimeStart: undefined,
|
|
1525
|
+
eventTimeEnd: undefined,
|
|
1526
|
+
samples: [
|
|
1527
|
+
sample({
|
|
1528
|
+
eventTime: undefined,
|
|
1529
|
+
createdTime: "2026-09-10T04:10:00Z",
|
|
1530
|
+
}),
|
|
1531
|
+
],
|
|
1532
|
+
}),
|
|
1533
|
+
),
|
|
1534
|
+
),
|
|
1535
|
+
),
|
|
1536
|
+
).toEqual(["2026-09-10T04:09:59.000Z", "2026-09-10T04:10:01.000Z"]);
|
|
1537
|
+
|
|
1538
|
+
expect(
|
|
1539
|
+
filterRange(
|
|
1540
|
+
eventsFilter(
|
|
1541
|
+
connectionEventsRoute(
|
|
1542
|
+
result({
|
|
1543
|
+
eventTimeStart: undefined,
|
|
1544
|
+
eventTimeEnd: undefined,
|
|
1545
|
+
samples: [],
|
|
1546
|
+
}),
|
|
1547
|
+
),
|
|
1548
|
+
),
|
|
1549
|
+
),
|
|
1550
|
+
).toEqual(["2026-09-10T03:59:59.000Z", "2026-09-10T05:00:01.000Z"]);
|
|
1551
|
+
});
|
|
1552
|
+
|
|
1553
|
+
test("a stored event-time range wins over the samples", (): void => {
|
|
1554
|
+
expect(
|
|
1555
|
+
filterRange(
|
|
1556
|
+
eventsFilter(
|
|
1557
|
+
connectionEventsRoute(
|
|
1558
|
+
result({
|
|
1559
|
+
eventTimeStart: "2026-09-01T00:00:00Z",
|
|
1560
|
+
eventTimeEnd: "2026-09-02T00:00:00Z",
|
|
1561
|
+
}),
|
|
1562
|
+
),
|
|
1563
|
+
),
|
|
1564
|
+
),
|
|
1565
|
+
).toEqual(["2026-08-31T23:59:59.000Z", "2026-09-02T00:00:01.000Z"]);
|
|
1566
|
+
});
|
|
1567
|
+
});
|
|
1568
|
+
|
|
1569
|
+
describe("connection diagnostics util", () => {
|
|
1570
|
+
const google: SecurityEventConnectorDefinition =
|
|
1571
|
+
getSecurityEventConnectorDefinition(
|
|
1572
|
+
GOOGLE,
|
|
1573
|
+
) as SecurityEventConnectorDefinition;
|
|
1574
|
+
const okta: SecurityEventConnectorDefinition =
|
|
1575
|
+
getSecurityEventConnectorDefinition(
|
|
1576
|
+
OKTA,
|
|
1577
|
+
) as SecurityEventConnectorDefinition;
|
|
1578
|
+
|
|
1579
|
+
test("Google SecOps is titled from the catalog, and an unknown provider by its identifier", (): void => {
|
|
1580
|
+
expect(connectorProviderTitle(GOOGLE)).toBe("Google SecOps");
|
|
1581
|
+
expect(connectorProviderTitle("future-provider")).toBe("future-provider");
|
|
1582
|
+
expect(connectorProviderTitle("")).toBe("");
|
|
1583
|
+
});
|
|
1584
|
+
|
|
1585
|
+
test("the scope reads in the control's words, generically without one, and not at all without a distinction", (): void => {
|
|
1586
|
+
expect(connectorScopeLabel(google)).toBe("Data to import");
|
|
1587
|
+
expect(connectorScopeLabel(okta)).toBe("Scope");
|
|
1588
|
+
expect(connectorScopeLabel(undefined)).toBe("Scope");
|
|
1589
|
+
|
|
1590
|
+
expect(connectorScopeSummary(google, true)).toBe("Alerts only");
|
|
1591
|
+
expect(connectorScopeSummary(google, undefined)).toBe("Alerts only");
|
|
1592
|
+
expect(connectorScopeSummary(google, false)).toBe("Alerts and detections");
|
|
1593
|
+
expect(connectorScopeSummary(okta, false)).toBeUndefined();
|
|
1594
|
+
expect(connectorScopeSummary(undefined, false)).toBeUndefined();
|
|
1595
|
+
|
|
1596
|
+
const toggleOnly: Pick<
|
|
1597
|
+
SecurityEventConnectorDefinition,
|
|
1598
|
+
"supportsAlertingOnlyToggle" | "alertingOnlyControl"
|
|
1599
|
+
> = { supportsAlertingOnlyToggle: true };
|
|
1600
|
+
expect(connectorScopeLabel(toggleOnly)).toBe("Scope");
|
|
1601
|
+
expect(connectorScopeSummary(toggleOnly, true)).toBe("Alerts only");
|
|
1602
|
+
expect(connectorScopeSummary(toggleOnly, false)).toBe(
|
|
1603
|
+
"Alerts and detections",
|
|
1604
|
+
);
|
|
1605
|
+
});
|
|
1606
|
+
|
|
1607
|
+
test("an import names what it brings in only when the provider has a control", (): void => {
|
|
1608
|
+
expect(connectorScopeImportNoun(google, true)).toBe("alerts");
|
|
1609
|
+
expect(connectorScopeImportNoun(google, false)).toBe(
|
|
1610
|
+
"alerts and detections",
|
|
1611
|
+
);
|
|
1612
|
+
expect(connectorScopeImportNoun(okta, false)).toBeUndefined();
|
|
1613
|
+
expect(
|
|
1614
|
+
connectorScopeImportNoun({ supportsAlertingOnlyToggle: true }, false),
|
|
1615
|
+
).toBeUndefined();
|
|
1616
|
+
// A control on a provider without the distinction is ignored.
|
|
1617
|
+
expect(
|
|
1618
|
+
connectorScopeImportNoun(
|
|
1619
|
+
{
|
|
1620
|
+
supportsAlertingOnlyToggle: false,
|
|
1621
|
+
alertingOnlyControl: google.alertingOnlyControl,
|
|
1622
|
+
},
|
|
1623
|
+
false,
|
|
1624
|
+
),
|
|
1625
|
+
).toBeUndefined();
|
|
1626
|
+
});
|
|
1627
|
+
|
|
1628
|
+
test("provider details come from providerDetails, or from the top of a carried-over result", (): void => {
|
|
1629
|
+
expect(
|
|
1630
|
+
readConnectorProviderDetails(
|
|
1631
|
+
result({ providerDetails: { basis: "created-time" } }),
|
|
1632
|
+
),
|
|
1633
|
+
).toEqual({ basis: "created-time" });
|
|
1634
|
+
expect(readConnectorProviderDetails(result())).toBeUndefined();
|
|
1635
|
+
|
|
1636
|
+
const legacy: SecurityEventConnectionRunResult = {
|
|
1637
|
+
...result(),
|
|
1638
|
+
includeNonAlertingDetections: false,
|
|
1639
|
+
basis: "detection-time",
|
|
1640
|
+
sourceCounts: { ruleDetections: 1 },
|
|
1641
|
+
creationLag: { measured: 0, lateCount: 0, maxLagMinutes: 0 },
|
|
1642
|
+
unrelated: "ignored",
|
|
1643
|
+
} as unknown as SecurityEventConnectionRunResult;
|
|
1644
|
+
expect(readConnectorProviderDetails(legacy)).toEqual({
|
|
1645
|
+
includeNonAlertingDetections: false,
|
|
1646
|
+
basis: "detection-time",
|
|
1647
|
+
sourceCounts: { ruleDetections: 1 },
|
|
1648
|
+
creationLag: { measured: 0, lateCount: 0, maxLagMinutes: 0 },
|
|
1649
|
+
});
|
|
1650
|
+
|
|
1651
|
+
// providerDetails wins when both are present.
|
|
1652
|
+
expect(
|
|
1653
|
+
readConnectorProviderDetails({
|
|
1654
|
+
...legacy,
|
|
1655
|
+
providerDetails: { basis: "created-time" },
|
|
1656
|
+
}),
|
|
1657
|
+
).toEqual({ basis: "created-time" });
|
|
1658
|
+
});
|
|
1659
|
+
|
|
1660
|
+
test("detail groups: values first, one group per object, deeper values as JSON, blanks and omitted keys dropped", (): void => {
|
|
1661
|
+
expect(connectorProviderDetailGroups(undefined)).toEqual([]);
|
|
1662
|
+
expect(connectorProviderDetailGroups({})).toEqual([]);
|
|
1663
|
+
|
|
1664
|
+
const groups: Array<ConnectorProviderDetailGroup> =
|
|
1665
|
+
connectorProviderDetailGroups(
|
|
1666
|
+
{
|
|
1667
|
+
sourceCounts: { ruleDetections: 1200, nested: { deep: true } },
|
|
1668
|
+
basis: "created-time",
|
|
1669
|
+
truncated: true,
|
|
1670
|
+
skipped: null,
|
|
1671
|
+
emptyGroup: {},
|
|
1672
|
+
includeNonAlertingDetections: false,
|
|
1673
|
+
},
|
|
1674
|
+
["includeNonAlertingDetections"],
|
|
1675
|
+
);
|
|
1676
|
+
|
|
1677
|
+
expect(groups).toEqual([
|
|
1678
|
+
{
|
|
1679
|
+
key: "",
|
|
1680
|
+
rows: [
|
|
1681
|
+
{ key: "basis", label: "Time basis read", value: "Created time" },
|
|
1682
|
+
{ key: "truncated", label: "Truncated", value: "Yes" },
|
|
1683
|
+
],
|
|
1684
|
+
},
|
|
1685
|
+
{
|
|
1686
|
+
key: "sourceCounts",
|
|
1687
|
+
title: "Returned by each pass",
|
|
1688
|
+
rows: [
|
|
1689
|
+
{
|
|
1690
|
+
key: "sourceCounts.ruleDetections",
|
|
1691
|
+
label: "Rule detections",
|
|
1692
|
+
value: (1200).toLocaleString(),
|
|
1693
|
+
},
|
|
1694
|
+
{
|
|
1695
|
+
key: "sourceCounts.nested",
|
|
1696
|
+
label: "Nested",
|
|
1697
|
+
value: '{"deep":true}',
|
|
1698
|
+
},
|
|
1699
|
+
],
|
|
1700
|
+
},
|
|
1701
|
+
]);
|
|
1702
|
+
});
|
|
1703
|
+
|
|
1704
|
+
test("carried-over check statuses and samples are read in the shared vocabulary", (): void => {
|
|
1705
|
+
expect(normalizeConnectorCheckStatus("success")).toBe("pass");
|
|
1706
|
+
expect(normalizeConnectorCheckStatus("failed")).toBe("fail");
|
|
1707
|
+
for (const status of ["pass", "fail", "warn", "skip", "unexpected"]) {
|
|
1708
|
+
expect(normalizeConnectorCheckStatus(status)).toBe(status);
|
|
1709
|
+
}
|
|
1710
|
+
|
|
1711
|
+
expect(connectorSampleTitle(sample())).toBe("Successful brute force");
|
|
1712
|
+
const legacy: SecurityConnectorSample = {
|
|
1713
|
+
id: "legacy",
|
|
1714
|
+
ruleName: "Legacy rule",
|
|
1715
|
+
detectionTime: "2026-09-09T00:00:00Z",
|
|
1716
|
+
} as unknown as SecurityConnectorSample;
|
|
1717
|
+
expect(connectorSampleTitle(legacy)).toBe("Legacy rule");
|
|
1718
|
+
expect(connectorSampleEventTime(legacy)).toBe("2026-09-09T00:00:00Z");
|
|
1719
|
+
expect(connectorSampleEventTime(sample())).toBe("2026-09-09T01:30:30Z");
|
|
1720
|
+
expect(
|
|
1721
|
+
connectorSampleTitle({ id: "blank" } as SecurityConnectorSample),
|
|
1722
|
+
).toBe("");
|
|
1723
|
+
expect(
|
|
1724
|
+
connectorSampleEventTime({ id: "blank" } as SecurityConnectorSample),
|
|
1725
|
+
).toBeUndefined();
|
|
1726
|
+
});
|
|
1727
|
+
|
|
1728
|
+
test.each([
|
|
1729
|
+
["", "2026-09-10T12:00:00Z", "valid start"],
|
|
1730
|
+
["2026-09-10T12:00:00Z", "2026-09-10T12:00:00Z", "before the end"],
|
|
1731
|
+
["2026-09-10T11:00:00Z", "2026-09-10T12:01:00Z", "future"],
|
|
1732
|
+
["2026-09-02T12:00:00Z", "2026-09-10T12:00:00Z", "7 days or less"],
|
|
1733
|
+
])(
|
|
1734
|
+
"rejects range %s to %s",
|
|
1735
|
+
(start: string, end: string, expected: string): void => {
|
|
1736
|
+
expect(validateConnectionRange(start, end, NOW)).toContain(expected);
|
|
1737
|
+
},
|
|
1738
|
+
);
|
|
1739
|
+
|
|
1740
|
+
test("allows a complete seven-day historical range", (): void => {
|
|
1741
|
+
expect(
|
|
1742
|
+
validateConnectionRange(
|
|
1743
|
+
"2026-09-03T12:00:00Z",
|
|
1744
|
+
"2026-09-10T12:00:00Z",
|
|
1745
|
+
NOW,
|
|
1746
|
+
),
|
|
1747
|
+
).toBeNull();
|
|
1748
|
+
});
|
|
1749
|
+
|
|
1750
|
+
test("a Google SecOps connection's partial imports and catch-up stay distinguishable from failures", (): void => {
|
|
1751
|
+
const item: SecurityEventConnection = connection({
|
|
1752
|
+
lastPolledAt: new Date(NOW),
|
|
1753
|
+
lastError: "Poll incomplete; retrying the same window.",
|
|
1754
|
+
lastPollResult: result({
|
|
1755
|
+
status: "partial",
|
|
1756
|
+
complete: false,
|
|
1757
|
+
}) as unknown as JSONObject,
|
|
1758
|
+
});
|
|
1759
|
+
expect(connectorHealth(item, NOW)).toBe("Partial import");
|
|
1760
|
+
delete item.lastError;
|
|
1761
|
+
item.lastPollResult = result() as unknown as JSONObject;
|
|
1762
|
+
expect(connectorHealth(item, NOW)).toBe("Catching up");
|
|
1763
|
+
});
|
|
1764
|
+
});
|