@oneuptime/common 12.0.14 → 12.0.16
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/Index.ts +10 -0
- package/Models/AnalyticsModels/LogCountBaseline.ts +174 -0
- package/Models/AnalyticsModels/SecurityEvent.ts +794 -0
- package/Models/AnalyticsModels/Span.ts +29 -1
- package/Models/AnalyticsModels/SpanCountBaseline.ts +198 -0
- package/Models/DatabaseModels/Alert.ts +42 -0
- package/Models/DatabaseModels/AlertMeasurement.ts +1114 -0
- package/Models/DatabaseModels/AlertMeasurementValue.ts +501 -0
- package/Models/DatabaseModels/AlertStateTimeline.ts +10 -2
- package/Models/DatabaseModels/BillingPaymentMethod.ts +8 -10
- package/Models/DatabaseModels/DetectionRule.ts +487 -0
- package/Models/DatabaseModels/GlobalOidc.ts +20 -0
- package/Models/DatabaseModels/GlobalSso.ts +20 -0
- package/Models/DatabaseModels/GoogleSecOpsConnection.ts +382 -0
- package/Models/DatabaseModels/Incident.ts +42 -0
- package/Models/DatabaseModels/IncidentMeasurement.ts +1114 -0
- package/Models/DatabaseModels/IncidentMeasurementValue.ts +501 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +10 -2
- package/Models/DatabaseModels/IncomingCallPolicyLabelRule.ts +36 -0
- package/Models/DatabaseModels/IncomingCallPolicyOwnerRule.ts +42 -0
- package/Models/DatabaseModels/Index.ts +20 -0
- package/Models/DatabaseModels/Label.ts +11 -0
- package/Models/DatabaseModels/LlmCostBudget.ts +655 -0
- package/Models/DatabaseModels/LlmModelPrice.ts +481 -0
- package/Models/DatabaseModels/LogSavedView.ts +87 -0
- package/Models/DatabaseModels/MarketingConversion.ts +222 -10
- package/Models/DatabaseModels/MetricSavedView.ts +59 -0
- package/Models/DatabaseModels/NetworkDeviceLinkRule.ts +46 -0
- package/Models/DatabaseModels/OnCallDutyPolicyLabelRule.ts +36 -0
- package/Models/DatabaseModels/OnCallDutyPolicyOwnerRule.ts +42 -0
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleLabelRule.ts +36 -0
- package/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerRule.ts +42 -0
- package/Models/DatabaseModels/Probe.ts +21 -0
- package/Models/DatabaseModels/RunbookLabelRule.ts +36 -0
- package/Models/DatabaseModels/RunbookOwnerRule.ts +42 -0
- package/Models/DatabaseModels/ScheduledMaintenanceMeasurement.ts +1104 -0
- package/Models/DatabaseModels/ScheduledMaintenanceMeasurementValue.ts +491 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +10 -2
- package/Models/DatabaseModels/StatusPage.ts +7 -4
- package/Models/DatabaseModels/StatusPageLabelRule.ts +36 -0
- package/Models/DatabaseModels/StatusPageOwnerRule.ts +42 -0
- package/Models/DatabaseModels/TableView.ts +15 -0
- package/Models/DatabaseModels/Team.ts +13 -0
- package/Models/DatabaseModels/TeamMember.ts +8 -0
- package/Models/DatabaseModels/TraceSavedView.ts +52 -0
- package/Models/DatabaseModels/WorkflowLabelRule.ts +36 -0
- package/Models/DatabaseModels/WorkflowOwnerRule.ts +42 -0
- package/Server/API/StatusPageAPI.ts +29 -4
- package/Server/API/TelemetryAPI.ts +252 -6
- package/Server/API/UserAPI.ts +88 -0
- package/Server/EnvironmentConfig.ts +43 -1
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.ts +19 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.ts +40 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788000000000-AddDetectionRuleAndGoogleSecOpsConnection.ts +88 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788100000000-AddMeasurements.ts +457 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788200000000-AddLlmCostBudget.ts +97 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788300000000-AddLlmModelPrice.ts +45 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788400000000-AddMarketingConversionAttribution.ts +105 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1788500000000-RemoveLlmCostBudgetAlertColumns.ts +77 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +16 -0
- package/Server/Middleware/IdentityRateLimit.ts +672 -0
- package/Server/Middleware/SlackAuthorization.ts +43 -6
- package/Server/Middleware/UserAuthorization.ts +236 -26
- package/Server/Middleware/WhatsAppAuthorization.ts +43 -3
- package/Server/Services/AccessTokenService.ts +15 -2
- package/Server/Services/AlertMeasurementService.ts +302 -0
- package/Server/Services/AlertMeasurementValueService.ts +508 -0
- package/Server/Services/AlertService.ts +50 -0
- package/Server/Services/AlertStateTimelineService.ts +54 -0
- package/Server/Services/DetectionRuleService.ts +74 -0
- package/Server/Services/GlobalOidcProjectService.ts +126 -1
- package/Server/Services/GlobalOidcService.ts +132 -0
- package/Server/Services/GlobalSsoProjectService.ts +126 -1
- package/Server/Services/GlobalSsoService.ts +132 -0
- package/Server/Services/GoogleSecOpsConnectionService.ts +89 -0
- package/Server/Services/IncidentMeasurementService.ts +304 -0
- package/Server/Services/IncidentMeasurementValueService.ts +523 -0
- package/Server/Services/IncidentService.ts +51 -0
- package/Server/Services/IncidentStateTimelineService.ts +54 -0
- package/Server/Services/Index.ts +30 -0
- package/Server/Services/LlmCostBudgetService.ts +93 -0
- package/Server/Services/LlmModelPriceService.ts +202 -0
- package/Server/Services/LogAggregationService.ts +1106 -2
- package/Server/Services/LogCountBaselineService.ts +163 -0
- package/Server/Services/MutableMetricService.ts +30 -9
- package/Server/Services/ScheduledMaintenanceMeasurementService.ts +311 -0
- package/Server/Services/ScheduledMaintenanceMeasurementValueService.ts +546 -0
- package/Server/Services/ScheduledMaintenanceService.ts +53 -0
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +188 -1
- package/Server/Services/SecurityEventService.ts +104 -0
- package/Server/Services/SpanCountBaselineService.ts +173 -0
- package/Server/Services/StatusPageService.ts +4 -3
- package/Server/Services/TelemetryUsageBillingService.ts +17 -2
- package/Server/Services/UserEmailService.ts +4 -4
- package/Server/Services/UserService.ts +284 -51
- package/Server/Services/UserTotpAuthService.ts +27 -86
- package/Server/Services/UserWebAuthnService.ts +26 -86
- package/Server/Types/Billing/MeteredPlan/AllMeteredPlans.ts +14 -0
- package/Server/Types/Database/Permissions/TenantPermission.ts +10 -0
- package/Server/Types/Database/QueryHelper.ts +1 -1
- package/Server/Utils/AI/Chat/ObservabilityChatPrompt.ts +1 -1
- package/Server/Utils/AI/Toolbox/Index.ts +6 -0
- package/Server/Utils/AI/Toolbox/SecurityEventTools.ts +349 -0
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +13 -4
- package/Server/Utils/Attribution.ts +130 -34
- package/Server/Utils/CronTab.ts +73 -0
- package/Server/Utils/GlobalSsoAuthorization.ts +175 -0
- package/Server/Utils/Marketing/ConversionUploadProvider.ts +123 -11
- package/Server/Utils/Marketing/Providers/GoogleAds.ts +71 -8
- package/Server/Utils/Marketing/Providers/LinkedIn.ts +55 -17
- package/Server/Utils/Marketing/Providers/Meta.ts +53 -19
- package/Server/Utils/Marketing/Providers/MicrosoftAds.ts +38 -11
- package/Server/Utils/Marketing/Providers/Reddit.ts +46 -16
- package/Server/Utils/Measurement/MeasurementDefinitionValidator.ts +209 -0
- package/Server/Utils/Measurement/MeasurementMetricWriter.ts +189 -0
- package/Server/Utils/Monitor/Criteria/APIRequestCriteria.ts +48 -33
- package/Server/Utils/Monitor/Criteria/CountAnomaly.ts +182 -0
- package/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.ts +35 -30
- package/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.ts +33 -28
- package/Server/Utils/Monitor/Criteria/EvaluateOverTime.ts +475 -47
- package/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.ts +35 -30
- package/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.ts +34 -28
- package/Server/Utils/Monitor/Criteria/LogMonitorCriteria.ts +132 -0
- package/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.ts +33 -28
- package/Server/Utils/Monitor/Criteria/SecurityEventsMonitorCriteria.ts +35 -0
- package/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.ts +69 -40
- package/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.ts +35 -29
- package/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.ts +132 -0
- package/Server/Utils/Monitor/DataToProcess.ts +2 -0
- package/Server/Utils/Monitor/MonitorCriteriaDataExtractor.ts +14 -0
- package/Server/Utils/Monitor/MonitorCriteriaEvaluator.ts +23 -0
- package/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.ts +20 -0
- package/Server/Utils/Monitor/MonitorResource.ts +177 -21
- package/Server/Utils/Monitor/MonitorStepResourceIdentity.ts +1 -0
- package/Server/Utils/Monitor/MonitorStepsReferenceExtractor.ts +1 -0
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +548 -0
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.ts +262 -0
- package/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.ts +212 -0
- package/Server/Utils/SecurityEvent/SecurityEventRow.ts +101 -0
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +635 -0
- package/Server/Utils/StatusPageContentSecurityPolicy.ts +56 -0
- package/Server/Utils/StatusPageCustomizationAccess.ts +117 -0
- package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +379 -0
- package/Server/Utils/Telemetry/LlmSpan.ts +83 -4
- package/Server/Utils/Telemetry/LogErrorPatternSql.ts +136 -0
- package/Server/Utils/UserPermission/UserPermission.ts +57 -1
- package/Tests/App/Dashboard/AlertsSideMenu.test.tsx +4 -0
- package/Tests/App/Dashboard/CreatePageEntryPointPermissions.test.tsx +306 -0
- package/Tests/App/Dashboard/DangerZone.test.tsx +11 -0
- package/Tests/App/Dashboard/IncidentsSideMenu.test.tsx +4 -0
- package/Tests/App/Dashboard/LogMonitorAttributeOperatorPreview.test.tsx +459 -0
- package/Tests/App/Dashboard/OverviewCustomFields.test.tsx +24 -0
- package/Tests/App/Dashboard/ScheduledMaintenanceSideMenu.test.tsx +6 -0
- package/Tests/Models/DatabaseModels/DomainRoleTierCoverage.test.ts +323 -0
- package/Tests/Models/DatabaseModels/ProjectSharedResources.test.ts +386 -0
- package/Tests/Server/API/LogErrorPatternAPI.test.ts +811 -0
- package/Tests/Server/API/StatusPageCustomizationOrigin.test.ts +348 -0
- package/Tests/Server/API/UserAuthenticationAPI.test.ts +11 -0
- package/Tests/Server/API/UserProjectsAPI.test.ts +11 -2
- package/Tests/Server/API/UserTwoFactorAuthAdminAPI.test.ts +918 -0
- package/Tests/Server/Infrastructure/LocalCache.test.ts +149 -0
- package/Tests/Server/Infrastructure/Postgres/FixTotpOtpUrlAlgorithmMigration.test.ts +9 -59
- package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +227 -0
- package/Tests/Server/Middleware/SlackAuthorization.test.ts +149 -0
- package/Tests/Server/Middleware/UserAuthorization.test.ts +27 -13
- package/Tests/Server/Middleware/UserAuthorizationGlobalSsoGovernance.test.ts +1455 -0
- package/Tests/Server/Middleware/UserAuthorizationSSOProvider.test.ts +91 -2
- package/Tests/Server/Middleware/WhatsAppAuthorization.test.ts +233 -0
- package/Tests/Server/Services/AccessTokenProjectUserGrant.test.ts +249 -0
- package/Tests/Server/Services/AlertMeasurementService.test.ts +665 -0
- package/Tests/Server/Services/AlertMeasurementValueService.test.ts +389 -0
- package/Tests/Server/Services/DetectionRuleService.test.ts +254 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +21 -8
- package/Tests/Server/Services/GlobalSsoProviderAuthorization.test.ts +1242 -0
- package/Tests/Server/Services/IncidentMeasurementService.test.ts +394 -0
- package/Tests/Server/Services/IncidentMeasurementValueService.test.ts +816 -0
- package/Tests/Server/Services/LlmCostBudgetService.test.ts +104 -0
- package/Tests/Server/Services/LogAggregationAttributeOperators.test.ts +405 -0
- package/Tests/Server/Services/LogErrorPatternAggregation.test.ts +804 -0
- package/Tests/Server/Services/MonitorResourceProbeAgreementHydration.test.ts +459 -0
- package/Tests/Server/Services/MonitorResourceProbeAgreementReuse.test.ts +10 -0
- package/Tests/Server/Services/MutableMetricNameRouting.test.ts +83 -0
- package/Tests/Server/Services/ScheduledMaintenanceMeasurementService.test.ts +750 -0
- package/Tests/Server/Services/ScheduledMaintenanceMeasurementValueService.test.ts +478 -0
- package/Tests/Server/Services/UserAuthenticationService.test.ts +216 -4
- package/Tests/Server/Services/UserTwoFactorAuthAdmin.test.ts +1190 -0
- package/Tests/Server/Types/AnalyticsDatabase/AggregateUtilBucketTimestamp.test.ts +416 -0
- package/Tests/Server/Types/Database/Permissions/ScopedRoleSharedResourceAccess.test.ts +466 -0
- package/Tests/Server/Types/Database/QueryHelperManyToManyAndEmptyValues.test.ts +551 -0
- package/Tests/Server/Types/Database/QueryHelperOperators.test.ts +265 -0
- package/Tests/Server/Types/Database/QueryHelperTextAndStructuralOperators.test.ts +206 -0
- package/Tests/Server/Types/Database/SelectUtil.test.ts +348 -0
- package/Tests/Server/Utils/AnalyticsDatabase/ClusterAwareSchema.test.ts +6 -0
- package/Tests/Server/Utils/AnalyticsDatabase/FractionalAttributeThreshold.test.ts +224 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGenerator.test.ts +172 -2
- package/Tests/Server/Utils/Attribution.test.ts +228 -1
- package/Tests/Server/Utils/CronTab.test.ts +116 -0
- package/Tests/Server/Utils/GlobalSSOToken.test.ts +581 -0
- package/Tests/Server/Utils/GlobalSsoAuthorization.test.ts +1401 -0
- package/Tests/Server/Utils/Marketing/AdUploadableConversionTypes.test.ts +315 -0
- package/Tests/Server/Utils/Marketing/ConversionUploadProvider.test.ts +124 -7
- package/Tests/Server/Utils/Marketing/GoogleAds.test.ts +253 -1
- package/Tests/Server/Utils/Marketing/LinkedIn.test.ts +282 -0
- package/Tests/Server/Utils/Marketing/Meta.test.ts +304 -0
- package/Tests/Server/Utils/Marketing/MicrosoftAds.test.ts +263 -0
- package/Tests/Server/Utils/Marketing/Reddit.test.ts +259 -0
- package/Tests/Server/Utils/Measurement/MeasurementDefinitionValidator.test.ts +322 -0
- package/Tests/Server/Utils/Measurement/MeasurementMetricWriter.test.ts +373 -0
- package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaEvaluateOverTime.test.ts +508 -0
- package/Tests/Server/Utils/Monitor/Criteria/APIRequestCriteriaPortTimings.test.ts +30 -9
- package/Tests/Server/Utils/Monitor/Criteria/CountAnomaly.test.ts +221 -0
- package/Tests/Server/Utils/Monitor/Criteria/EvaluateOverTime.test.ts +987 -0
- package/Tests/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.test.ts +916 -0
- package/Tests/Server/Utils/Monitor/Criteria/LogMonitorCriteria.test.ts +236 -0
- package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +382 -0
- package/Tests/Server/Utils/Monitor/Criteria/SecurityEventsMonitorCriteria.test.ts +147 -0
- package/Tests/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.test.ts +1219 -0
- package/Tests/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.test.ts +274 -0
- package/Tests/Server/Utils/Monitor/MonitorResourceIngestedStepSelection.test.ts +86 -0
- package/Tests/Server/Utils/Monitor/SecurityEventsMonitorDataExtractor.test.ts +93 -0
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +660 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsClient.test.ts +308 -0
- package/Tests/Server/Utils/SecurityEvent/GoogleSecOpsPoller.test.ts +271 -0
- package/Tests/Server/Utils/SecurityEvent/SecurityEventRow.test.ts +272 -0
- package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +511 -0
- package/Tests/Server/Utils/StatusPageContentSecurityPolicy.test.ts +136 -0
- package/Tests/Server/Utils/StatusPageCustomizationAccess.test.ts +223 -0
- package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +497 -0
- package/Tests/Server/Utils/Telemetry/LlmSpan.test.ts +380 -0
- package/Tests/Server/Utils/Telemetry/LogErrorPatternSql.test.ts +207 -0
- package/Tests/Server/Utils/UserPermission/ProjectUserGrant.test.ts +293 -0
- package/Tests/Types/Monitor/MonitorStepConfigHelpers.test.ts +165 -0
- package/Tests/Types/Monitor/MonitorStepInfraMonitorUtils.test.ts +194 -0
- package/Tests/Types/Monitor/MonitorStepSecurityEventsMonitor.test.ts +201 -0
- package/Tests/Types/NetworkDevice/NetworkDeviceLinkRuleScope.test.ts +215 -0
- package/Tests/Types/SecurityEvent/OcsfEventClass.test.ts +105 -0
- package/Tests/Types/SecurityEvent/OcsfSeverity.test.ts +111 -0
- package/Tests/Types/Telemetry/LlmCostCatalog.test.ts +516 -0
- package/Tests/UI/Components/ActiveFilterChipsOperatorValues.test.tsx +222 -0
- package/Tests/UI/Components/BulkActionPermissionGating.test.tsx +296 -0
- package/Tests/UI/Components/CardModelDetailPermissionGating.test.tsx +305 -0
- package/Tests/UI/Components/CustomFields/CustomFieldsDetail.test.tsx +23 -0
- package/Tests/UI/Components/DictionaryFilterOperatorDisplay.test.ts +239 -0
- package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +267 -0
- package/Tests/UI/Components/DuplicateModel.test.tsx +37 -0
- package/Tests/UI/Components/Forms/FormSubmitAnalytics.test.tsx +206 -0
- package/Tests/UI/Components/ModelCardPermissionGating.test.tsx +348 -0
- package/Tests/UI/Components/ModelDelete.test.tsx +25 -1
- package/Tests/UI/Components/ModelFormCreatePermission.test.tsx +342 -0
- package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +680 -0
- package/Tests/UI/Components/OrderedStatesListCreateGate.test.tsx +174 -0
- package/Tests/UI/Components/SavedViewsDropdownSearch.test.tsx +1127 -0
- package/Tests/UI/Components/SavedViewsFacetSection.test.tsx +958 -0
- package/Tests/UI/Components/SavedViewsList.test.ts +699 -0
- package/Tests/UI/Components/SavedViewsSidebarIntegration.test.tsx +640 -0
- package/Tests/UI/Components/SavedViewsSurfaceParity.test.tsx +679 -0
- package/Tests/UI/Components/TableViewLoadFailure.test.tsx +248 -0
- package/Tests/UI/Types/UseComponentOutsideClick.test.tsx +226 -0
- package/Tests/UI/Utils/AIChatExport/ConversationMarkdown.test.ts +774 -0
- package/Tests/UI/Utils/AIChatExport/MarkdownBlocks.test.ts +791 -0
- package/Tests/UI/Utils/AIChatExport/MarkdownSafety.test.ts +544 -0
- package/Tests/UI/Utils/PermissionGate.test.ts +467 -0
- package/Tests/Utils/Analytics.test.ts +187 -0
- package/Tests/Utils/Measurement/MeasurementEvaluator.test.ts +639 -0
- package/Tests/Utils/Monitor/MonitorMetricType.test.ts +80 -0
- package/Tests/Utils/Monitor/MonitorSummarySnapshotUtil.test.ts +1 -0
- package/Tests/Utils/Monitor/NetworkDeviceLinkRuleUtil.test.ts +1576 -0
- package/Tests/Utils/NetworkDevice/DeviceHealthStateUtil.test.ts +433 -0
- package/Tests/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.test.ts +214 -0
- package/Tests/Utils/SecurityEvent/GenericNormalizer.test.ts +204 -0
- package/Tests/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.test.ts +252 -0
- package/Tests/Utils/SecurityEvent/NormalizerHelpers.test.ts +274 -0
- package/Tests/Utils/SecurityEvent/OcsfNormalizer.test.ts +208 -0
- package/Tests/Utils/SecurityEvent/SecurityEventNormalizer.test.ts +172 -0
- package/Tests/Utils/SecurityEvent/SigmaRuleParser.test.ts +471 -0
- package/Tests/Utils/SecurityEvent/UdmNormalizer.test.ts +361 -0
- package/Tests/Utils/Telemetry/CrossSignalScope.test.ts +82 -0
- package/Tests/Utils/Telemetry/LogErrorPattern.test.ts +463 -0
- package/Types/AI/AIChatTypes.ts +1 -0
- package/Types/Alerts/AlertMeasurementAnchorType.ts +14 -0
- package/Types/Alerts/AlertStateRole.ts +11 -0
- package/Types/AnalyticsDatabase/AnalyticsTableName.ts +16 -0
- package/Types/Dashboard/Chart/ChartType.ts +1 -0
- package/Types/Dashboard/DashboardComponentType.ts +2 -0
- package/Types/Dashboard/DashboardComponents/DashboardSecurityEventsFlowComponent.ts +18 -0
- package/Types/Dashboard/DashboardComponents/DashboardSecurityEventsListComponent.ts +16 -0
- package/Types/Incident/IncidentMeasurementAnchorType.ts +34 -0
- package/Types/Incident/IncidentStateRole.ts +14 -0
- package/Types/Marketing/Attribution.ts +75 -0
- package/Types/Marketing/MarketingConversion.ts +43 -1
- package/Types/Measurement/MeasurementAggregationType.ts +43 -0
- package/Types/Measurement/MeasurementAnchorKind.ts +23 -0
- package/Types/Measurement/MeasurementOccurrence.ts +14 -0
- package/Types/Measurement/MeasurementStatus.ts +34 -0
- package/Types/MeteredPlan/ProductType.ts +1 -0
- package/Types/Monitor/CriteriaFilter.ts +12 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +64 -0
- package/Types/Monitor/MonitorStep.ts +28 -0
- package/Types/Monitor/MonitorStepSecurityEventsMonitor.ts +100 -0
- package/Types/Monitor/MonitorType.ts +20 -0
- package/Types/Monitor/SecurityEventsMonitor/SecurityEventsMonitorResponse.ts +17 -0
- package/Types/NetworkDevice/NetworkDeviceLinkRuleScope.ts +51 -0
- package/Types/Permission.ts +399 -2
- package/Types/ScheduledMaintenance/ScheduledMaintenanceMeasurementAnchorType.ts +20 -0
- package/Types/ScheduledMaintenance/ScheduledMaintenanceStateRole.ts +16 -0
- package/Types/SecurityEvent/NormalizedSecurityEvent.ts +92 -0
- package/Types/SecurityEvent/OcsfEventClass.ts +205 -0
- package/Types/SecurityEvent/OcsfSeverity.ts +88 -0
- package/Types/SecurityEvent/SecurityEventFormat.ts +23 -0
- package/Types/SecurityEvent/SigmaRule.ts +70 -0
- package/Types/Telemetry/LlmConventions.ts +20 -1
- package/Types/Telemetry/LlmCostCatalog.ts +557 -0
- package/Types/Telemetry/ServiceType.ts +1 -0
- package/Types/Telemetry/TelemetryRetentionConfig.ts +5 -1
- package/Types/Telemetry/TelemetryType.ts +1 -0
- package/Types/TwoFactorAuthStatus.ts +42 -0
- package/Types/UserAuthenticationStatus.ts +18 -0
- package/UI/Components/ActionButton/ActionButtonSchema.ts +7 -0
- package/UI/Components/BulkUpdate/BulkArchiveActions.tsx +35 -2
- package/UI/Components/BulkUpdate/BulkLabelActions.tsx +34 -1
- package/UI/Components/BulkUpdate/BulkOwnerActions.tsx +45 -1
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +3 -0
- package/UI/Components/Button/Button.tsx +37 -2
- package/UI/Components/Card/Card.tsx +6 -0
- package/UI/Components/CustomFields/CustomFieldsDetail.tsx +24 -1
- package/UI/Components/Dictionary/DictionaryFilterOperator.ts +57 -0
- package/UI/Components/DuplicateModel/DuplicateModel.tsx +16 -0
- package/UI/Components/FormModal/BasicFormModal.tsx +12 -0
- package/UI/Components/Forms/BasicForm.tsx +13 -1
- package/UI/Components/Forms/BasicModelForm.tsx +2 -1
- package/UI/Components/Forms/ModelForm.tsx +64 -3
- package/UI/Components/Forms/Utils/FormAnalyticsName.ts +39 -0
- package/UI/Components/List/ListRow.tsx +6 -0
- package/UI/Components/LogsViewer/components/ActiveFilterChips.tsx +34 -7
- package/UI/Components/LogsViewer/components/LogsFacetSidebar.tsx +7 -40
- package/UI/Components/LogsViewer/components/SavedViewsDropdown.tsx +155 -3
- package/UI/Components/ModelDelete/ModelDelete.tsx +20 -0
- package/UI/Components/ModelDetail/CardModelDetail.tsx +36 -23
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +6 -1
- package/UI/Components/ModelList/ModelList.tsx +21 -2
- package/UI/Components/ModelList/StaticModelList.tsx +11 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +168 -62
- package/UI/Components/ModelTable/ModelTable.tsx +16 -10
- package/UI/Components/ModelTable/TableView.tsx +36 -4
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +36 -4
- package/UI/Components/OrderedStatesList/Item.tsx +6 -0
- package/UI/Components/OrderedStatesList/OrderedStatesList.tsx +81 -47
- package/UI/Components/ResetObjectID/ResetObjectID.tsx +16 -0
- package/UI/Components/SavedViews/SavedViewsFacetSection.tsx +242 -0
- package/UI/Components/SavedViews/SavedViewsList.ts +168 -0
- package/UI/Components/SavedViews/SavedViewsSearchInput.tsx +46 -0
- package/UI/Components/SavedViews/SavedViewsShowMoreButton.tsx +44 -0
- package/UI/Components/Table/TableRow.tsx +12 -0
- package/UI/Components/TelemetryViewer/components/SavedViewsDropdown.tsx +152 -2
- package/UI/Types/UseComponentOutsideClick.ts +47 -0
- package/UI/Utils/PermissionGate.ts +277 -0
- package/Utils/Analytics.ts +55 -2
- package/Utils/Dashboard/Components/DashboardSecurityEventsFlowComponent.ts +84 -0
- package/Utils/Dashboard/Components/DashboardSecurityEventsListComponent.ts +110 -0
- package/Utils/Dashboard/Components/Index.ts +14 -0
- package/Utils/Measurement/MeasurementEvaluator.ts +359 -0
- package/Utils/Monitor/MonitorMetricType.ts +40 -0
- package/Utils/Monitor/MonitorSummarySnapshotUtil.ts +1 -0
- package/Utils/Monitor/NetworkDeviceLinkRuleUtil.ts +669 -37
- package/Utils/NetworkDevice/DeviceHealthStateUtil.ts +231 -0
- package/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.ts +115 -0
- package/Utils/SecurityEvent/GenericNormalizer.ts +208 -0
- package/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.ts +206 -0
- package/Utils/SecurityEvent/NormalizerHelpers.ts +263 -0
- package/Utils/SecurityEvent/OcsfNormalizer.ts +194 -0
- package/Utils/SecurityEvent/SecurityEventNormalizer.ts +82 -0
- package/Utils/SecurityEvent/Sigma/SigmaRuleParser.ts +579 -0
- package/Utils/SecurityEvent/UdmNormalizer.ts +442 -0
- package/Utils/Telemetry/CrossSignalScope.ts +46 -9
- package/Utils/Telemetry/LogErrorPattern.ts +350 -0
- package/build/dist/Models/AnalyticsModels/Index.js +10 -0
- package/build/dist/Models/AnalyticsModels/Index.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/LogCountBaseline.js +159 -0
- package/build/dist/Models/AnalyticsModels/LogCountBaseline.js.map +1 -0
- package/build/dist/Models/AnalyticsModels/SecurityEvent.js +596 -0
- package/build/dist/Models/AnalyticsModels/SecurityEvent.js.map +1 -0
- package/build/dist/Models/AnalyticsModels/Span.js +24 -1
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/AnalyticsModels/SpanCountBaseline.js +181 -0
- package/build/dist/Models/AnalyticsModels/SpanCountBaseline.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Alert.js +43 -0
- package/build/dist/Models/DatabaseModels/Alert.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertMeasurement.js +1143 -0
- package/build/dist/Models/DatabaseModels/AlertMeasurement.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AlertMeasurementValue.js +523 -0
- package/build/dist/Models/DatabaseModels/AlertMeasurementValue.js.map +1 -0
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +9 -2
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/BillingPaymentMethod.js +10 -11
- package/build/dist/Models/DatabaseModels/BillingPaymentMethod.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DetectionRule.js +517 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/GlobalOidc.js +21 -0
- package/build/dist/Models/DatabaseModels/GlobalOidc.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GlobalSso.js +21 -0
- package/build/dist/Models/DatabaseModels/GlobalSso.js.map +1 -1
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js +410 -0
- package/build/dist/Models/DatabaseModels/GoogleSecOpsConnection.js.map +1 -0
- package/build/dist/Models/DatabaseModels/Incident.js +43 -0
- package/build/dist/Models/DatabaseModels/Incident.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentMeasurement.js +1143 -0
- package/build/dist/Models/DatabaseModels/IncidentMeasurement.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentMeasurementValue.js +523 -0
- package/build/dist/Models/DatabaseModels/IncidentMeasurementValue.js.map +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +9 -2
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyLabelRule.js +36 -0
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyOwnerRule.js +42 -0
- package/build/dist/Models/DatabaseModels/IncomingCallPolicyOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +20 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Label.js +11 -0
- package/build/dist/Models/DatabaseModels/Label.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LlmCostBudget.js +681 -0
- package/build/dist/Models/DatabaseModels/LlmCostBudget.js.map +1 -0
- package/build/dist/Models/DatabaseModels/LlmModelPrice.js +503 -0
- package/build/dist/Models/DatabaseModels/LlmModelPrice.js.map +1 -0
- package/build/dist/Models/DatabaseModels/LogSavedView.js +87 -0
- package/build/dist/Models/DatabaseModels/LogSavedView.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MarketingConversion.js +237 -11
- package/build/dist/Models/DatabaseModels/MarketingConversion.js.map +1 -1
- package/build/dist/Models/DatabaseModels/MetricSavedView.js +59 -0
- package/build/dist/Models/DatabaseModels/MetricSavedView.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js +47 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceLinkRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyLabelRule.js +36 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyOwnerRule.js +42 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLabelRule.js +36 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerRule.js +42 -0
- package/build/dist/Models/DatabaseModels/OnCallDutyPolicyScheduleOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Probe.js +21 -0
- package/build/dist/Models/DatabaseModels/Probe.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunbookLabelRule.js +36 -0
- package/build/dist/Models/DatabaseModels/RunbookLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunbookOwnerRule.js +42 -0
- package/build/dist/Models/DatabaseModels/RunbookOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceMeasurement.js +1133 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceMeasurement.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceMeasurementValue.js +515 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceMeasurementValue.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +9 -2
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPage.js +4 -4
- package/build/dist/Models/DatabaseModels/StatusPage.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageLabelRule.js +36 -0
- package/build/dist/Models/DatabaseModels/StatusPageLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageOwnerRule.js +42 -0
- package/build/dist/Models/DatabaseModels/StatusPageOwnerRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TableView.js +15 -0
- package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Team.js +13 -0
- package/build/dist/Models/DatabaseModels/Team.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamMember.js +8 -0
- package/build/dist/Models/DatabaseModels/TeamMember.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceSavedView.js +52 -0
- package/build/dist/Models/DatabaseModels/TraceSavedView.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowLabelRule.js +36 -0
- package/build/dist/Models/DatabaseModels/WorkflowLabelRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowOwnerRule.js +42 -0
- package/build/dist/Models/DatabaseModels/WorkflowOwnerRule.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +25 -4
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +136 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/API/UserAPI.js +75 -2
- package/build/dist/Server/API/UserAPI.js.map +1 -1
- package/build/dist/Server/EnvironmentConfig.js +35 -1
- package/build/dist/Server/EnvironmentConfig.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787800000000-AddScopeToNetworkDeviceLinkRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js +29 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1787900000000-AddRestrictToAttachedProjectsToGlobalSso.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788000000000-AddDetectionRuleAndGoogleSecOpsConnection.js +36 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788000000000-AddDetectionRuleAndGoogleSecOpsConnection.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788100000000-AddMeasurements.js +162 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788100000000-AddMeasurements.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788200000000-AddLlmCostBudget.js +40 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788200000000-AddLlmCostBudget.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788300000000-AddLlmModelPrice.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788300000000-AddLlmModelPrice.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788400000000-AddMarketingConversionAttribution.js +52 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788400000000-AddMarketingConversionAttribution.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788500000000-RemoveLlmCostBudgetAlertColumns.js +38 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1788500000000-RemoveLlmCostBudgetAlertColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/IdentityRateLimit.js +491 -0
- package/build/dist/Server/Middleware/IdentityRateLimit.js.map +1 -0
- package/build/dist/Server/Middleware/SlackAuthorization.js +28 -4
- package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/UserAuthorization.js +197 -22
- package/build/dist/Server/Middleware/UserAuthorization.js.map +1 -1
- package/build/dist/Server/Middleware/WhatsAppAuthorization.js +32 -2
- package/build/dist/Server/Middleware/WhatsAppAuthorization.js.map +1 -1
- package/build/dist/Server/Services/AccessTokenService.js +13 -2
- package/build/dist/Server/Services/AccessTokenService.js.map +1 -1
- package/build/dist/Server/Services/AlertMeasurementService.js +250 -0
- package/build/dist/Server/Services/AlertMeasurementService.js.map +1 -0
- package/build/dist/Server/Services/AlertMeasurementValueService.js +400 -0
- package/build/dist/Server/Services/AlertMeasurementValueService.js.map +1 -0
- package/build/dist/Server/Services/AlertService.js +37 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertStateTimelineService.js +48 -0
- package/build/dist/Server/Services/AlertStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +50 -0
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -0
- package/build/dist/Server/Services/GlobalOidcProjectService.js +105 -0
- package/build/dist/Server/Services/GlobalOidcProjectService.js.map +1 -1
- package/build/dist/Server/Services/GlobalOidcService.js +127 -0
- package/build/dist/Server/Services/GlobalOidcService.js.map +1 -1
- package/build/dist/Server/Services/GlobalSsoProjectService.js +105 -0
- package/build/dist/Server/Services/GlobalSsoProjectService.js.map +1 -1
- package/build/dist/Server/Services/GlobalSsoService.js +127 -0
- package/build/dist/Server/Services/GlobalSsoService.js.map +1 -1
- package/build/dist/Server/Services/GoogleSecOpsConnectionService.js +55 -0
- package/build/dist/Server/Services/GoogleSecOpsConnectionService.js.map +1 -0
- package/build/dist/Server/Services/IncidentMeasurementService.js +252 -0
- package/build/dist/Server/Services/IncidentMeasurementService.js.map +1 -0
- package/build/dist/Server/Services/IncidentMeasurementValueService.js +413 -0
- package/build/dist/Server/Services/IncidentMeasurementValueService.js.map +1 -0
- package/build/dist/Server/Services/IncidentService.js +41 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentStateTimelineService.js +50 -0
- package/build/dist/Server/Services/IncidentStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +30 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LlmCostBudgetService.js +89 -0
- package/build/dist/Server/Services/LlmCostBudgetService.js.map +1 -0
- package/build/dist/Server/Services/LlmModelPriceService.js +163 -0
- package/build/dist/Server/Services/LlmModelPriceService.js.map +1 -0
- package/build/dist/Server/Services/LogAggregationService.js +709 -0
- package/build/dist/Server/Services/LogAggregationService.js.map +1 -1
- package/build/dist/Server/Services/LogCountBaselineService.js +112 -0
- package/build/dist/Server/Services/LogCountBaselineService.js.map +1 -0
- package/build/dist/Server/Services/MutableMetricService.js +28 -9
- package/build/dist/Server/Services/MutableMetricService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceMeasurementService.js +254 -0
- package/build/dist/Server/Services/ScheduledMaintenanceMeasurementService.js.map +1 -0
- package/build/dist/Server/Services/ScheduledMaintenanceMeasurementValueService.js +434 -0
- package/build/dist/Server/Services/ScheduledMaintenanceMeasurementValueService.js.map +1 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +37 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +134 -0
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/SecurityEventService.js +64 -0
- package/build/dist/Server/Services/SecurityEventService.js.map +1 -0
- package/build/dist/Server/Services/SpanCountBaselineService.js +122 -0
- package/build/dist/Server/Services/SpanCountBaselineService.js.map +1 -0
- package/build/dist/Server/Services/StatusPageService.js +4 -3
- package/build/dist/Server/Services/StatusPageService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryUsageBillingService.js +15 -3
- package/build/dist/Server/Services/TelemetryUsageBillingService.js.map +1 -1
- package/build/dist/Server/Services/UserEmailService.js +4 -4
- package/build/dist/Server/Services/UserService.js +262 -42
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Services/UserTotpAuthService.js +0 -73
- package/build/dist/Server/Services/UserTotpAuthService.js.map +1 -1
- package/build/dist/Server/Services/UserWebAuthnService.js +1 -73
- package/build/dist/Server/Services/UserWebAuthnService.js.map +1 -1
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js +13 -0
- package/build/dist/Server/Types/Billing/MeteredPlan/AllMeteredPlans.js.map +1 -1
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js +10 -0
- package/build/dist/Server/Types/Database/Permissions/TenantPermission.js.map +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js +1 -1
- package/build/dist/Server/Types/Database/QueryHelper.js.map +1 -1
- package/build/dist/Server/Utils/AI/Chat/ObservabilityChatPrompt.js +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/Index.js +3 -0
- package/build/dist/Server/Utils/AI/Toolbox/Index.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/SecurityEventTools.js +257 -0
- package/build/dist/Server/Utils/AI/Toolbox/SecurityEventTools.js.map +1 -0
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +13 -4
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Attribution.js +77 -33
- package/build/dist/Server/Utils/Attribution.js.map +1 -1
- package/build/dist/Server/Utils/CronTab.js +66 -0
- package/build/dist/Server/Utils/CronTab.js.map +1 -1
- package/build/dist/Server/Utils/GlobalSsoAuthorization.js +112 -0
- package/build/dist/Server/Utils/GlobalSsoAuthorization.js.map +1 -0
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js +57 -4
- package/build/dist/Server/Utils/Marketing/ConversionUploadProvider.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js +53 -9
- package/build/dist/Server/Utils/Marketing/Providers/GoogleAds.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js +42 -18
- package/build/dist/Server/Utils/Marketing/Providers/LinkedIn.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js +43 -19
- package/build/dist/Server/Utils/Marketing/Providers/Meta.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js +25 -12
- package/build/dist/Server/Utils/Marketing/Providers/MicrosoftAds.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js +35 -16
- package/build/dist/Server/Utils/Marketing/Providers/Reddit.js.map +1 -1
- package/build/dist/Server/Utils/Measurement/MeasurementDefinitionValidator.js +109 -0
- package/build/dist/Server/Utils/Measurement/MeasurementDefinitionValidator.js.map +1 -0
- package/build/dist/Server/Utils/Measurement/MeasurementMetricWriter.js +131 -0
- package/build/dist/Server/Utils/Measurement/MeasurementMetricWriter.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js +37 -35
- package/build/dist/Server/Utils/Monitor/Criteria/APIRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/CountAnomaly.js +111 -0
- package/build/dist/Server/Utils/Monitor/Criteria/CountAnomaly.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/DnsMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js +20 -22
- package/build/dist/Server/Utils/Monitor/Criteria/DomainMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js +370 -42
- package/build/dist/Server/Utils/Monitor/Criteria/EvaluateOverTime.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/ExternalStatusPageMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/IncomingRequestCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/LogMonitorCriteria.js +84 -1
- package/build/dist/Server/Utils/Monitor/Criteria/LogMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js +21 -25
- package/build/dist/Server/Utils/Monitor/Criteria/SSLMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SecurityEventsMonitorCriteria.js +35 -0
- package/build/dist/Server/Utils/Monitor/Criteria/SecurityEventsMonitorCriteria.js.map +1 -0
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js +64 -52
- package/build/dist/Server/Utils/Monitor/Criteria/ServerMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js +25 -29
- package/build/dist/Server/Utils/Monitor/Criteria/SnmpMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.js +84 -1
- package/build/dist/Server/Utils/Monitor/Criteria/TraceMonitorCriteria.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js +7 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaDataExtractor.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js +20 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js +9 -0
- package/build/dist/Server/Utils/Monitor/MonitorCriteriaObservationBuilder.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorResource.js +133 -23
- package/build/dist/Server/Utils/Monitor/MonitorResource.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js +6 -5
- package/build/dist/Server/Utils/Monitor/MonitorStepResourceIdentity.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js +1 -0
- package/build/dist/Server/Utils/Monitor/MonitorStepsReferenceExtractor.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +404 -0
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js +145 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsClient.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js +176 -0
- package/build/dist/Server/Utils/SecurityEvent/GoogleSecOps/GoogleSecOpsPoller.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/SecurityEventRow.js +69 -0
- package/build/dist/Server/Utils/SecurityEvent/SecurityEventRow.js.map +1 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +493 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -0
- package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js +39 -0
- package/build/dist/Server/Utils/StatusPageContentSecurityPolicy.js.map +1 -0
- package/build/dist/Server/Utils/StatusPageCustomizationAccess.js +85 -0
- package/build/dist/Server/Utils/StatusPageCustomizationAccess.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +317 -0
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/LlmSpan.js +56 -4
- package/build/dist/Server/Utils/Telemetry/LlmSpan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LogErrorPatternSql.js +112 -0
- package/build/dist/Server/Utils/Telemetry/LogErrorPatternSql.js.map +1 -0
- package/build/dist/Server/Utils/UserPermission/UserPermission.js +46 -1
- package/build/dist/Server/Utils/UserPermission/UserPermission.js.map +1 -1
- package/build/dist/Types/AI/AIChatTypes.js +1 -0
- package/build/dist/Types/AI/AIChatTypes.js.map +1 -1
- package/build/dist/Types/Alerts/AlertMeasurementAnchorType.js +15 -0
- package/build/dist/Types/Alerts/AlertMeasurementAnchorType.js.map +1 -0
- package/build/dist/Types/Alerts/AlertStateRole.js +12 -0
- package/build/dist/Types/Alerts/AlertStateRole.js.map +1 -0
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js +16 -0
- package/build/dist/Types/AnalyticsDatabase/AnalyticsTableName.js.map +1 -1
- package/build/dist/Types/Dashboard/Chart/ChartType.js +1 -0
- package/build/dist/Types/Dashboard/Chart/ChartType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponentType.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponentType.js.map +1 -1
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardSecurityEventsFlowComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardSecurityEventsFlowComponent.js.map +1 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardSecurityEventsListComponent.js +2 -0
- package/build/dist/Types/Dashboard/DashboardComponents/DashboardSecurityEventsListComponent.js.map +1 -0
- package/build/dist/Types/Incident/IncidentMeasurementAnchorType.js +29 -0
- package/build/dist/Types/Incident/IncidentMeasurementAnchorType.js.map +1 -0
- package/build/dist/Types/Incident/IncidentStateRole.js +15 -0
- package/build/dist/Types/Incident/IncidentStateRole.js.map +1 -0
- package/build/dist/Types/Marketing/Attribution.js +68 -0
- package/build/dist/Types/Marketing/Attribution.js.map +1 -0
- package/build/dist/Types/Marketing/MarketingConversion.js +40 -1
- package/build/dist/Types/Marketing/MarketingConversion.js.map +1 -1
- package/build/dist/Types/Measurement/MeasurementAggregationType.js +40 -0
- package/build/dist/Types/Measurement/MeasurementAggregationType.js.map +1 -0
- package/build/dist/Types/Measurement/MeasurementAnchorKind.js +22 -0
- package/build/dist/Types/Measurement/MeasurementAnchorKind.js.map +1 -0
- package/build/dist/Types/Measurement/MeasurementOccurrence.js +15 -0
- package/build/dist/Types/Measurement/MeasurementOccurrence.js.map +1 -0
- package/build/dist/Types/Measurement/MeasurementStatus.js +32 -0
- package/build/dist/Types/Measurement/MeasurementStatus.js.map +1 -0
- package/build/dist/Types/MeteredPlan/ProductType.js +1 -0
- package/build/dist/Types/MeteredPlan/ProductType.js.map +1 -1
- package/build/dist/Types/Monitor/CriteriaFilter.js +3 -0
- package/build/dist/Types/Monitor/CriteriaFilter.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +59 -0
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +17 -0
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepSecurityEventsMonitor.js +64 -0
- package/build/dist/Types/Monitor/MonitorStepSecurityEventsMonitor.js.map +1 -0
- package/build/dist/Types/Monitor/MonitorType.js +19 -0
- package/build/dist/Types/Monitor/MonitorType.js.map +1 -1
- package/build/dist/Types/Monitor/SecurityEventsMonitor/SecurityEventsMonitorResponse.js +2 -0
- package/build/dist/Types/Monitor/SecurityEventsMonitor/SecurityEventsMonitorResponse.js.map +1 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js +46 -0
- package/build/dist/Types/NetworkDevice/NetworkDeviceLinkRuleScope.js.map +1 -0
- package/build/dist/Types/Permission.js +347 -2
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/ScheduledMaintenance/ScheduledMaintenanceMeasurementAnchorType.js +19 -0
- package/build/dist/Types/ScheduledMaintenance/ScheduledMaintenanceMeasurementAnchorType.js.map +1 -0
- package/build/dist/Types/ScheduledMaintenance/ScheduledMaintenanceStateRole.js +17 -0
- package/build/dist/Types/ScheduledMaintenance/ScheduledMaintenanceStateRole.js.map +1 -0
- package/build/dist/Types/SecurityEvent/NormalizedSecurityEvent.js +2 -0
- package/build/dist/Types/SecurityEvent/NormalizedSecurityEvent.js.map +1 -0
- package/build/dist/Types/SecurityEvent/OcsfEventClass.js +181 -0
- package/build/dist/Types/SecurityEvent/OcsfEventClass.js.map +1 -0
- package/build/dist/Types/SecurityEvent/OcsfSeverity.js +81 -0
- package/build/dist/Types/SecurityEvent/OcsfSeverity.js.map +1 -0
- package/build/dist/Types/SecurityEvent/SecurityEventFormat.js +21 -0
- package/build/dist/Types/SecurityEvent/SecurityEventFormat.js.map +1 -0
- package/build/dist/Types/SecurityEvent/SigmaRule.js +16 -0
- package/build/dist/Types/SecurityEvent/SigmaRule.js.map +1 -0
- package/build/dist/Types/Telemetry/LlmConventions.js +19 -1
- package/build/dist/Types/Telemetry/LlmConventions.js.map +1 -1
- package/build/dist/Types/Telemetry/LlmCostCatalog.js +484 -0
- package/build/dist/Types/Telemetry/LlmCostCatalog.js.map +1 -0
- package/build/dist/Types/Telemetry/ServiceType.js +1 -0
- package/build/dist/Types/Telemetry/ServiceType.js.map +1 -1
- package/build/dist/Types/Telemetry/TelemetryRetentionConfig.js.map +1 -1
- package/build/dist/Types/Telemetry/TelemetryType.js +1 -0
- package/build/dist/Types/Telemetry/TelemetryType.js.map +1 -1
- package/build/dist/Types/TwoFactorAuthStatus.js +41 -0
- package/build/dist/Types/TwoFactorAuthStatus.js.map +1 -0
- package/build/dist/UI/Components/BulkUpdate/BulkArchiveActions.js +16 -2
- package/build/dist/UI/Components/BulkUpdate/BulkArchiveActions.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +15 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkOwnerActions.js +20 -1
- package/build/dist/UI/Components/BulkUpdate/BulkOwnerActions.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/Button/Button.js +22 -1
- package/build/dist/UI/Components/Button/Button.js.map +1 -1
- package/build/dist/UI/Components/Card/Card.js +1 -1
- package/build/dist/UI/Components/Card/Card.js.map +1 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js +14 -1
- package/build/dist/UI/Components/CustomFields/CustomFieldsDetail.js.map +1 -1
- package/build/dist/UI/Components/Dictionary/DictionaryFilterOperator.js +47 -0
- package/build/dist/UI/Components/Dictionary/DictionaryFilterOperator.js.map +1 -1
- package/build/dist/UI/Components/DuplicateModel/DuplicateModel.js +8 -0
- package/build/dist/UI/Components/DuplicateModel/DuplicateModel.js.map +1 -1
- package/build/dist/UI/Components/FormModal/BasicFormModal.js +2 -1
- package/build/dist/UI/Components/FormModal/BasicFormModal.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicForm.js +10 -1
- package/build/dist/UI/Components/Forms/BasicForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/BasicModelForm.js +2 -1
- package/build/dist/UI/Components/Forms/BasicModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/ModelForm.js +44 -2
- package/build/dist/UI/Components/Forms/ModelForm.js.map +1 -1
- package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js +33 -0
- package/build/dist/UI/Components/Forms/Utils/FormAnalyticsName.js.map +1 -0
- package/build/dist/UI/Components/List/ListRow.js +4 -1
- package/build/dist/UI/Components/List/ListRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js +32 -7
- package/build/dist/UI/Components/LogsViewer/components/ActiveFilterChips.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js +2 -17
- package/build/dist/UI/Components/LogsViewer/components/LogsFacetSidebar.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js +93 -4
- package/build/dist/UI/Components/LogsViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js +13 -0
- package/build/dist/UI/Components/ModelDelete/ModelDelete.js.map +1 -1
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js +27 -11
- package/build/dist/UI/Components/ModelDetail/CardModelDetail.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js +2 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelList/ModelList.js +13 -2
- package/build/dist/UI/Components/ModelList/ModelList.js.map +1 -1
- package/build/dist/UI/Components/ModelList/StaticModelList.js +4 -1
- package/build/dist/UI/Components/ModelList/StaticModelList.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +101 -40
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ModelTable.js +9 -7
- package/build/dist/UI/Components/ModelTable/ModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/TableView.js +32 -4
- package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +19 -2
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/Item.js +4 -1
- package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js +33 -32
- package/build/dist/UI/Components/OrderedStatesList/OrderedStatesList.js.map +1 -1
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js +8 -0
- package/build/dist/UI/Components/ResetObjectID/ResetObjectID.js.map +1 -1
- package/build/dist/UI/Components/SavedViews/SavedViewsFacetSection.js +98 -0
- package/build/dist/UI/Components/SavedViews/SavedViewsFacetSection.js.map +1 -0
- package/build/dist/UI/Components/SavedViews/SavedViewsList.js +99 -0
- package/build/dist/UI/Components/SavedViews/SavedViewsList.js.map +1 -0
- package/build/dist/UI/Components/SavedViews/SavedViewsSearchInput.js +17 -0
- package/build/dist/UI/Components/SavedViews/SavedViewsSearchInput.js.map +1 -0
- package/build/dist/UI/Components/SavedViews/SavedViewsShowMoreButton.js +15 -0
- package/build/dist/UI/Components/SavedViews/SavedViewsShowMoreButton.js.map +1 -0
- package/build/dist/UI/Components/Table/TableRow.js +8 -2
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js +93 -4
- package/build/dist/UI/Components/TelemetryViewer/components/SavedViewsDropdown.js.map +1 -1
- package/build/dist/UI/Types/UseComponentOutsideClick.js +39 -0
- package/build/dist/UI/Types/UseComponentOutsideClick.js.map +1 -1
- package/build/dist/UI/Utils/PermissionGate.js +164 -0
- package/build/dist/UI/Utils/PermissionGate.js.map +1 -0
- package/build/dist/Utils/Analytics.js +50 -2
- package/build/dist/Utils/Analytics.js.map +1 -1
- package/build/dist/Utils/Dashboard/Components/DashboardSecurityEventsFlowComponent.js +70 -0
- package/build/dist/Utils/Dashboard/Components/DashboardSecurityEventsFlowComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/DashboardSecurityEventsListComponent.js +92 -0
- package/build/dist/Utils/Dashboard/Components/DashboardSecurityEventsListComponent.js.map +1 -0
- package/build/dist/Utils/Dashboard/Components/Index.js +8 -0
- package/build/dist/Utils/Dashboard/Components/Index.js.map +1 -1
- package/build/dist/Utils/Measurement/MeasurementEvaluator.js +210 -0
- package/build/dist/Utils/Measurement/MeasurementEvaluator.js.map +1 -0
- package/build/dist/Utils/Monitor/MonitorMetricType.js +37 -0
- package/build/dist/Utils/Monitor/MonitorMetricType.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorSummarySnapshotUtil.js +1 -0
- package/build/dist/Utils/Monitor/MonitorSummarySnapshotUtil.js.map +1 -1
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js +359 -37
- package/build/dist/Utils/Monitor/NetworkDeviceLinkRuleUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js +108 -0
- package/build/dist/Utils/NetworkDevice/DeviceHealthStateUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js +87 -0
- package/build/dist/Utils/NetworkDiscovery/UnclaimedScanDiagnosis.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/GenericNormalizer.js +176 -0
- package/build/dist/Utils/SecurityEvent/GenericNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js +146 -0
- package/build/dist/Utils/SecurityEvent/GoogleSecOpsAlertNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/NormalizerHelpers.js +196 -0
- package/build/dist/Utils/SecurityEvent/NormalizerHelpers.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/OcsfNormalizer.js +136 -0
- package/build/dist/Utils/SecurityEvent/OcsfNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/SecurityEventNormalizer.js +64 -0
- package/build/dist/Utils/SecurityEvent/SecurityEventNormalizer.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js +388 -0
- package/build/dist/Utils/SecurityEvent/Sigma/SigmaRuleParser.js.map +1 -0
- package/build/dist/Utils/SecurityEvent/UdmNormalizer.js +328 -0
- package/build/dist/Utils/SecurityEvent/UdmNormalizer.js.map +1 -0
- package/build/dist/Utils/Telemetry/CrossSignalScope.js +29 -7
- package/build/dist/Utils/Telemetry/CrossSignalScope.js.map +1 -1
- package/build/dist/Utils/Telemetry/LogErrorPattern.js +284 -0
- package/build/dist/Utils/Telemetry/LogErrorPattern.js.map +1 -0
- package/package.json +3 -1
|
@@ -196,3 +196,1579 @@ describe("NetworkDeviceLinkRuleUtil.resolveRule", () => {
|
|
|
196
196
|
]);
|
|
197
197
|
});
|
|
198
198
|
});
|
|
199
|
+
|
|
200
|
+
/*
|
|
201
|
+
* ---------------------------------------------------------------------------
|
|
202
|
+
* Site scope — GitHub issue #3260.
|
|
203
|
+
*
|
|
204
|
+
* The suite above is the project-scope contract and has to keep passing
|
|
205
|
+
* exactly as written: site scope is opt-in, so every rule saved before the
|
|
206
|
+
* column existed still means "one parent for the whole project".
|
|
207
|
+
*
|
|
208
|
+
* The fixtures below speak the reporter's vocabulary — routers and switches
|
|
209
|
+
* in numbered units — because the bug is only legible at that shape: fourteen
|
|
210
|
+
* units, fourteen routers all carrying the same label, and a global map that
|
|
211
|
+
* answered "which one is THE router?" with silence in all fourteen.
|
|
212
|
+
* ---------------------------------------------------------------------------
|
|
213
|
+
*/
|
|
214
|
+
|
|
215
|
+
const SWITCH_LABEL: string = "label-subcategory-switch";
|
|
216
|
+
const ROUTER_LABEL: string = "label-subcategory-router";
|
|
217
|
+
const MANAGED_LABEL: string = "label-managed";
|
|
218
|
+
|
|
219
|
+
/*
|
|
220
|
+
* The `device` helper from the suite above, extended rather than replaced: id
|
|
221
|
+
* and labels first, the two site fields optional, so an unsited device is
|
|
222
|
+
* still written `deviceAt("sw1", [...])` and reads the way it always did.
|
|
223
|
+
*/
|
|
224
|
+
const deviceAt: (
|
|
225
|
+
id: string,
|
|
226
|
+
labelIds: Array<string>,
|
|
227
|
+
siteId?: string | null | undefined,
|
|
228
|
+
siteName?: string | undefined,
|
|
229
|
+
) => LinkRuleDeviceInput = (
|
|
230
|
+
id: string,
|
|
231
|
+
labelIds: Array<string>,
|
|
232
|
+
siteId?: string | null | undefined,
|
|
233
|
+
siteName?: string | undefined,
|
|
234
|
+
): LinkRuleDeviceInput => {
|
|
235
|
+
return { id, labelIds, siteId, siteName };
|
|
236
|
+
};
|
|
237
|
+
|
|
238
|
+
const routerAt: (
|
|
239
|
+
id: string,
|
|
240
|
+
siteId?: string | null | undefined,
|
|
241
|
+
siteName?: string | undefined,
|
|
242
|
+
) => LinkRuleDeviceInput = (
|
|
243
|
+
id: string,
|
|
244
|
+
siteId?: string | null | undefined,
|
|
245
|
+
siteName?: string | undefined,
|
|
246
|
+
): LinkRuleDeviceInput => {
|
|
247
|
+
return deviceAt(id, [ROUTER_LABEL, MANAGED_LABEL], siteId, siteName);
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const switchAt: (
|
|
251
|
+
id: string,
|
|
252
|
+
siteId?: string | null | undefined,
|
|
253
|
+
siteName?: string | undefined,
|
|
254
|
+
) => LinkRuleDeviceInput = (
|
|
255
|
+
id: string,
|
|
256
|
+
siteId?: string | null | undefined,
|
|
257
|
+
siteName?: string | undefined,
|
|
258
|
+
): LinkRuleDeviceInput => {
|
|
259
|
+
return deviceAt(id, [SWITCH_LABEL, MANAGED_LABEL], siteId, siteName);
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
// Site-scoped by default; `scopedRule({ scope: "Project" })` for the other one.
|
|
263
|
+
const scopedRule: (overrides?: Partial<LinkRuleInput>) => LinkRuleInput = (
|
|
264
|
+
overrides?: Partial<LinkRuleInput>,
|
|
265
|
+
): LinkRuleInput => {
|
|
266
|
+
return {
|
|
267
|
+
id: "rule-uplink",
|
|
268
|
+
name: "Switch uplinks",
|
|
269
|
+
isEnabled: true,
|
|
270
|
+
childLabelIds: [SWITCH_LABEL, MANAGED_LABEL],
|
|
271
|
+
parentLabelIds: [ROUTER_LABEL, MANAGED_LABEL],
|
|
272
|
+
scope: "Site",
|
|
273
|
+
...overrides,
|
|
274
|
+
};
|
|
275
|
+
};
|
|
276
|
+
|
|
277
|
+
/*
|
|
278
|
+
* Site names sort into the warning text, so they are zero padded: "Unit 9"
|
|
279
|
+
* would sort after "Unit 14" and make an assertion about which three sites got
|
|
280
|
+
* named depend on how many units the test happened to build.
|
|
281
|
+
*/
|
|
282
|
+
const twoDigit: (value: number) => string = (value: number): string => {
|
|
283
|
+
return value < 10 ? `0${value}` : `${value}`;
|
|
284
|
+
};
|
|
285
|
+
|
|
286
|
+
/** One unit's devices, in the order a device query would hand them over. */
|
|
287
|
+
const unit: (
|
|
288
|
+
index: number,
|
|
289
|
+
routerCount: number,
|
|
290
|
+
switchCount: number,
|
|
291
|
+
) => Array<LinkRuleDeviceInput> = (
|
|
292
|
+
index: number,
|
|
293
|
+
routerCount: number,
|
|
294
|
+
switchCount: number,
|
|
295
|
+
): Array<LinkRuleDeviceInput> => {
|
|
296
|
+
const siteId: string = `site-${twoDigit(index)}`;
|
|
297
|
+
const siteName: string = `Unit ${twoDigit(index)}`;
|
|
298
|
+
const devices: Array<LinkRuleDeviceInput> = [];
|
|
299
|
+
for (let router: number = 1; router <= routerCount; router++) {
|
|
300
|
+
devices.push(
|
|
301
|
+
routerAt(`router-${twoDigit(index)}-${router}`, siteId, siteName),
|
|
302
|
+
);
|
|
303
|
+
}
|
|
304
|
+
for (let leaf: number = 1; leaf <= switchCount; leaf++) {
|
|
305
|
+
devices.push(
|
|
306
|
+
switchAt(`switch-${twoDigit(index)}-${leaf}`, siteId, siteName),
|
|
307
|
+
);
|
|
308
|
+
}
|
|
309
|
+
return devices;
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
/** `unit()` over a run of consecutive units, flattened. */
|
|
313
|
+
const units: (
|
|
314
|
+
from: number,
|
|
315
|
+
to: number,
|
|
316
|
+
routerCount: number,
|
|
317
|
+
switchCount: number,
|
|
318
|
+
) => Array<LinkRuleDeviceInput> = (
|
|
319
|
+
from: number,
|
|
320
|
+
to: number,
|
|
321
|
+
routerCount: number,
|
|
322
|
+
switchCount: number,
|
|
323
|
+
): Array<LinkRuleDeviceInput> => {
|
|
324
|
+
const devices: Array<LinkRuleDeviceInput> = [];
|
|
325
|
+
for (let index: number = from; index <= to; index++) {
|
|
326
|
+
devices.push(...unit(index, routerCount, switchCount));
|
|
327
|
+
}
|
|
328
|
+
return devices;
|
|
329
|
+
};
|
|
330
|
+
|
|
331
|
+
/*
|
|
332
|
+
* Device id -> normalised site key, so a test can ask which site a link's two
|
|
333
|
+
* ends live in without hard-coding the id-to-site mapping a second time.
|
|
334
|
+
*/
|
|
335
|
+
const siteIndexOf: (
|
|
336
|
+
devices: Array<LinkRuleDeviceInput>,
|
|
337
|
+
) => Map<string, string> = (
|
|
338
|
+
devices: Array<LinkRuleDeviceInput>,
|
|
339
|
+
): Map<string, string> => {
|
|
340
|
+
const index: Map<string, string> = new Map<string, string>();
|
|
341
|
+
for (const entry of devices) {
|
|
342
|
+
index.set(entry.id, (entry.siteId || "").toString().trim());
|
|
343
|
+
}
|
|
344
|
+
return index;
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
/*
|
|
348
|
+
* The util's warning and group-failure types, reached through its own
|
|
349
|
+
* signatures. A second `import` from the same module would trip
|
|
350
|
+
* `no-duplicate-imports`, and this file is only ever meant to grow downwards.
|
|
351
|
+
*/
|
|
352
|
+
type ResolvedWarning = NonNullable<
|
|
353
|
+
ReturnType<typeof NetworkDeviceLinkRuleUtil.getWarning>
|
|
354
|
+
>;
|
|
355
|
+
|
|
356
|
+
type ResolvedGroupFailure = NonNullable<
|
|
357
|
+
LinkRuleOutcome["groupFailures"]
|
|
358
|
+
>[number];
|
|
359
|
+
|
|
360
|
+
const warningOf: (outcome: LinkRuleOutcome) => ResolvedWarning = (
|
|
361
|
+
outcome: LinkRuleOutcome,
|
|
362
|
+
): ResolvedWarning => {
|
|
363
|
+
const warning: ResolvedWarning | null =
|
|
364
|
+
NetworkDeviceLinkRuleUtil.getWarning(outcome);
|
|
365
|
+
expect(warning).not.toBeNull();
|
|
366
|
+
return warning!;
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
const failuresOf: (outcome: LinkRuleOutcome) => Array<ResolvedGroupFailure> = (
|
|
370
|
+
outcome: LinkRuleOutcome,
|
|
371
|
+
): Array<ResolvedGroupFailure> => {
|
|
372
|
+
return outcome.groupFailures || [];
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
interface ScopeScenario {
|
|
376
|
+
label: string;
|
|
377
|
+
rule: LinkRuleInput;
|
|
378
|
+
devices: Array<LinkRuleDeviceInput>;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
/*
|
|
382
|
+
* One table, swept by the invariant tests below. The point of sweeping rather
|
|
383
|
+
* than asserting case by case is that the two invariants — matchedChildCount
|
|
384
|
+
* tracks links, and "empty" and "skipped" are the same fact — are what every
|
|
385
|
+
* caller reads the outcome through, so they must hold on the paths nobody
|
|
386
|
+
* thought about as much as on the ones they did.
|
|
387
|
+
*/
|
|
388
|
+
const SCOPE_SCENARIOS: Array<ScopeScenario> = [
|
|
389
|
+
{
|
|
390
|
+
label: "project scope, one router and two switches",
|
|
391
|
+
rule: scopedRule({ scope: "Project" }),
|
|
392
|
+
devices: unit(1, 1, 2),
|
|
393
|
+
},
|
|
394
|
+
{
|
|
395
|
+
label: "project scope, a router in each of two units",
|
|
396
|
+
rule: scopedRule({ scope: "Project" }),
|
|
397
|
+
devices: units(1, 2, 1, 2),
|
|
398
|
+
},
|
|
399
|
+
{
|
|
400
|
+
label: "project scope, no router anywhere",
|
|
401
|
+
rule: scopedRule({ scope: "Project" }),
|
|
402
|
+
devices: unit(1, 0, 2),
|
|
403
|
+
},
|
|
404
|
+
{
|
|
405
|
+
label: "project scope, a router and nothing to place",
|
|
406
|
+
rule: scopedRule({ scope: "Project" }),
|
|
407
|
+
devices: unit(1, 1, 0),
|
|
408
|
+
},
|
|
409
|
+
{
|
|
410
|
+
label: "project scope, disabled",
|
|
411
|
+
rule: scopedRule({ scope: "Project", isEnabled: false }),
|
|
412
|
+
devices: unit(1, 1, 2),
|
|
413
|
+
},
|
|
414
|
+
{
|
|
415
|
+
label: "project scope, no child labels",
|
|
416
|
+
rule: scopedRule({ scope: "Project", childLabelIds: [] }),
|
|
417
|
+
devices: unit(1, 1, 2),
|
|
418
|
+
},
|
|
419
|
+
{
|
|
420
|
+
label: "project scope, no parent labels",
|
|
421
|
+
rule: scopedRule({ scope: "Project", parentLabelIds: [] }),
|
|
422
|
+
devices: unit(1, 1, 2),
|
|
423
|
+
},
|
|
424
|
+
{
|
|
425
|
+
label: "site scope, fourteen healthy units",
|
|
426
|
+
rule: scopedRule(),
|
|
427
|
+
devices: units(1, 14, 1, 3),
|
|
428
|
+
},
|
|
429
|
+
{
|
|
430
|
+
label: "site scope, thirteen healthy units and one with two routers",
|
|
431
|
+
rule: scopedRule(),
|
|
432
|
+
devices: [...units(1, 13, 1, 3), ...unit(14, 2, 3)],
|
|
433
|
+
},
|
|
434
|
+
{
|
|
435
|
+
label: "site scope, every unit parentless",
|
|
436
|
+
rule: scopedRule(),
|
|
437
|
+
devices: units(1, 3, 0, 2),
|
|
438
|
+
},
|
|
439
|
+
{
|
|
440
|
+
label: "site scope, nothing is assigned to a site",
|
|
441
|
+
rule: scopedRule(),
|
|
442
|
+
devices: [
|
|
443
|
+
switchAt("floater-1"),
|
|
444
|
+
switchAt("floater-2"),
|
|
445
|
+
routerAt("floater-3"),
|
|
446
|
+
],
|
|
447
|
+
},
|
|
448
|
+
{
|
|
449
|
+
label: "site scope, no devices at all",
|
|
450
|
+
rule: scopedRule(),
|
|
451
|
+
devices: [],
|
|
452
|
+
},
|
|
453
|
+
{
|
|
454
|
+
label: "site scope, the only child match is the router itself",
|
|
455
|
+
rule: scopedRule(),
|
|
456
|
+
devices: [
|
|
457
|
+
deviceAt(
|
|
458
|
+
"core-01",
|
|
459
|
+
[ROUTER_LABEL, SWITCH_LABEL, MANAGED_LABEL],
|
|
460
|
+
"site-01",
|
|
461
|
+
"Unit 01",
|
|
462
|
+
),
|
|
463
|
+
],
|
|
464
|
+
},
|
|
465
|
+
{
|
|
466
|
+
label: "site scope, nothing carries the child labels",
|
|
467
|
+
rule: scopedRule(),
|
|
468
|
+
devices: units(1, 3, 1, 0),
|
|
469
|
+
},
|
|
470
|
+
{
|
|
471
|
+
label: "site scope, disabled",
|
|
472
|
+
rule: scopedRule({ isEnabled: false }),
|
|
473
|
+
devices: units(1, 3, 1, 2),
|
|
474
|
+
},
|
|
475
|
+
];
|
|
476
|
+
|
|
477
|
+
describe("NetworkDeviceLinkRuleUtil.resolveRule — site scope", () => {
|
|
478
|
+
it("leaves the project-scope outcome byte for byte what it always was", () => {
|
|
479
|
+
/*
|
|
480
|
+
* The migration adds a nullable column, so every rule in every existing
|
|
481
|
+
* project arrives here with scope undefined. If that path grew even one
|
|
482
|
+
* new key, a rule nobody touched would start reporting site coverage for
|
|
483
|
+
* a project that has never been divided into sites — which is why the
|
|
484
|
+
* absence of the keys is asserted with `in`, not with `toBeUndefined`.
|
|
485
|
+
*/
|
|
486
|
+
const devices: Array<LinkRuleDeviceInput> = unit(1, 1, 2);
|
|
487
|
+
|
|
488
|
+
const implicit: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
489
|
+
scopedRule({ scope: undefined }),
|
|
490
|
+
devices,
|
|
491
|
+
);
|
|
492
|
+
const explicit: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
493
|
+
scopedRule({ scope: "Project" }),
|
|
494
|
+
devices,
|
|
495
|
+
);
|
|
496
|
+
// An unrecognised column value is a project-scoped rule too, never a guess.
|
|
497
|
+
const garbage: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
498
|
+
scopedRule({ scope: "Region" }),
|
|
499
|
+
devices,
|
|
500
|
+
);
|
|
501
|
+
|
|
502
|
+
expect(implicit).toEqual(explicit);
|
|
503
|
+
expect(garbage).toEqual(explicit);
|
|
504
|
+
|
|
505
|
+
expect(Object.keys(implicit).sort()).toEqual([
|
|
506
|
+
"links",
|
|
507
|
+
"matchedChildCount",
|
|
508
|
+
"matchedParentCount",
|
|
509
|
+
"ruleId",
|
|
510
|
+
"ruleName",
|
|
511
|
+
]);
|
|
512
|
+
expect("groupFailures" in implicit).toBe(false);
|
|
513
|
+
expect("applicableSiteCount" in implicit).toBe(false);
|
|
514
|
+
expect("unsitedChildDeviceCount" in implicit).toBe(false);
|
|
515
|
+
|
|
516
|
+
// The skipping path carries exactly one more key, and still no site keys.
|
|
517
|
+
const skipped: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
518
|
+
scopedRule({ scope: "Project" }),
|
|
519
|
+
unit(1, 0, 2),
|
|
520
|
+
);
|
|
521
|
+
|
|
522
|
+
expect(Object.keys(skipped).sort()).toEqual([
|
|
523
|
+
"links",
|
|
524
|
+
"matchedChildCount",
|
|
525
|
+
"matchedParentCount",
|
|
526
|
+
"ruleId",
|
|
527
|
+
"ruleName",
|
|
528
|
+
"skipReason",
|
|
529
|
+
]);
|
|
530
|
+
expect("groupFailures" in skipped).toBe(false);
|
|
531
|
+
expect("applicableSiteCount" in skipped).toBe(false);
|
|
532
|
+
expect("unsitedChildDeviceCount" in skipped).toBe(false);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
it("asks the parent question once per site, so two units draw four uplinks", () => {
|
|
536
|
+
/*
|
|
537
|
+
* Under project scope this exact device set is `ambiguousParent` — two
|
|
538
|
+
* routers carry the parent labels — and draws nothing. Partitioning by
|
|
539
|
+
* site turns the same labels into the star each unit was meant to have.
|
|
540
|
+
*/
|
|
541
|
+
const devices: Array<LinkRuleDeviceInput> = [
|
|
542
|
+
...unit(1, 1, 2),
|
|
543
|
+
...unit(2, 1, 2),
|
|
544
|
+
];
|
|
545
|
+
|
|
546
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
547
|
+
scopedRule(),
|
|
548
|
+
devices,
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
expect(outcome.links).toHaveLength(4);
|
|
552
|
+
expect(outcome.skipReason).toBeUndefined();
|
|
553
|
+
expect(outcome.groupFailures).toBeUndefined();
|
|
554
|
+
expect(outcome.applicableSiteCount).toBe(2);
|
|
555
|
+
|
|
556
|
+
// No uplink may cross a site boundary: that would be a cable nobody has.
|
|
557
|
+
const siteOf: Map<string, string> = siteIndexOf(devices);
|
|
558
|
+
for (const link of outcome.links) {
|
|
559
|
+
expect(siteOf.get(link.fromDeviceId)).toBe(siteOf.get(link.toDeviceId));
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it("draws all fourteen units instead of reporting fourteen candidate routers", () => {
|
|
564
|
+
/*
|
|
565
|
+
* The reporter's exact scenario from issue #3260: fourteen units, each
|
|
566
|
+
* with one router carrying `SubCategory:Router` and three switches. On
|
|
567
|
+
* the global map the old resolver saw fourteen parent candidates, called
|
|
568
|
+
* the rule ambiguous, and drew NOTHING — not even the thirteen units that
|
|
569
|
+
* were never ambiguous in the first place.
|
|
570
|
+
*/
|
|
571
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
572
|
+
scopedRule(),
|
|
573
|
+
units(1, 14, 1, 3),
|
|
574
|
+
);
|
|
575
|
+
|
|
576
|
+
expect(outcome.links).toHaveLength(42);
|
|
577
|
+
expect(outcome.skipReason).toBeUndefined();
|
|
578
|
+
expect(outcome.groupFailures).toBeUndefined();
|
|
579
|
+
expect(outcome.applicableSiteCount).toBe(14);
|
|
580
|
+
expect(outcome.matchedParentCount).toBe(14);
|
|
581
|
+
// A rule doing its job owes the operator no explanation.
|
|
582
|
+
expect(NetworkDeviceLinkRuleUtil.getWarning(outcome)).toBeNull();
|
|
583
|
+
});
|
|
584
|
+
|
|
585
|
+
it("puts a null site and a missing site in the same excluded bucket", () => {
|
|
586
|
+
/*
|
|
587
|
+
* `null` and `undefined` are the same fact — "nobody has assigned this
|
|
588
|
+
* device to a site" — and the resolver keys its groups on a string, so if
|
|
589
|
+
* they normalised differently they would become two sites, each with one
|
|
590
|
+
* router, each drawing uplinks between devices whose only relationship is
|
|
591
|
+
* that neither has been filed yet.
|
|
592
|
+
*/
|
|
593
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
594
|
+
scopedRule(),
|
|
595
|
+
[
|
|
596
|
+
routerAt("router-null", null),
|
|
597
|
+
switchAt("switch-null", null),
|
|
598
|
+
routerAt("router-undefined"),
|
|
599
|
+
switchAt("switch-undefined"),
|
|
600
|
+
switchAt("switch-empty", ""),
|
|
601
|
+
switchAt("switch-blank", " "),
|
|
602
|
+
],
|
|
603
|
+
);
|
|
604
|
+
|
|
605
|
+
expect(outcome.links).toEqual([]);
|
|
606
|
+
expect(outcome.applicableSiteCount).toBe(0);
|
|
607
|
+
expect(outcome.groupFailures).toBeUndefined();
|
|
608
|
+
expect(outcome.unsitedChildDeviceCount).toBe(4);
|
|
609
|
+
});
|
|
610
|
+
|
|
611
|
+
it("trims and stringifies the site key, so ' s1 ' and 's1' are one site", () => {
|
|
612
|
+
/*
|
|
613
|
+
* The caller stringifies an ObjectID into this field, and a stray space in
|
|
614
|
+
* an imported site id would otherwise split one building into two — each
|
|
615
|
+
* half then reporting that it has no router.
|
|
616
|
+
*/
|
|
617
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
618
|
+
scopedRule(),
|
|
619
|
+
[
|
|
620
|
+
routerAt("router-1", " s1 ", "Unit 01"),
|
|
621
|
+
switchAt("switch-1", "s1", "Unit 01"),
|
|
622
|
+
switchAt("switch-2", "s1 ", "Unit 01"),
|
|
623
|
+
],
|
|
624
|
+
);
|
|
625
|
+
|
|
626
|
+
expect(outcome.links).toEqual([
|
|
627
|
+
{ fromDeviceId: "switch-1", toDeviceId: "router-1" },
|
|
628
|
+
{ fromDeviceId: "switch-2", toDeviceId: "router-1" },
|
|
629
|
+
]);
|
|
630
|
+
expect(outcome.applicableSiteCount).toBe(1);
|
|
631
|
+
expect(outcome.groupFailures).toBeUndefined();
|
|
632
|
+
});
|
|
633
|
+
|
|
634
|
+
it("counts unsited children rather than linking them to each other", () => {
|
|
635
|
+
/*
|
|
636
|
+
* Two switches and a router with no site is not a site with three devices
|
|
637
|
+
* in it. Linking them would assert a cabling fact from the absence of
|
|
638
|
+
* data, so they are walked past — and counted, because "the rule skipped
|
|
639
|
+
* four of your devices" is the whole reason the operator can act on it.
|
|
640
|
+
*/
|
|
641
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
642
|
+
scopedRule(),
|
|
643
|
+
[switchAt("switch-1"), switchAt("switch-2"), routerAt("router-1")],
|
|
644
|
+
);
|
|
645
|
+
|
|
646
|
+
expect(outcome.links).toEqual([]);
|
|
647
|
+
expect(outcome.unsitedChildDeviceCount).toBe(2);
|
|
648
|
+
expect(outcome.applicableSiteCount).toBe(0);
|
|
649
|
+
expect(warningOf(outcome).message).toContain("not assigned to a site");
|
|
650
|
+
});
|
|
651
|
+
|
|
652
|
+
it("does not count an unsited device that only carries the parent labels", () => {
|
|
653
|
+
/*
|
|
654
|
+
* The count is a damage assessment — "these are the nodes floating on your
|
|
655
|
+
* map" — and an unsited router is not a node the rule wanted to place. It
|
|
656
|
+
* would inflate the number the operator is being asked to act on.
|
|
657
|
+
*/
|
|
658
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
659
|
+
scopedRule(),
|
|
660
|
+
[routerAt("router-unsited"), ...unit(1, 1, 1)],
|
|
661
|
+
);
|
|
662
|
+
|
|
663
|
+
expect(outcome.unsitedChildDeviceCount).toBeUndefined();
|
|
664
|
+
expect(outcome.links).toEqual([
|
|
665
|
+
{ fromDeviceId: "switch-01-1", toDeviceId: "router-01-1" },
|
|
666
|
+
]);
|
|
667
|
+
});
|
|
668
|
+
|
|
669
|
+
it("says nothing about a site holding neither a parent nor a child", () => {
|
|
670
|
+
/*
|
|
671
|
+
* The rule simply does not apply in that building. Reporting it would
|
|
672
|
+
* trade one loud failure for one quiet one per site in the project, and a
|
|
673
|
+
* banner that lists two hundred irrelevant sites is a banner nobody reads.
|
|
674
|
+
*/
|
|
675
|
+
const devices: Array<LinkRuleDeviceInput> = [
|
|
676
|
+
...unit(1, 1, 2),
|
|
677
|
+
deviceAt("camera-1", ["label-camera"], "site-02", "Unit 02"),
|
|
678
|
+
deviceAt("camera-2", ["label-camera"], "site-02", "Unit 02"),
|
|
679
|
+
];
|
|
680
|
+
|
|
681
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
682
|
+
scopedRule(),
|
|
683
|
+
devices,
|
|
684
|
+
);
|
|
685
|
+
|
|
686
|
+
expect(outcome.links).toHaveLength(2);
|
|
687
|
+
expect(outcome.groupFailures).toBeUndefined();
|
|
688
|
+
expect(outcome.applicableSiteCount).toBe(1);
|
|
689
|
+
});
|
|
690
|
+
|
|
691
|
+
it("says nothing about a site with a router and nothing to place", () => {
|
|
692
|
+
/*
|
|
693
|
+
* A parent label like `SubCategory:Router` is present in nearly every
|
|
694
|
+
* site by construction, so a site that has one and no switches is the
|
|
695
|
+
* normal case, not a misconfiguration.
|
|
696
|
+
*/
|
|
697
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
698
|
+
scopedRule(),
|
|
699
|
+
[...unit(1, 1, 2), ...unit(2, 1, 0), ...unit(3, 3, 0)],
|
|
700
|
+
);
|
|
701
|
+
|
|
702
|
+
expect(outcome.links).toHaveLength(2);
|
|
703
|
+
expect(outcome.groupFailures).toBeUndefined();
|
|
704
|
+
expect(outcome.applicableSiteCount).toBe(1);
|
|
705
|
+
});
|
|
706
|
+
|
|
707
|
+
it("says nothing about a site whose only child match is its own router", () => {
|
|
708
|
+
/*
|
|
709
|
+
* A collapsed core carrying both label sets is one box: after the
|
|
710
|
+
* self-link exclusion the site has nothing left to place, which is the
|
|
711
|
+
* same silence as having no switches at all rather than a failure to
|
|
712
|
+
* report. The pre-exclusion child count is 1 here, so this pins that the
|
|
713
|
+
* gate looks at what is left AFTER the parent is removed.
|
|
714
|
+
*/
|
|
715
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
716
|
+
scopedRule(),
|
|
717
|
+
[
|
|
718
|
+
...unit(1, 1, 2),
|
|
719
|
+
deviceAt(
|
|
720
|
+
"core-02",
|
|
721
|
+
[ROUTER_LABEL, SWITCH_LABEL, MANAGED_LABEL],
|
|
722
|
+
"site-02",
|
|
723
|
+
"Unit 02",
|
|
724
|
+
),
|
|
725
|
+
],
|
|
726
|
+
);
|
|
727
|
+
|
|
728
|
+
expect(outcome.links).toHaveLength(2);
|
|
729
|
+
expect(outcome.groupFailures).toBeUndefined();
|
|
730
|
+
expect(outcome.applicableSiteCount).toBe(1);
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
it("reports a site with switches and no router once, with its own damage count", () => {
|
|
734
|
+
const devices: Array<LinkRuleDeviceInput> = [
|
|
735
|
+
...unit(1, 1, 2),
|
|
736
|
+
...unit(2, 0, 3),
|
|
737
|
+
];
|
|
738
|
+
|
|
739
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
740
|
+
scopedRule(),
|
|
741
|
+
devices,
|
|
742
|
+
);
|
|
743
|
+
|
|
744
|
+
expect(outcome.links).toHaveLength(2);
|
|
745
|
+
expect(failuresOf(outcome)).toEqual([
|
|
746
|
+
{
|
|
747
|
+
siteId: "site-02",
|
|
748
|
+
siteName: "Unit 02",
|
|
749
|
+
reason: "noParentMatched",
|
|
750
|
+
matchedParentCount: 0,
|
|
751
|
+
// The three switches stranded in Unit 02, not the five in the project.
|
|
752
|
+
matchedChildCount: 3,
|
|
753
|
+
},
|
|
754
|
+
]);
|
|
755
|
+
expect(outcome.applicableSiteCount).toBe(2);
|
|
756
|
+
});
|
|
757
|
+
|
|
758
|
+
it("reports an ambiguous site's own parent count, never the project-wide one", () => {
|
|
759
|
+
/*
|
|
760
|
+
* The old failure mode in one number. Project-wide there are four routers
|
|
761
|
+
* here; the operator's actual problem is the two sitting in Unit 03, and
|
|
762
|
+
* telling them "4 devices carry the parent labels" would send them looking
|
|
763
|
+
* at the three units that are fine.
|
|
764
|
+
*/
|
|
765
|
+
const devices: Array<LinkRuleDeviceInput> = [
|
|
766
|
+
...unit(1, 1, 2),
|
|
767
|
+
...unit(2, 1, 2),
|
|
768
|
+
...unit(3, 2, 2),
|
|
769
|
+
];
|
|
770
|
+
|
|
771
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
772
|
+
scopedRule(),
|
|
773
|
+
devices,
|
|
774
|
+
);
|
|
775
|
+
|
|
776
|
+
expect(outcome.links).toHaveLength(4);
|
|
777
|
+
expect(failuresOf(outcome)).toHaveLength(1);
|
|
778
|
+
expect(failuresOf(outcome)[0]!.reason).toBe("ambiguousParent");
|
|
779
|
+
expect(failuresOf(outcome)[0]!.matchedParentCount).toBe(2);
|
|
780
|
+
expect(failuresOf(outcome)[0]!.siteId).toBe("site-03");
|
|
781
|
+
expect(failuresOf(outcome)[0]!.matchedChildCount).toBe(2);
|
|
782
|
+
});
|
|
783
|
+
|
|
784
|
+
it("keeps matchedChildCount equal to links.length in every scope and outcome", () => {
|
|
785
|
+
/*
|
|
786
|
+
* The rule list renders "Draws N uplinks" from matchedChildCount while the
|
|
787
|
+
* map renders links, so the moment those two disagree the product tells
|
|
788
|
+
* the operator two different stories about the same rule.
|
|
789
|
+
*/
|
|
790
|
+
for (const scenario of SCOPE_SCENARIOS) {
|
|
791
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
792
|
+
scenario.rule,
|
|
793
|
+
scenario.devices,
|
|
794
|
+
);
|
|
795
|
+
|
|
796
|
+
expect({
|
|
797
|
+
label: scenario.label,
|
|
798
|
+
matchedChildCount: outcome.matchedChildCount,
|
|
799
|
+
}).toEqual({
|
|
800
|
+
label: scenario.label,
|
|
801
|
+
matchedChildCount: outcome.links.length,
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
|
|
806
|
+
it("treats an empty links array and a skipReason as the same fact, both scopes", () => {
|
|
807
|
+
/*
|
|
808
|
+
* describeOutcome switches on skipReason and falls through to "Draws N
|
|
809
|
+
* uplinks", so a gap in either direction produces a rule that either
|
|
810
|
+
* claims to draw nothing while drawing, or claims to draw nothing while
|
|
811
|
+
* offering no reason at all.
|
|
812
|
+
*/
|
|
813
|
+
for (const scenario of SCOPE_SCENARIOS) {
|
|
814
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
815
|
+
scenario.rule,
|
|
816
|
+
scenario.devices,
|
|
817
|
+
);
|
|
818
|
+
|
|
819
|
+
expect({
|
|
820
|
+
label: scenario.label,
|
|
821
|
+
empty: outcome.links.length === 0,
|
|
822
|
+
}).toEqual({
|
|
823
|
+
label: scenario.label,
|
|
824
|
+
empty: outcome.skipReason !== undefined,
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
it("never claims to draw anything for an outcome that drew nothing", () => {
|
|
830
|
+
for (const scenario of SCOPE_SCENARIOS) {
|
|
831
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
832
|
+
scenario.rule,
|
|
833
|
+
scenario.devices,
|
|
834
|
+
);
|
|
835
|
+
const sentence: string =
|
|
836
|
+
NetworkDeviceLinkRuleUtil.describeOutcome(outcome);
|
|
837
|
+
|
|
838
|
+
expect({
|
|
839
|
+
label: scenario.label,
|
|
840
|
+
draws: sentence.startsWith("Draws"),
|
|
841
|
+
}).toEqual({
|
|
842
|
+
label: scenario.label,
|
|
843
|
+
draws: outcome.links.length > 0,
|
|
844
|
+
});
|
|
845
|
+
}
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
it("reports an empty device list as nothing to place, not as a resolved rule", () => {
|
|
849
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
850
|
+
scopedRule(),
|
|
851
|
+
[],
|
|
852
|
+
);
|
|
853
|
+
|
|
854
|
+
expect(outcome.links).toEqual([]);
|
|
855
|
+
expect(outcome.skipReason).toBe("noChildrenMatched");
|
|
856
|
+
expect(outcome.groupFailures).toBeUndefined();
|
|
857
|
+
expect(outcome.unsitedChildDeviceCount).toBeUndefined();
|
|
858
|
+
expect(outcome.applicableSiteCount).toBe(0);
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
it("never links a device to itself, and a dual-labelled core is its site's parent", () => {
|
|
862
|
+
/*
|
|
863
|
+
* A collapsed core carrying both label sets is still one box. In Unit 01
|
|
864
|
+
* it is the only parent candidate, so it becomes the parent and is
|
|
865
|
+
* excluded from its own children; Unit 02 resolves its own star in
|
|
866
|
+
* parallel and the two never touch.
|
|
867
|
+
*/
|
|
868
|
+
const dualIsTheOnlyParent: LinkRuleOutcome =
|
|
869
|
+
NetworkDeviceLinkRuleUtil.resolveRule(scopedRule(), [
|
|
870
|
+
deviceAt(
|
|
871
|
+
"core-01",
|
|
872
|
+
[ROUTER_LABEL, SWITCH_LABEL, MANAGED_LABEL],
|
|
873
|
+
"site-01",
|
|
874
|
+
"Unit 01",
|
|
875
|
+
),
|
|
876
|
+
switchAt("switch-01-1", "site-01", "Unit 01"),
|
|
877
|
+
routerAt("router-02-1", "site-02", "Unit 02"),
|
|
878
|
+
switchAt("switch-02-1", "site-02", "Unit 02"),
|
|
879
|
+
]);
|
|
880
|
+
|
|
881
|
+
expect(dualIsTheOnlyParent.links).toEqual([
|
|
882
|
+
{ fromDeviceId: "switch-01-1", toDeviceId: "core-01" },
|
|
883
|
+
{ fromDeviceId: "switch-02-1", toDeviceId: "router-02-1" },
|
|
884
|
+
]);
|
|
885
|
+
for (const link of dualIsTheOnlyParent.links) {
|
|
886
|
+
expect(link.fromDeviceId).not.toBe(link.toDeviceId);
|
|
887
|
+
}
|
|
888
|
+
|
|
889
|
+
/*
|
|
890
|
+
* And the other half of the same rule: a device carrying the parent labels
|
|
891
|
+
* is always a parent CANDIDATE, so it is never quietly demoted to child
|
|
892
|
+
* just because its site already has a router. Unit 02 below has two boxes
|
|
893
|
+
* answering to `SubCategory:Router` and the honest answer is that nobody
|
|
894
|
+
* said which one is the uplink — so Unit 02 draws nothing and says why,
|
|
895
|
+
* while Unit 01 is untouched by its neighbour's problem.
|
|
896
|
+
*/
|
|
897
|
+
const dualCompetesWithARouter: LinkRuleOutcome =
|
|
898
|
+
NetworkDeviceLinkRuleUtil.resolveRule(scopedRule(), [
|
|
899
|
+
deviceAt(
|
|
900
|
+
"core-01",
|
|
901
|
+
[ROUTER_LABEL, SWITCH_LABEL, MANAGED_LABEL],
|
|
902
|
+
"site-01",
|
|
903
|
+
"Unit 01",
|
|
904
|
+
),
|
|
905
|
+
switchAt("switch-01-1", "site-01", "Unit 01"),
|
|
906
|
+
deviceAt(
|
|
907
|
+
"core-02",
|
|
908
|
+
[ROUTER_LABEL, SWITCH_LABEL, MANAGED_LABEL],
|
|
909
|
+
"site-02",
|
|
910
|
+
"Unit 02",
|
|
911
|
+
),
|
|
912
|
+
routerAt("router-02-1", "site-02", "Unit 02"),
|
|
913
|
+
switchAt("switch-02-1", "site-02", "Unit 02"),
|
|
914
|
+
]);
|
|
915
|
+
|
|
916
|
+
expect(dualCompetesWithARouter.links).toEqual([
|
|
917
|
+
{ fromDeviceId: "switch-01-1", toDeviceId: "core-01" },
|
|
918
|
+
]);
|
|
919
|
+
expect(failuresOf(dualCompetesWithARouter)).toEqual([
|
|
920
|
+
{
|
|
921
|
+
siteId: "site-02",
|
|
922
|
+
siteName: "Unit 02",
|
|
923
|
+
reason: "ambiguousParent",
|
|
924
|
+
matchedParentCount: 2,
|
|
925
|
+
matchedChildCount: 2,
|
|
926
|
+
},
|
|
927
|
+
]);
|
|
928
|
+
});
|
|
929
|
+
|
|
930
|
+
it("never draws the same pair twice under site scope", () => {
|
|
931
|
+
/*
|
|
932
|
+
* Sites are disjoint buckets and the builder merges by pair, so a repeated
|
|
933
|
+
* pair would silently become one edge on the map while inflating every
|
|
934
|
+
* count the rule list shows.
|
|
935
|
+
*/
|
|
936
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
937
|
+
scopedRule(),
|
|
938
|
+
units(1, 4, 1, 3),
|
|
939
|
+
);
|
|
940
|
+
|
|
941
|
+
const pairs: Set<string> = new Set<string>(
|
|
942
|
+
outcome.links.map(
|
|
943
|
+
(link: { fromDeviceId: string; toDeviceId: string }) => {
|
|
944
|
+
return `${link.fromDeviceId}->${link.toDeviceId}`;
|
|
945
|
+
},
|
|
946
|
+
),
|
|
947
|
+
);
|
|
948
|
+
|
|
949
|
+
expect(outcome.links).toHaveLength(12);
|
|
950
|
+
expect(pairs.size).toBe(outcome.links.length);
|
|
951
|
+
});
|
|
952
|
+
|
|
953
|
+
it("is exactly project scope when every device is in one site", () => {
|
|
954
|
+
/*
|
|
955
|
+
* One site is one universe, so the partition is the identity and the two
|
|
956
|
+
* scopes cannot disagree. This is what makes the column safe to flip on a
|
|
957
|
+
* single-site project: the map does not move.
|
|
958
|
+
*/
|
|
959
|
+
const devices: Array<LinkRuleDeviceInput> = unit(1, 1, 3);
|
|
960
|
+
|
|
961
|
+
const siteScoped: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
962
|
+
scopedRule(),
|
|
963
|
+
devices,
|
|
964
|
+
);
|
|
965
|
+
const projectScoped: LinkRuleOutcome =
|
|
966
|
+
NetworkDeviceLinkRuleUtil.resolveRule(
|
|
967
|
+
scopedRule({ scope: "Project" }),
|
|
968
|
+
devices,
|
|
969
|
+
);
|
|
970
|
+
|
|
971
|
+
expect(siteScoped.links).toEqual(projectScoped.links);
|
|
972
|
+
expect(siteScoped.matchedChildCount).toBe(projectScoped.matchedChildCount);
|
|
973
|
+
expect(siteScoped.matchedParentCount).toBe(
|
|
974
|
+
projectScoped.matchedParentCount,
|
|
975
|
+
);
|
|
976
|
+
expect(siteScoped.skipReason).toBe(projectScoped.skipReason);
|
|
977
|
+
});
|
|
978
|
+
|
|
979
|
+
it("reports noSiteResolved rather than borrowing one site's reason", () => {
|
|
980
|
+
/*
|
|
981
|
+
* Unit 01 has no router and Unit 02 has two. Reporting either reason for
|
|
982
|
+
* the rule as a whole would be a false statement about the other site, and
|
|
983
|
+
* the mixture is exactly why this reason exists.
|
|
984
|
+
*/
|
|
985
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
986
|
+
scopedRule(),
|
|
987
|
+
[...unit(1, 0, 2), ...unit(2, 2, 1)],
|
|
988
|
+
);
|
|
989
|
+
|
|
990
|
+
expect(outcome.links).toEqual([]);
|
|
991
|
+
expect(outcome.skipReason).toBe("noSiteResolved");
|
|
992
|
+
expect(NetworkDeviceLinkRuleUtil.describeOutcome(outcome)).toBe(
|
|
993
|
+
"No site resolved this rule.",
|
|
994
|
+
);
|
|
995
|
+
expect(failuresOf(outcome)).toHaveLength(2);
|
|
996
|
+
expect(outcome.applicableSiteCount).toBe(2);
|
|
997
|
+
});
|
|
998
|
+
|
|
999
|
+
it("counts every site that drew in the coverage fraction's numerator", () => {
|
|
1000
|
+
/*
|
|
1001
|
+
* "Drawing in X of Y sites" is subtraction, so it is only true if the
|
|
1002
|
+
* sites that failed and the sites that drew partition the applicable set
|
|
1003
|
+
* with nothing double-counted and nothing missing. Checked against the
|
|
1004
|
+
* links themselves rather than against the counters.
|
|
1005
|
+
*/
|
|
1006
|
+
const devices: Array<LinkRuleDeviceInput> = [
|
|
1007
|
+
...unit(1, 1, 2),
|
|
1008
|
+
...unit(2, 1, 1),
|
|
1009
|
+
...unit(3, 0, 2),
|
|
1010
|
+
...unit(4, 2, 1),
|
|
1011
|
+
...unit(5, 1, 0),
|
|
1012
|
+
deviceAt("camera-1", ["label-camera"], "site-06", "Unit 06"),
|
|
1013
|
+
];
|
|
1014
|
+
|
|
1015
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1016
|
+
scopedRule(),
|
|
1017
|
+
devices,
|
|
1018
|
+
);
|
|
1019
|
+
|
|
1020
|
+
const siteOf: Map<string, string> = siteIndexOf(devices);
|
|
1021
|
+
const sitesThatDrew: Set<string> = new Set<string>();
|
|
1022
|
+
for (const link of outcome.links) {
|
|
1023
|
+
sitesThatDrew.add(siteOf.get(link.fromDeviceId)!);
|
|
1024
|
+
sitesThatDrew.add(siteOf.get(link.toDeviceId)!);
|
|
1025
|
+
}
|
|
1026
|
+
|
|
1027
|
+
expect(outcome.applicableSiteCount).toBe(4);
|
|
1028
|
+
expect(failuresOf(outcome)).toHaveLength(2);
|
|
1029
|
+
expect(sitesThatDrew.size).toBe(
|
|
1030
|
+
(outcome.applicableSiteCount || 0) - failuresOf(outcome).length,
|
|
1031
|
+
);
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
it("does not touch the device array it was handed", () => {
|
|
1035
|
+
/*
|
|
1036
|
+
* resolveRules hands the SAME array instance to every rule in turn, so a
|
|
1037
|
+
* resolver that sorted or bucketed in place would make rule two see a
|
|
1038
|
+
* different project from rule one — and the bug would only show up in
|
|
1039
|
+
* projects with more than one rule.
|
|
1040
|
+
*/
|
|
1041
|
+
const devices: Array<LinkRuleDeviceInput> = [
|
|
1042
|
+
...units(1, 3, 1, 2),
|
|
1043
|
+
switchAt("floater-1"),
|
|
1044
|
+
];
|
|
1045
|
+
const before: string = JSON.stringify(devices);
|
|
1046
|
+
|
|
1047
|
+
const alone: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1048
|
+
scopedRule(),
|
|
1049
|
+
devices,
|
|
1050
|
+
);
|
|
1051
|
+
|
|
1052
|
+
const outcomes: Array<LinkRuleOutcome> =
|
|
1053
|
+
NetworkDeviceLinkRuleUtil.resolveRules(
|
|
1054
|
+
[
|
|
1055
|
+
scopedRule({ id: "rule-first", scope: "Project" }),
|
|
1056
|
+
scopedRule({ id: "rule-second", childLabelIds: ["label-camera"] }),
|
|
1057
|
+
scopedRule(),
|
|
1058
|
+
],
|
|
1059
|
+
devices,
|
|
1060
|
+
);
|
|
1061
|
+
|
|
1062
|
+
expect(JSON.stringify(devices)).toBe(before);
|
|
1063
|
+
// Third in a queue, and still the outcome it produces on its own.
|
|
1064
|
+
expect(outcomes[2]).toEqual(alone);
|
|
1065
|
+
});
|
|
1066
|
+
|
|
1067
|
+
it("does not reach across the site tree from an ancestor site", () => {
|
|
1068
|
+
/*
|
|
1069
|
+
* DELIBERATE, not a gap: sites are matched exactly, so a router filed
|
|
1070
|
+
* under the campus does not parent the switches filed under each building
|
|
1071
|
+
* inside it. Resolving up the tree would mean the map silently depends on
|
|
1072
|
+
* a hierarchy the operator can restructure at any time, and every building
|
|
1073
|
+
* would suddenly share one uplink. The rule refuses and names the
|
|
1074
|
+
* buildings instead, which is a fix the operator can actually make.
|
|
1075
|
+
*/
|
|
1076
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1077
|
+
scopedRule(),
|
|
1078
|
+
[
|
|
1079
|
+
routerAt("campus-core", "site-campus", "Campus"),
|
|
1080
|
+
switchAt("switch-a-1", "site-bldg-a", "Building A"),
|
|
1081
|
+
switchAt("switch-a-2", "site-bldg-a", "Building A"),
|
|
1082
|
+
switchAt("switch-b-1", "site-bldg-b", "Building B"),
|
|
1083
|
+
],
|
|
1084
|
+
);
|
|
1085
|
+
|
|
1086
|
+
expect(outcome.links).toEqual([]);
|
|
1087
|
+
expect(outcome.skipReason).toBe("noSiteResolved");
|
|
1088
|
+
expect(failuresOf(outcome)).toEqual([
|
|
1089
|
+
{
|
|
1090
|
+
siteId: "site-bldg-a",
|
|
1091
|
+
siteName: "Building A",
|
|
1092
|
+
reason: "noParentMatched",
|
|
1093
|
+
matchedParentCount: 0,
|
|
1094
|
+
matchedChildCount: 2,
|
|
1095
|
+
},
|
|
1096
|
+
{
|
|
1097
|
+
siteId: "site-bldg-b",
|
|
1098
|
+
siteName: "Building B",
|
|
1099
|
+
reason: "noParentMatched",
|
|
1100
|
+
matchedParentCount: 0,
|
|
1101
|
+
matchedChildCount: 1,
|
|
1102
|
+
},
|
|
1103
|
+
]);
|
|
1104
|
+
// The campus itself had nothing to place, so it is not counted against.
|
|
1105
|
+
expect(outcome.applicableSiteCount).toBe(2);
|
|
1106
|
+
});
|
|
1107
|
+
});
|
|
1108
|
+
|
|
1109
|
+
describe("NetworkDeviceLinkRuleUtil.getWarning", () => {
|
|
1110
|
+
it("stays silent for a rule that is doing its job, in either scope", () => {
|
|
1111
|
+
const projectScoped: LinkRuleOutcome =
|
|
1112
|
+
NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1113
|
+
scopedRule({ scope: "Project" }),
|
|
1114
|
+
unit(1, 1, 3),
|
|
1115
|
+
);
|
|
1116
|
+
const siteScoped: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1117
|
+
scopedRule(),
|
|
1118
|
+
units(1, 6, 1, 2),
|
|
1119
|
+
);
|
|
1120
|
+
|
|
1121
|
+
expect(NetworkDeviceLinkRuleUtil.getWarning(projectScoped)).toBeNull();
|
|
1122
|
+
expect(NetworkDeviceLinkRuleUtil.getWarning(siteScoped)).toBeNull();
|
|
1123
|
+
});
|
|
1124
|
+
|
|
1125
|
+
it("echoes describeOutcome verbatim for every project-scoped skip", () => {
|
|
1126
|
+
/*
|
|
1127
|
+
* The project path is what the topology API used to compute inline, and
|
|
1128
|
+
* the banner and the rule list render the same rule side by side. Asserted
|
|
1129
|
+
* against describeOutcome's OWN return value rather than against copies of
|
|
1130
|
+
* its sentences, so the two can never be edited apart.
|
|
1131
|
+
*/
|
|
1132
|
+
const skips: Array<ScopeScenario> = [
|
|
1133
|
+
{
|
|
1134
|
+
label: "disabled",
|
|
1135
|
+
rule: scopedRule({ scope: "Project", isEnabled: false }),
|
|
1136
|
+
devices: unit(1, 1, 2),
|
|
1137
|
+
},
|
|
1138
|
+
{
|
|
1139
|
+
label: "noChildLabels",
|
|
1140
|
+
rule: scopedRule({ scope: "Project", childLabelIds: [] }),
|
|
1141
|
+
devices: unit(1, 1, 2),
|
|
1142
|
+
},
|
|
1143
|
+
{
|
|
1144
|
+
label: "noParentLabels",
|
|
1145
|
+
rule: scopedRule({ scope: "Project", parentLabelIds: [] }),
|
|
1146
|
+
devices: unit(1, 1, 2),
|
|
1147
|
+
},
|
|
1148
|
+
{
|
|
1149
|
+
label: "noParentMatched",
|
|
1150
|
+
rule: scopedRule({ scope: "Project" }),
|
|
1151
|
+
devices: unit(1, 0, 2),
|
|
1152
|
+
},
|
|
1153
|
+
{
|
|
1154
|
+
label: "ambiguousParent",
|
|
1155
|
+
rule: scopedRule({ scope: "Project" }),
|
|
1156
|
+
devices: units(1, 2, 1, 2),
|
|
1157
|
+
},
|
|
1158
|
+
{
|
|
1159
|
+
label: "noChildrenMatched",
|
|
1160
|
+
rule: scopedRule({ scope: "Project" }),
|
|
1161
|
+
devices: unit(1, 1, 0),
|
|
1162
|
+
},
|
|
1163
|
+
];
|
|
1164
|
+
|
|
1165
|
+
for (const scenario of skips) {
|
|
1166
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1167
|
+
scenario.rule,
|
|
1168
|
+
scenario.devices,
|
|
1169
|
+
);
|
|
1170
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1171
|
+
|
|
1172
|
+
expect({ label: scenario.label, reason: warning.reason }).toEqual({
|
|
1173
|
+
label: scenario.label,
|
|
1174
|
+
reason: outcome.skipReason,
|
|
1175
|
+
});
|
|
1176
|
+
expect({ label: scenario.label, message: warning.message }).toEqual({
|
|
1177
|
+
label: scenario.label,
|
|
1178
|
+
message: NetworkDeviceLinkRuleUtil.describeOutcome(outcome),
|
|
1179
|
+
});
|
|
1180
|
+
expect(warning.ruleId).toBe("rule-uplink");
|
|
1181
|
+
expect(warning.ruleName).toBe("Switch uplinks");
|
|
1182
|
+
}
|
|
1183
|
+
});
|
|
1184
|
+
|
|
1185
|
+
it("warns about the fourteenth unit while the other thirteen keep drawing", () => {
|
|
1186
|
+
/*
|
|
1187
|
+
* The regression this whole change is for. The warning predicate used to
|
|
1188
|
+
* be `links.length === 0`, which this outcome passes — 39 links — while
|
|
1189
|
+
* the operator's fourteenth building is genuinely broken. Silence here
|
|
1190
|
+
* means a site that has been quietly missing its uplinks since somebody
|
|
1191
|
+
* racked a second router in it.
|
|
1192
|
+
*/
|
|
1193
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1194
|
+
scopedRule(),
|
|
1195
|
+
[...units(1, 13, 1, 3), ...unit(14, 2, 3)],
|
|
1196
|
+
);
|
|
1197
|
+
|
|
1198
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1199
|
+
|
|
1200
|
+
expect(outcome.links.length).toBeGreaterThan(0);
|
|
1201
|
+
expect(outcome.links).toHaveLength(39);
|
|
1202
|
+
expect(warning.reason).toBe("ambiguousParent");
|
|
1203
|
+
expect(warning.message).toContain("Drawing in 13 of 14 sites.");
|
|
1204
|
+
expect(warning.message).toContain("Unit 14");
|
|
1205
|
+
expect(warning.message).toContain("2 devices carry the parent labels");
|
|
1206
|
+
});
|
|
1207
|
+
|
|
1208
|
+
it("folds mixed failures into one bullet, ambiguity before missing parents", () => {
|
|
1209
|
+
/*
|
|
1210
|
+
* One bullet per rule, never one per site: the banner's length has to be
|
|
1211
|
+
* bounded by the number of rules the operator wrote, not by the number of
|
|
1212
|
+
* sites they own. Ambiguity leads because it is the more specific
|
|
1213
|
+
* complaint — "you have two of these" is a five-second fix, "you have
|
|
1214
|
+
* none" may be a truck roll.
|
|
1215
|
+
*/
|
|
1216
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1217
|
+
scopedRule(),
|
|
1218
|
+
[...unit(1, 2, 2), ...unit(2, 0, 1), ...unit(3, 0, 2), ...unit(4, 1, 1)],
|
|
1219
|
+
);
|
|
1220
|
+
|
|
1221
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1222
|
+
|
|
1223
|
+
expect(failuresOf(outcome)).toHaveLength(3);
|
|
1224
|
+
expect(warning.reason).toBe("ambiguousParent");
|
|
1225
|
+
|
|
1226
|
+
const ambiguity: number = warning.message.indexOf(
|
|
1227
|
+
"2 devices carry the parent labels in Unit 01.",
|
|
1228
|
+
);
|
|
1229
|
+
const missing: number = warning.message.indexOf(
|
|
1230
|
+
"No device carries the parent labels in Unit 02 and Unit 03,",
|
|
1231
|
+
);
|
|
1232
|
+
|
|
1233
|
+
expect(ambiguity).toBeGreaterThan(-1);
|
|
1234
|
+
expect(missing).toBeGreaterThan(-1);
|
|
1235
|
+
expect(ambiguity).toBeLessThan(missing);
|
|
1236
|
+
expect(warning.message).toContain("3 devices there have no uplink.");
|
|
1237
|
+
});
|
|
1238
|
+
|
|
1239
|
+
it("uses the project's own sentence when nothing carries the child labels", () => {
|
|
1240
|
+
/*
|
|
1241
|
+
* No site failed and nothing was stranded, so there is nothing site-shaped
|
|
1242
|
+
* to say — and inventing a site-scoped phrasing here would give the
|
|
1243
|
+
* operator two different sentences for the same situation depending on a
|
|
1244
|
+
* column they may not know they set.
|
|
1245
|
+
*/
|
|
1246
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1247
|
+
scopedRule(),
|
|
1248
|
+
units(1, 3, 1, 0),
|
|
1249
|
+
);
|
|
1250
|
+
|
|
1251
|
+
expect(outcome.skipReason).toBe("noChildrenMatched");
|
|
1252
|
+
expect(warningOf(outcome).message).toBe(
|
|
1253
|
+
"No device carries the child labels yet.",
|
|
1254
|
+
);
|
|
1255
|
+
});
|
|
1256
|
+
|
|
1257
|
+
it("opens with the headline that matches whether anything drew at all", () => {
|
|
1258
|
+
const nothingDrew: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1259
|
+
scopedRule(),
|
|
1260
|
+
units(1, 2, 0, 1),
|
|
1261
|
+
);
|
|
1262
|
+
const somethingDrew: LinkRuleOutcome =
|
|
1263
|
+
NetworkDeviceLinkRuleUtil.resolveRule(scopedRule(), [
|
|
1264
|
+
...unit(1, 1, 2),
|
|
1265
|
+
...unit(2, 0, 1),
|
|
1266
|
+
]);
|
|
1267
|
+
|
|
1268
|
+
expect(
|
|
1269
|
+
warningOf(nothingDrew).message.startsWith("No site resolved this rule."),
|
|
1270
|
+
).toBe(true);
|
|
1271
|
+
expect(warningOf(somethingDrew).message.startsWith("Drawing in")).toBe(
|
|
1272
|
+
true,
|
|
1273
|
+
);
|
|
1274
|
+
expect(warningOf(somethingDrew).message).toContain(
|
|
1275
|
+
"Drawing in 1 of 2 sites.",
|
|
1276
|
+
);
|
|
1277
|
+
});
|
|
1278
|
+
|
|
1279
|
+
it("omits the coverage fraction when only one site applies", () => {
|
|
1280
|
+
/*
|
|
1281
|
+
* "Drawing in 1 of 1 sites" is a sentence that costs the reader time and
|
|
1282
|
+
* tells them nothing. The unsited clause still has to be emitted, so this
|
|
1283
|
+
* pins that the two are independent rather than one gating the other.
|
|
1284
|
+
*/
|
|
1285
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1286
|
+
scopedRule(),
|
|
1287
|
+
[...unit(1, 1, 2), switchAt("floater-1"), switchAt("floater-2")],
|
|
1288
|
+
);
|
|
1289
|
+
|
|
1290
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1291
|
+
|
|
1292
|
+
expect(outcome.applicableSiteCount).toBe(1);
|
|
1293
|
+
expect(outcome.links).toHaveLength(2);
|
|
1294
|
+
expect(warning.reason).toBe("devicesWithoutSite");
|
|
1295
|
+
expect(warning.message).toBe(
|
|
1296
|
+
"2 devices carrying the child labels are not assigned to a site, so this site-scoped rule skips them.",
|
|
1297
|
+
);
|
|
1298
|
+
expect(warning.message).not.toContain("Drawing in");
|
|
1299
|
+
expect(warning.message).not.toContain("of 1 sites");
|
|
1300
|
+
});
|
|
1301
|
+
|
|
1302
|
+
it("names three sites and counts the rest exactly", () => {
|
|
1303
|
+
/*
|
|
1304
|
+
* Three names is enough to recognise a pattern; fifteen is a wall of text
|
|
1305
|
+
* in a banner. The COUNT stays exact even though the names are elided, so
|
|
1306
|
+
* the operator still knows the true blast radius.
|
|
1307
|
+
*/
|
|
1308
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1309
|
+
scopedRule(),
|
|
1310
|
+
units(1, 15, 0, 1),
|
|
1311
|
+
);
|
|
1312
|
+
|
|
1313
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1314
|
+
const allNames: Array<string> = [];
|
|
1315
|
+
for (let index: number = 1; index <= 15; index++) {
|
|
1316
|
+
allNames.push(`Unit ${twoDigit(index)}`);
|
|
1317
|
+
}
|
|
1318
|
+
const named: Array<string> = allNames.filter((name: string): boolean => {
|
|
1319
|
+
return warning.message.includes(name);
|
|
1320
|
+
});
|
|
1321
|
+
|
|
1322
|
+
expect(failuresOf(outcome)).toHaveLength(15);
|
|
1323
|
+
expect(named).toEqual(["Unit 01", "Unit 02", "Unit 03"]);
|
|
1324
|
+
expect(warning.message).toContain("and 12 more sites");
|
|
1325
|
+
expect(warning.message).toContain("15 devices there have no uplink.");
|
|
1326
|
+
});
|
|
1327
|
+
|
|
1328
|
+
it("says 'and 1 more site' rather than 'and 1 more sites'", () => {
|
|
1329
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1330
|
+
scopedRule(),
|
|
1331
|
+
units(1, 4, 0, 1),
|
|
1332
|
+
);
|
|
1333
|
+
|
|
1334
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1335
|
+
|
|
1336
|
+
expect(warning.message).toContain(
|
|
1337
|
+
"Unit 01, Unit 02, Unit 03 and 1 more site,",
|
|
1338
|
+
);
|
|
1339
|
+
expect(warning.message).not.toContain("more sites");
|
|
1340
|
+
});
|
|
1341
|
+
|
|
1342
|
+
it("describes a site with no name instead of printing its id", () => {
|
|
1343
|
+
/*
|
|
1344
|
+
* The id is a database key: it means nothing to the reader and, printed in
|
|
1345
|
+
* a banner, it is the one part of the sentence they might paste into a
|
|
1346
|
+
* ticket. A site with no name is missing a name, which is a different
|
|
1347
|
+
* problem from the one this warning is about.
|
|
1348
|
+
*/
|
|
1349
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1350
|
+
scopedRule(),
|
|
1351
|
+
[switchAt("switch-x", "site-9f3c-never-printed")],
|
|
1352
|
+
);
|
|
1353
|
+
|
|
1354
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1355
|
+
|
|
1356
|
+
expect(warning.message).toBe(
|
|
1357
|
+
"No site resolved this rule. No device carries the parent labels in an unnamed site, so 1 device there has no uplink.",
|
|
1358
|
+
);
|
|
1359
|
+
expect(warning.message).not.toContain("site-9f3c-never-printed");
|
|
1360
|
+
});
|
|
1361
|
+
|
|
1362
|
+
it("truncates a site name past 40 characters to 39 and an ellipsis", () => {
|
|
1363
|
+
/*
|
|
1364
|
+
* Site names are ShortText and run to 100 characters. Three of them
|
|
1365
|
+
* unabridged would be a 300-character site list inside a one-line banner.
|
|
1366
|
+
*/
|
|
1367
|
+
const longName: string = "Manufacturing Plant Seven East Annex Riser Room";
|
|
1368
|
+
|
|
1369
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1370
|
+
scopedRule(),
|
|
1371
|
+
[switchAt("switch-x", "site-long", longName)],
|
|
1372
|
+
);
|
|
1373
|
+
|
|
1374
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1375
|
+
|
|
1376
|
+
expect(longName.length).toBeGreaterThan(40);
|
|
1377
|
+
expect(warning.message).toContain(
|
|
1378
|
+
"Manufacturing Plant Seven East Annex Ri…",
|
|
1379
|
+
);
|
|
1380
|
+
expect(warning.message).not.toContain(longName);
|
|
1381
|
+
});
|
|
1382
|
+
|
|
1383
|
+
it("produces a byte-identical message however the devices are ordered", () => {
|
|
1384
|
+
/*
|
|
1385
|
+
* The live view refetches every sixty seconds and the device query has no
|
|
1386
|
+
* ORDER BY, so an unsorted site list would reshuffle the banner on every
|
|
1387
|
+
* poll and read as a change that did not happen. Links keep following
|
|
1388
|
+
* first appearance in the array — that is the map's stable draw order, not
|
|
1389
|
+
* the banner's.
|
|
1390
|
+
*/
|
|
1391
|
+
const inOrder: Array<LinkRuleDeviceInput> = [
|
|
1392
|
+
...unit(5, 1, 1),
|
|
1393
|
+
...unit(3, 0, 1),
|
|
1394
|
+
...unit(1, 1, 1),
|
|
1395
|
+
...unit(4, 2, 1),
|
|
1396
|
+
...unit(2, 0, 1),
|
|
1397
|
+
];
|
|
1398
|
+
const shuffled: Array<LinkRuleDeviceInput> = [
|
|
1399
|
+
...unit(1, 1, 1),
|
|
1400
|
+
...unit(2, 0, 1),
|
|
1401
|
+
...unit(4, 2, 1),
|
|
1402
|
+
...unit(5, 1, 1),
|
|
1403
|
+
...unit(3, 0, 1),
|
|
1404
|
+
];
|
|
1405
|
+
|
|
1406
|
+
const first: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1407
|
+
scopedRule(),
|
|
1408
|
+
inOrder,
|
|
1409
|
+
);
|
|
1410
|
+
const second: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1411
|
+
scopedRule(),
|
|
1412
|
+
shuffled,
|
|
1413
|
+
);
|
|
1414
|
+
|
|
1415
|
+
expect(warningOf(first).message).toBe(warningOf(second).message);
|
|
1416
|
+
expect(warningOf(first).message).toContain(
|
|
1417
|
+
"Unit 02 and Unit 03, so 2 devices there have no uplink.",
|
|
1418
|
+
);
|
|
1419
|
+
|
|
1420
|
+
expect(first.links).toEqual([
|
|
1421
|
+
{ fromDeviceId: "switch-05-1", toDeviceId: "router-05-1" },
|
|
1422
|
+
{ fromDeviceId: "switch-01-1", toDeviceId: "router-01-1" },
|
|
1423
|
+
]);
|
|
1424
|
+
expect(second.links).toEqual([
|
|
1425
|
+
{ fromDeviceId: "switch-01-1", toDeviceId: "router-01-1" },
|
|
1426
|
+
{ fromDeviceId: "switch-05-1", toDeviceId: "router-05-1" },
|
|
1427
|
+
]);
|
|
1428
|
+
});
|
|
1429
|
+
|
|
1430
|
+
it("reads 'has' for one stranded device and 'have' for several", () => {
|
|
1431
|
+
const one: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1432
|
+
scopedRule(),
|
|
1433
|
+
[...unit(1, 1, 1), ...unit(2, 0, 1)],
|
|
1434
|
+
);
|
|
1435
|
+
const several: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1436
|
+
scopedRule(),
|
|
1437
|
+
[...unit(1, 1, 1), ...unit(2, 0, 4)],
|
|
1438
|
+
);
|
|
1439
|
+
|
|
1440
|
+
expect(warningOf(one).message).toContain(
|
|
1441
|
+
"so 1 device there has no uplink.",
|
|
1442
|
+
);
|
|
1443
|
+
expect(warningOf(several).message).toContain(
|
|
1444
|
+
"so 4 devices there have no uplink.",
|
|
1445
|
+
);
|
|
1446
|
+
});
|
|
1447
|
+
|
|
1448
|
+
it("keeps even the worst case short enough to read", () => {
|
|
1449
|
+
/*
|
|
1450
|
+
* The banner is one line per rule. Thirty failing sites of both kinds,
|
|
1451
|
+
* hundred-character names and stranded unsited devices is the loudest a
|
|
1452
|
+
* single rule can be, and it still has to fit somewhere a human will read
|
|
1453
|
+
* it rather than scroll past.
|
|
1454
|
+
*/
|
|
1455
|
+
const hundredCharName: (index: number) => string = (
|
|
1456
|
+
index: number,
|
|
1457
|
+
): string => {
|
|
1458
|
+
const base: string = `Unit ${twoDigit(index)} Distribution Annex `;
|
|
1459
|
+
return (base + "0123456789".repeat(10)).slice(0, 100);
|
|
1460
|
+
};
|
|
1461
|
+
|
|
1462
|
+
const devices: Array<LinkRuleDeviceInput> = [];
|
|
1463
|
+
for (let index: number = 1; index <= 15; index++) {
|
|
1464
|
+
const siteId: string = `ambiguous-${twoDigit(index)}`;
|
|
1465
|
+
const siteName: string = hundredCharName(index);
|
|
1466
|
+
devices.push(routerAt(`amb-router-a-${index}`, siteId, siteName));
|
|
1467
|
+
devices.push(routerAt(`amb-router-b-${index}`, siteId, siteName));
|
|
1468
|
+
devices.push(switchAt(`amb-switch-${index}`, siteId, siteName));
|
|
1469
|
+
}
|
|
1470
|
+
for (let index: number = 16; index <= 30; index++) {
|
|
1471
|
+
const siteId: string = `parentless-${twoDigit(index)}`;
|
|
1472
|
+
const siteName: string = hundredCharName(index);
|
|
1473
|
+
devices.push(switchAt(`orphan-switch-${index}`, siteId, siteName));
|
|
1474
|
+
}
|
|
1475
|
+
devices.push(switchAt("floater-1"));
|
|
1476
|
+
devices.push(switchAt("floater-2"));
|
|
1477
|
+
|
|
1478
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1479
|
+
scopedRule(),
|
|
1480
|
+
devices,
|
|
1481
|
+
);
|
|
1482
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1483
|
+
|
|
1484
|
+
expect(hundredCharName(1)).toHaveLength(100);
|
|
1485
|
+
expect(failuresOf(outcome)).toHaveLength(30);
|
|
1486
|
+
expect(outcome.unsitedChildDeviceCount).toBe(2);
|
|
1487
|
+
expect(warning.message.length).toBeLessThanOrEqual(700);
|
|
1488
|
+
});
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
/*
|
|
1492
|
+
* ISSUE #3321 — the audit list.
|
|
1493
|
+
*
|
|
1494
|
+
* The sentence a failing site-scoped rule produces names three sites and then
|
|
1495
|
+
* a number: "…in WB Franchise Unit 0005, WB Franchise Unit 0047, WB Franchise
|
|
1496
|
+
* Unit 0069 and 694 more sites". At fourteen sites that is a summary. At 949
|
|
1497
|
+
* it is a dead end — the operator has been told the size of the problem and
|
|
1498
|
+
* nothing about where it is. These rows are what the banner expands into.
|
|
1499
|
+
*/
|
|
1500
|
+
describe("NetworkDeviceLinkRuleUtil.getWarning — the failing-site list", () => {
|
|
1501
|
+
type ResolvedWarningSite = NonNullable<ResolvedWarning["sites"]>[number];
|
|
1502
|
+
|
|
1503
|
+
/** Zero padded past three digits so 949 sites still sort as text. */
|
|
1504
|
+
const fourDigit: (value: number) => string = (value: number): string => {
|
|
1505
|
+
return `${value}`.padStart(4, "0");
|
|
1506
|
+
};
|
|
1507
|
+
|
|
1508
|
+
/**
|
|
1509
|
+
* `franchise(index, routers, switches)` — one WB-style unit, named the way
|
|
1510
|
+
* the reporter's are so the sort order under test is the one they see.
|
|
1511
|
+
*/
|
|
1512
|
+
const franchise: (
|
|
1513
|
+
index: number,
|
|
1514
|
+
routerCount: number,
|
|
1515
|
+
switchCount: number,
|
|
1516
|
+
) => Array<LinkRuleDeviceInput> = (
|
|
1517
|
+
index: number,
|
|
1518
|
+
routerCount: number,
|
|
1519
|
+
switchCount: number,
|
|
1520
|
+
): Array<LinkRuleDeviceInput> => {
|
|
1521
|
+
const siteId: string = `wb-${fourDigit(index)}`;
|
|
1522
|
+
const siteName: string = `WB Franchise Unit ${fourDigit(index)}`;
|
|
1523
|
+
const devices: Array<LinkRuleDeviceInput> = [];
|
|
1524
|
+
for (let router: number = 1; router <= routerCount; router++) {
|
|
1525
|
+
devices.push(routerAt(`wb-router-${index}-${router}`, siteId, siteName));
|
|
1526
|
+
}
|
|
1527
|
+
for (let leaf: number = 1; leaf <= switchCount; leaf++) {
|
|
1528
|
+
devices.push(switchAt(`wb-switch-${index}-${leaf}`, siteId, siteName));
|
|
1529
|
+
}
|
|
1530
|
+
return devices;
|
|
1531
|
+
};
|
|
1532
|
+
|
|
1533
|
+
const sitesOf: (outcome: LinkRuleOutcome) => Array<ResolvedWarningSite> = (
|
|
1534
|
+
outcome: LinkRuleOutcome,
|
|
1535
|
+
): Array<ResolvedWarningSite> => {
|
|
1536
|
+
return warningOf(outcome).sites || [];
|
|
1537
|
+
};
|
|
1538
|
+
|
|
1539
|
+
it("lists the sites the sentence could only count", () => {
|
|
1540
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1541
|
+
scopedRule(),
|
|
1542
|
+
[
|
|
1543
|
+
...franchise(5, 1, 2),
|
|
1544
|
+
// No router: three switches with nowhere to go.
|
|
1545
|
+
...franchise(47, 0, 3),
|
|
1546
|
+
...franchise(69, 0, 1),
|
|
1547
|
+
],
|
|
1548
|
+
);
|
|
1549
|
+
|
|
1550
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1551
|
+
|
|
1552
|
+
expect(warning.siteCount).toBe(2);
|
|
1553
|
+
expect(warning.sites).toEqual([
|
|
1554
|
+
{
|
|
1555
|
+
siteId: "wb-0047",
|
|
1556
|
+
siteName: "WB Franchise Unit 0047",
|
|
1557
|
+
reason: "noParentMatched",
|
|
1558
|
+
strandedDeviceCount: 3,
|
|
1559
|
+
matchedParentCount: 0,
|
|
1560
|
+
},
|
|
1561
|
+
{
|
|
1562
|
+
siteId: "wb-0069",
|
|
1563
|
+
siteName: "WB Franchise Unit 0069",
|
|
1564
|
+
reason: "noParentMatched",
|
|
1565
|
+
strandedDeviceCount: 1,
|
|
1566
|
+
matchedParentCount: 0,
|
|
1567
|
+
},
|
|
1568
|
+
]);
|
|
1569
|
+
});
|
|
1570
|
+
|
|
1571
|
+
it("carries each site's own reason, not the rule's headline one", () => {
|
|
1572
|
+
/*
|
|
1573
|
+
* The rule's `reason` is the highest-ranked condition present, so a mixed
|
|
1574
|
+
* failure reads "ambiguousParent" — which is wrong about every one of the
|
|
1575
|
+
* parentless sites. Per-site reasons are the whole reason the list is
|
|
1576
|
+
* worth sending: the two faults need opposite fixes (delete a label vs.
|
|
1577
|
+
* add one), and a list that got them backwards would be worse than none.
|
|
1578
|
+
*/
|
|
1579
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1580
|
+
scopedRule(),
|
|
1581
|
+
[
|
|
1582
|
+
...franchise(1, 1, 1),
|
|
1583
|
+
// Two routers in one unit.
|
|
1584
|
+
...franchise(2, 2, 1),
|
|
1585
|
+
// None in the other.
|
|
1586
|
+
...franchise(3, 0, 2),
|
|
1587
|
+
],
|
|
1588
|
+
);
|
|
1589
|
+
|
|
1590
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1591
|
+
|
|
1592
|
+
expect(warning.reason).toBe("ambiguousParent");
|
|
1593
|
+
expect(
|
|
1594
|
+
(warning.sites || []).map((site: ResolvedWarningSite) => {
|
|
1595
|
+
return [site.siteName, site.reason, site.matchedParentCount];
|
|
1596
|
+
}),
|
|
1597
|
+
).toEqual([
|
|
1598
|
+
["WB Franchise Unit 0002", "ambiguousParent", 2],
|
|
1599
|
+
["WB Franchise Unit 0003", "noParentMatched", 0],
|
|
1600
|
+
]);
|
|
1601
|
+
});
|
|
1602
|
+
|
|
1603
|
+
it("orders the list exactly as the sentence names them", () => {
|
|
1604
|
+
/*
|
|
1605
|
+
* The first rows of the list must be the sites the sentence already
|
|
1606
|
+
* named, or the two read as different sets of buildings. Both go through
|
|
1607
|
+
* one sort for that reason.
|
|
1608
|
+
*/
|
|
1609
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1610
|
+
scopedRule(),
|
|
1611
|
+
[
|
|
1612
|
+
...franchise(1, 1, 1),
|
|
1613
|
+
...franchise(69, 0, 1),
|
|
1614
|
+
...franchise(5, 0, 1),
|
|
1615
|
+
...franchise(47, 0, 1),
|
|
1616
|
+
...franchise(12, 0, 1),
|
|
1617
|
+
],
|
|
1618
|
+
);
|
|
1619
|
+
|
|
1620
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1621
|
+
|
|
1622
|
+
expect(warning.message).toContain(
|
|
1623
|
+
"WB Franchise Unit 0005, WB Franchise Unit 0012, WB Franchise Unit 0047 and 1 more site",
|
|
1624
|
+
);
|
|
1625
|
+
expect(
|
|
1626
|
+
(warning.sites || []).map((site: ResolvedWarningSite) => {
|
|
1627
|
+
return site.siteName;
|
|
1628
|
+
}),
|
|
1629
|
+
).toEqual([
|
|
1630
|
+
"WB Franchise Unit 0005",
|
|
1631
|
+
"WB Franchise Unit 0012",
|
|
1632
|
+
"WB Franchise Unit 0047",
|
|
1633
|
+
"WB Franchise Unit 0069",
|
|
1634
|
+
]);
|
|
1635
|
+
});
|
|
1636
|
+
|
|
1637
|
+
it("stays byte-identical however the device query ordered its rows", () => {
|
|
1638
|
+
/*
|
|
1639
|
+
* The live view refetches every sixty seconds; a reshuffling list reads
|
|
1640
|
+
* as a change that did not happen.
|
|
1641
|
+
*/
|
|
1642
|
+
const forwards: Array<LinkRuleDeviceInput> = [
|
|
1643
|
+
...franchise(3, 0, 1),
|
|
1644
|
+
...franchise(1, 0, 1),
|
|
1645
|
+
...franchise(2, 0, 1),
|
|
1646
|
+
];
|
|
1647
|
+
const backwards: Array<LinkRuleDeviceInput> = [
|
|
1648
|
+
...franchise(2, 0, 1),
|
|
1649
|
+
...franchise(3, 0, 1),
|
|
1650
|
+
...franchise(1, 0, 1),
|
|
1651
|
+
];
|
|
1652
|
+
|
|
1653
|
+
expect(
|
|
1654
|
+
sitesOf(NetworkDeviceLinkRuleUtil.resolveRule(scopedRule(), forwards)),
|
|
1655
|
+
).toEqual(
|
|
1656
|
+
sitesOf(NetworkDeviceLinkRuleUtil.resolveRule(scopedRule(), backwards)),
|
|
1657
|
+
);
|
|
1658
|
+
});
|
|
1659
|
+
|
|
1660
|
+
it("names a site with no name nothing at all rather than guessing one", () => {
|
|
1661
|
+
/*
|
|
1662
|
+
* A device can carry a siteId whose name never came back — the name rides
|
|
1663
|
+
* on a relation read. The row still has to be sent: its id is what the
|
|
1664
|
+
* operator navigates by.
|
|
1665
|
+
*/
|
|
1666
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1667
|
+
scopedRule(),
|
|
1668
|
+
[...franchise(1, 1, 1), switchAt("nameless-switch", "site-nameless")],
|
|
1669
|
+
);
|
|
1670
|
+
|
|
1671
|
+
expect(sitesOf(outcome)).toEqual([
|
|
1672
|
+
{
|
|
1673
|
+
siteId: "site-nameless",
|
|
1674
|
+
siteName: undefined,
|
|
1675
|
+
reason: "noParentMatched",
|
|
1676
|
+
strandedDeviceCount: 1,
|
|
1677
|
+
matchedParentCount: 0,
|
|
1678
|
+
},
|
|
1679
|
+
]);
|
|
1680
|
+
});
|
|
1681
|
+
|
|
1682
|
+
it("caps the list but never the count", () => {
|
|
1683
|
+
/*
|
|
1684
|
+
* The cap is what keeps a 60-second poll from carrying a megabyte of site
|
|
1685
|
+
* names. Reporting sites.length as the number of broken sites is then a
|
|
1686
|
+
* bug waiting to happen, so the exact total rides alongside.
|
|
1687
|
+
*/
|
|
1688
|
+
const devices: Array<LinkRuleDeviceInput> = [...franchise(1, 1, 1)];
|
|
1689
|
+
for (let index: number = 2; index <= 400; index++) {
|
|
1690
|
+
devices.push(...franchise(index, 0, 1));
|
|
1691
|
+
}
|
|
1692
|
+
|
|
1693
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1694
|
+
scopedRule(),
|
|
1695
|
+
devices,
|
|
1696
|
+
);
|
|
1697
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1698
|
+
|
|
1699
|
+
expect(warning.siteCount).toBe(399);
|
|
1700
|
+
expect(warning.sites).toHaveLength(
|
|
1701
|
+
NetworkDeviceLinkRuleUtil.MAX_LISTED_SITES,
|
|
1702
|
+
);
|
|
1703
|
+
// The cap keeps the sentence's own three sites, which are the first rows.
|
|
1704
|
+
expect((warning.sites || [])[0]!.siteName).toBe("WB Franchise Unit 0002");
|
|
1705
|
+
});
|
|
1706
|
+
|
|
1707
|
+
it("says nothing about sites when the rule is project scoped", () => {
|
|
1708
|
+
/*
|
|
1709
|
+
* A project-scoped outcome has no site dimension at all, and inventing an
|
|
1710
|
+
* empty list for it would put a "0 sites need attention" control on a
|
|
1711
|
+
* banner that is not about sites.
|
|
1712
|
+
*/
|
|
1713
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1714
|
+
scopedRule({ scope: "Project" }),
|
|
1715
|
+
[...franchise(1, 1, 1), ...franchise(2, 1, 1)],
|
|
1716
|
+
);
|
|
1717
|
+
|
|
1718
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1719
|
+
|
|
1720
|
+
expect(warning.reason).toBe("ambiguousParent");
|
|
1721
|
+
expect(warning.sites).toBeUndefined();
|
|
1722
|
+
expect(warning.siteCount).toBeUndefined();
|
|
1723
|
+
});
|
|
1724
|
+
|
|
1725
|
+
it("says nothing about sites when only unsited devices are wrong", () => {
|
|
1726
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1727
|
+
scopedRule(),
|
|
1728
|
+
[...franchise(1, 1, 1), switchAt("spare-switch")],
|
|
1729
|
+
);
|
|
1730
|
+
|
|
1731
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1732
|
+
|
|
1733
|
+
expect(warning.reason).toBe("devicesWithoutSite");
|
|
1734
|
+
expect(warning.sites).toBeUndefined();
|
|
1735
|
+
expect(warning.siteCount).toBeUndefined();
|
|
1736
|
+
});
|
|
1737
|
+
|
|
1738
|
+
it("keeps the list and the sentence telling the same story at 949 sites", () => {
|
|
1739
|
+
/*
|
|
1740
|
+
* The reporter's estate, with the truncation removed: 949 units, of which
|
|
1741
|
+
* 697 have no router. The two numbers an operator reads — the coverage
|
|
1742
|
+
* fraction and the stranded device total — have to be reproducible from
|
|
1743
|
+
* the rows, or the list is a second opinion rather than a breakdown.
|
|
1744
|
+
*/
|
|
1745
|
+
const devices: Array<LinkRuleDeviceInput> = [];
|
|
1746
|
+
for (let index: number = 1; index <= 252; index++) {
|
|
1747
|
+
devices.push(...franchise(index, 1, 3));
|
|
1748
|
+
}
|
|
1749
|
+
for (let index: number = 253; index <= 949; index++) {
|
|
1750
|
+
devices.push(...franchise(index, 0, 3));
|
|
1751
|
+
}
|
|
1752
|
+
|
|
1753
|
+
const outcome: LinkRuleOutcome = NetworkDeviceLinkRuleUtil.resolveRule(
|
|
1754
|
+
scopedRule(),
|
|
1755
|
+
devices,
|
|
1756
|
+
);
|
|
1757
|
+
const warning: ResolvedWarning = warningOf(outcome);
|
|
1758
|
+
|
|
1759
|
+
expect(outcome.links).toHaveLength(252 * 3);
|
|
1760
|
+
expect(outcome.applicableSiteCount).toBe(949);
|
|
1761
|
+
expect(warning.message).toContain("Drawing in 252 of 949 sites.");
|
|
1762
|
+
expect(warning.message).toContain("so 2091 devices there have no uplink.");
|
|
1763
|
+
|
|
1764
|
+
expect(warning.siteCount).toBe(697);
|
|
1765
|
+
expect(warning.sites).toHaveLength(
|
|
1766
|
+
NetworkDeviceLinkRuleUtil.MAX_LISTED_SITES,
|
|
1767
|
+
);
|
|
1768
|
+
for (const site of warning.sites || []) {
|
|
1769
|
+
expect(site.reason).toBe("noParentMatched");
|
|
1770
|
+
expect(site.strandedDeviceCount).toBe(3);
|
|
1771
|
+
expect(site.matchedParentCount).toBe(0);
|
|
1772
|
+
}
|
|
1773
|
+
});
|
|
1774
|
+
});
|