@oneuptime/common 12.0.23 → 12.0.25
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/Span.ts +101 -0
- package/Models/DatabaseModels/DetectionRule.ts +55 -0
- package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +27 -7
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +67 -0
- package/Models/DatabaseModels/Project.ts +68 -0
- package/Models/DatabaseModels/User.ts +68 -0
- package/Server/API/BaseAPI.ts +0 -24
- package/Server/API/SlackAPI.ts +0 -2
- package/Server/API/TelemetryAPI.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/GzipRequestBody.ts +311 -0
- package/Server/Middleware/MultipartFormData.ts +87 -3
- package/Server/Middleware/SlackAuthorization.ts +96 -18
- package/Server/Services/DetectionRuleService.ts +73 -6
- package/Server/Services/Index.ts +205 -0
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
- package/Server/Services/NetworkDeviceService.ts +148 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
- package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
- package/Server/Services/ProjectService.ts +29 -19
- package/Server/Services/SecurityEventService.ts +90 -13
- package/Server/Services/StatusPageResourceService.ts +223 -0
- package/Server/Services/TelemetryAttributeService.ts +17 -0
- package/Server/Services/UserService.ts +4 -6
- package/Server/Types/Markdown.ts +161 -67
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
- package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
- package/Server/Utils/Attribution.ts +4 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
- package/Server/Utils/StartServer.ts +12 -32
- package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
- package/Server/Utils/Telemetry/LlmMetricSpend.ts +181 -0
- package/Server/Utils/Telemetry/LlmSpan.ts +46 -0
- package/Server/Utils/Workspace/Slack/Actions/Auth.ts +0 -12
- package/Server/Views/Partials/AnalyticsConsent.ejs +159 -102
- package/Server/Views/Partials/Head.ejs +2 -1
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +776 -38
- package/Tests/App/Dashboard/CorrelateFilterBuilder.test.tsx +359 -0
- package/Tests/App/Dashboard/CorrelateGraph.test.tsx +865 -0
- package/Tests/App/Dashboard/CorrelationGraph.test.ts +346 -0
- package/Tests/App/Dashboard/CriteriaFilterConditionDropdown.test.tsx +256 -0
- package/Tests/App/Dashboard/CriteriaFilterDefaults.test.ts +738 -0
- package/Tests/App/Dashboard/CriteriaFilterMonitorTypeChange.test.ts +1122 -0
- package/Tests/App/Dashboard/DiscoveryScanWizardValidation.test.tsx +283 -0
- package/Tests/App/Dashboard/ExternalStatusPageCriteriaFilter.test.ts +407 -0
- package/Tests/App/Dashboard/LlmCallsTableIdentity.test.tsx +322 -0
- package/Tests/App/Dashboard/LlmOverview.test.tsx +335 -0
- package/Tests/App/Dashboard/LlmSpanDisplay.test.ts +391 -0
- package/Tests/App/Dashboard/LlmUsageBreakdown.test.tsx +1007 -0
- package/Tests/App/Dashboard/MetricSeriesInvestigateMenu.test.tsx +5 -14
- package/Tests/App/Dashboard/MonitorCriteriaActionToggles.test.tsx +426 -0
- package/Tests/App/Dashboard/MonitorStepsCriteriaAlignmentWiring.test.tsx +352 -0
- package/Tests/App/Dashboard/OnCallLayerCardOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/OnCallLayerShiftPreviewOverrides.test.ts +237 -0
- package/Tests/App/Dashboard/OnCallReadinessSurfaces.test.tsx +2 -2
- package/Tests/App/Dashboard/OnCallSchedulePreviewOverrides.test.tsx +460 -0
- package/Tests/App/Dashboard/SecurityEventAttributeUtil.test.ts +171 -0
- package/Tests/App/Dashboard/SecurityEventCorrelation.test.ts +857 -0
- package/Tests/App/Dashboard/SecurityEventDetailObservables.test.tsx +194 -0
- package/Tests/App/Dashboard/SecurityEventsDetectionRulesPage.test.tsx +124 -0
- package/Tests/App/Dashboard/SecurityEventsMonitorStepForm.test.tsx +79 -19
- package/Tests/App/Dashboard/SecurityEventsTableColumns.test.tsx +382 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitors.test.ts +216 -0
- package/Tests/App/StatusPage/BulkAddStatusPageMonitorsModal.test.tsx +590 -11
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +757 -0
- package/Tests/Models/DiscoveryScanNameColumn.test.ts +293 -0
- package/Tests/Server/API/BaseAPI.test.ts +41 -0
- package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +9 -16
- package/Tests/Server/API/SecurityEventAttributesAPI.test.ts +564 -0
- package/Tests/Server/Infrastructure/Postgres/SchemaMigrationsOrdering.test.ts +120 -4
- package/Tests/Server/Middleware/GzipRequestBody.test.ts +725 -0
- package/Tests/Server/Middleware/MultipartFormData.test.ts +298 -0
- package/Tests/Server/Middleware/SlackAuthorization.test.ts +262 -5
- package/Tests/Server/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
- package/Tests/Server/Services/DetectionRuleService.test.ts +134 -2
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
- package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
- package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
- package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
- package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
- package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
- package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
- package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
- package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
- package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
- package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +387 -4
- package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +386 -0
- package/Tests/Server/Utils/Telemetry/LlmSpan.test.ts +804 -0
- package/Tests/Server/Views/AnalyticsConsentPartial.test.ts +48 -5
- package/Tests/Types/Code/YamlSyntax.test.ts +396 -0
- package/Tests/Types/Monitor/MetricAndProfileDefaultCriteria.test.ts +957 -0
- package/Tests/Types/Monitor/MonitorCriteriaActionValidation.test.ts +598 -0
- package/Tests/Types/Monitor/MonitorCriteriaInstance.test.ts +49 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +6 -6
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +4 -4
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +4 -3
- package/Tests/Types/Telemetry/LlmMetricConventions.test.ts +584 -0
- package/Tests/UI/Components/AiInvestigationSettingsCard.test.tsx +0 -12
- package/Tests/UI/Components/CardModelDetailEdit.test.tsx +1 -17
- package/Tests/UI/Components/CodeEditor.test.tsx +474 -10
- package/Tests/UI/Components/Detail/YamlField.test.tsx +105 -0
- package/Tests/UI/Components/EntityDropdownLabelsBulkAdd.test.tsx +319 -0
- package/Tests/UI/Components/Forms/FormFieldYaml.test.tsx +231 -0
- package/Tests/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.test.ts +1 -0
- package/Tests/UI/Components/Forms/ValidationYAML.test.ts +234 -0
- package/Tests/UI/Components/ModelTable/AttributeColumns.test.tsx +573 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableAttributeColumns.test.tsx +824 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModalAddableColumns.test.tsx +707 -0
- package/Tests/UI/Components/SideOver.test.tsx +1 -1
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +294 -0
- package/Tests/UI/Components/StatusPage/ResourceListSelection.test.tsx +436 -0
- package/Tests/UI/Components/TableBulkCsvExport.test.tsx +6 -2
- package/Tests/UI/Components/Toggle.test.tsx +25 -0
- package/Tests/UI/Components/YamlEditor.test.tsx +847 -0
- package/Tests/Utils/NetworkDevice/DeviceReachabilityUtil.test.ts +252 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
- package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
- package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +770 -0
- package/Types/Code/YamlSyntax.ts +396 -0
- package/Types/Exception/ExceptionCode.ts +1 -0
- package/Types/Exception/PayloadTooLargeException.ts +8 -0
- package/Types/Marketing/Attribution.ts +10 -0
- package/Types/Marketing/MarketingEvent.ts +4 -0
- package/Types/Monitor/MonitorCriteriaInstance.ts +116 -56
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +2 -1
- package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
- package/Types/Telemetry/LlmConventions.ts +255 -0
- package/Types/Telemetry/LlmMetricConventions.ts +329 -0
- package/UI/Components/AutocompleteTextInput/AutocompleteTextInput.tsx +3 -0
- package/UI/Components/BulkUpdate/BulkUpdateForm.tsx +10 -1
- package/UI/Components/CodeEditor/CodeEditor.tsx +241 -79
- package/UI/Components/CodeEditor/YamlEditor.tsx +369 -0
- package/UI/Components/Detail/Detail.tsx +10 -0
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +33 -0
- package/UI/Components/Forms/Fields/FormField.tsx +31 -0
- package/UI/Components/Forms/Types/Field.ts +8 -0
- package/UI/Components/Forms/Types/FormFieldSchemaType.ts +1 -0
- package/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.ts +2 -0
- package/UI/Components/Forms/Validation.ts +46 -0
- package/UI/Components/Input/Input.tsx +3 -0
- package/UI/Components/ModelTable/AttributeColumns.tsx +321 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +263 -14
- package/UI/Components/ModelTable/Column.ts +8 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +310 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +13 -1
- package/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.tsx +2 -2
- package/UI/Components/SideOver/SideOver.tsx +5 -5
- package/UI/Components/StatusPage/ResourceGrid.tsx +82 -2
- package/UI/Components/StatusPage/ResourceList.tsx +197 -38
- package/UI/Components/Toggle/Toggle.tsx +9 -1
- package/UI/Components/Types/FieldType.ts +1 -0
- package/Utils/NetworkDevice/DeviceReachabilityUtil.ts +73 -1
- package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
- package/Utils/StatusPage/ResourceExplorer.ts +258 -0
- package/Utils/Telemetry/LlmMetricQuery.ts +287 -0
- package/build/dist/Models/AnalyticsModels/Span.js +89 -0
- package/build/dist/Models/AnalyticsModels/Span.js.map +1 -1
- package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +27 -7
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +69 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +76 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +76 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +3 -19
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/SlackAPI.js +0 -2
- package/build/dist/Server/API/SlackAPI.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +17 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
- package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
- package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
- package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
- package/build/dist/Server/Middleware/SlackAuthorization.js +58 -7
- package/build/dist/Server/Middleware/SlackAuthorization.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +49 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +204 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +25 -45
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/SecurityEventService.js +57 -5
- package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
- package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +2 -12
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +147 -61
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Attribution.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -21
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +151 -0
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/LlmSpan.js +24 -1
- package/build/dist/Server/Utils/Telemetry/LlmSpan.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js +0 -10
- package/build/dist/Server/Utils/Workspace/Slack/Actions/Auth.js.map +1 -1
- package/build/dist/Types/Code/YamlSyntax.js +212 -0
- package/build/dist/Types/Code/YamlSyntax.js.map +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js +1 -0
- package/build/dist/Types/Exception/ExceptionCode.js.map +1 -1
- package/build/dist/Types/Exception/PayloadTooLargeException.js +8 -0
- package/build/dist/Types/Exception/PayloadTooLargeException.js.map +1 -0
- package/build/dist/Types/Marketing/Attribution.js +10 -0
- package/build/dist/Types/Marketing/Attribution.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js +112 -51
- package/build/dist/Types/Monitor/MonitorCriteriaInstance.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +2 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -1
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js +9 -0
- package/build/dist/Types/SecurityEvent/DetectionFindingConstants.js.map +1 -1
- package/build/dist/Types/Telemetry/LlmConventions.js +236 -0
- package/build/dist/Types/Telemetry/LlmConventions.js.map +1 -1
- package/build/dist/Types/Telemetry/LlmMetricConventions.js +290 -0
- package/build/dist/Types/Telemetry/LlmMetricConventions.js.map +1 -0
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js +1 -1
- package/build/dist/UI/Components/AutocompleteTextInput/AutocompleteTextInput.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js +5 -2
- package/build/dist/UI/Components/BulkUpdate/BulkUpdateForm.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +206 -76
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js +201 -0
- package/build/dist/UI/Components/CodeEditor/YamlEditor.js.map +1 -0
- package/build/dist/UI/Components/Detail/Detail.js +9 -0
- package/build/dist/UI/Components/Detail/Detail.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +16 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +14 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/Field.js.map +1 -1
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js +1 -0
- package/build/dist/UI/Components/Forms/Types/FormFieldSchemaType.js.map +1 -1
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js +2 -0
- package/build/dist/UI/Components/Forms/Utils/FormFieldSchemaTypeUtil.js.map +1 -1
- package/build/dist/UI/Components/Forms/Validation.js +27 -0
- package/build/dist/UI/Components/Forms/Validation.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +1 -1
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js +159 -0
- package/build/dist/UI/Components/ModelTable/AttributeColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +179 -17
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +133 -2
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +7 -1
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -1
- package/build/dist/UI/Components/MonitorTemplateVariables/TemplateVariablesModal.js +2 -2
- package/build/dist/UI/Components/SideOver/SideOver.js +5 -5
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +28 -2
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceList.js +87 -20
- package/build/dist/UI/Components/StatusPage/ResourceList.js.map +1 -1
- package/build/dist/UI/Components/Toggle/Toggle.js +9 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Types/FieldType.js +1 -0
- package/build/dist/UI/Components/Types/FieldType.js.map +1 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js +37 -1
- package/build/dist/Utils/NetworkDevice/DeviceReachabilityUtil.js.map +1 -1
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js +175 -0
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js.map +1 -0
- package/jest.config.json +2 -0
- package/package.json +1 -1
- package/tsconfig.json +2 -1
- package/Tests/UI/Components/Toast.test.tsx +0 -197
- package/Tests/UI/Components/ToastStacking.test.tsx +0 -198
- package/UI/Components/Toast/Toast.tsx +0 -189
- package/UI/Components/Toast/ToastInit.tsx +0 -110
- package/build/dist/UI/Components/Toast/Toast.js +0 -123
- package/build/dist/UI/Components/Toast/Toast.js.map +0 -1
- package/build/dist/UI/Components/Toast/ToastInit.js +0 -53
- package/build/dist/UI/Components/Toast/ToastInit.js.map +0 -1
|
@@ -0,0 +1,573 @@
|
|
|
1
|
+
import Column from "../../../../UI/Components/ModelTable/Column";
|
|
2
|
+
import Columns from "../../../../UI/Components/ModelTable/Columns";
|
|
3
|
+
import {
|
|
4
|
+
getAttributeColumnId,
|
|
5
|
+
getAttributeColumns,
|
|
6
|
+
getAttributeExportValue,
|
|
7
|
+
getAttributeKeyFromColumnId,
|
|
8
|
+
getAttributeKeysFromColumnPreference,
|
|
9
|
+
getAttributeValue,
|
|
10
|
+
normalizeAttributeKeys,
|
|
11
|
+
renderAttributeValue,
|
|
12
|
+
} from "../../../../UI/Components/ModelTable/AttributeColumns";
|
|
13
|
+
import {
|
|
14
|
+
ColumnPreference,
|
|
15
|
+
getColumnBaseId,
|
|
16
|
+
getColumnIds,
|
|
17
|
+
getCustomizableColumns,
|
|
18
|
+
CustomizableColumn,
|
|
19
|
+
} from "../../../../UI/Components/ModelTable/ColumnPreference";
|
|
20
|
+
import FieldType from "../../../../UI/Components/Types/FieldType";
|
|
21
|
+
import SecurityEvent from "../../../../Models/AnalyticsModels/SecurityEvent";
|
|
22
|
+
import { JSONObject } from "../../../../Types/JSON";
|
|
23
|
+
import "@testing-library/jest-dom";
|
|
24
|
+
import { cleanup, render } from "@testing-library/react";
|
|
25
|
+
import { afterEach, describe, expect, test } from "@jest/globals";
|
|
26
|
+
|
|
27
|
+
/*
|
|
28
|
+
* Attribute columns are generated from keys inside a row's Map(String,String)
|
|
29
|
+
* column, which is where every source field the OCSF schema has no typed
|
|
30
|
+
* column for ends up. Almost everything about their shape is load-bearing in a
|
|
31
|
+
* way that fails quietly rather than loudly:
|
|
32
|
+
*
|
|
33
|
+
* - the declared `field` has to be the REAL map column. A synthetic
|
|
34
|
+
* `field: { "attributes.x": true }` fails the model's column check in
|
|
35
|
+
* getSelectFromColumns and throws the whole table away.
|
|
36
|
+
* - the id has to match what ColumnPreference would derive on its own, or a
|
|
37
|
+
* stored layout and a live column disagree about which column is which.
|
|
38
|
+
* - sorting has to stay off: a dotted pseudo-column reaching the query
|
|
39
|
+
* builder is not something ClickHouse can order by.
|
|
40
|
+
* - the cell has to render through getElement, because the typed renderers
|
|
41
|
+
* index item[column.key] and would look for a literal
|
|
42
|
+
* "attributes.device.hostname" property no row has.
|
|
43
|
+
*
|
|
44
|
+
* And the whole "which columns did the viewer add" question is answered by
|
|
45
|
+
* reading the stored layout back, so that round trip is pinned here too.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
const ATTRIBUTES: string = "attributes";
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
cleanup();
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
type MakeEventFunction = (attributes: JSONObject | undefined) => SecurityEvent;
|
|
55
|
+
|
|
56
|
+
const makeEvent: MakeEventFunction = (
|
|
57
|
+
attributes: JSONObject | undefined,
|
|
58
|
+
): SecurityEvent => {
|
|
59
|
+
const event: SecurityEvent = new SecurityEvent();
|
|
60
|
+
event.attributes = attributes;
|
|
61
|
+
return event;
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
describe("AttributeColumns - ids", () => {
|
|
65
|
+
test("the id is the map column and the key, joined with a dot", () => {
|
|
66
|
+
expect(
|
|
67
|
+
getAttributeColumnId({
|
|
68
|
+
attributesColumnKey: ATTRIBUTES,
|
|
69
|
+
attributeKey: "device.hostname",
|
|
70
|
+
}),
|
|
71
|
+
).toBe("attributes.device.hostname");
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test("the id round-trips back to the key", () => {
|
|
75
|
+
const columnId: string = getAttributeColumnId({
|
|
76
|
+
attributesColumnKey: ATTRIBUTES,
|
|
77
|
+
attributeKey: "metadata.product.name",
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
expect(
|
|
81
|
+
getAttributeKeyFromColumnId({
|
|
82
|
+
attributesColumnKey: ATTRIBUTES,
|
|
83
|
+
columnId: columnId,
|
|
84
|
+
}),
|
|
85
|
+
).toBe("metadata.product.name");
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
test("an id under a different column, or with no key at all, is not an attribute id", () => {
|
|
89
|
+
expect(
|
|
90
|
+
getAttributeKeyFromColumnId({
|
|
91
|
+
attributesColumnKey: ATTRIBUTES,
|
|
92
|
+
columnId: "customFields.Severity",
|
|
93
|
+
}),
|
|
94
|
+
).toBeNull();
|
|
95
|
+
|
|
96
|
+
// The map column itself is a column, not a column-per-key.
|
|
97
|
+
expect(
|
|
98
|
+
getAttributeKeyFromColumnId({
|
|
99
|
+
attributesColumnKey: ATTRIBUTES,
|
|
100
|
+
columnId: "attributes",
|
|
101
|
+
}),
|
|
102
|
+
).toBeNull();
|
|
103
|
+
|
|
104
|
+
expect(
|
|
105
|
+
getAttributeKeyFromColumnId({
|
|
106
|
+
attributesColumnKey: ATTRIBUTES,
|
|
107
|
+
columnId: "attributes.",
|
|
108
|
+
}),
|
|
109
|
+
).toBeNull();
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
/*
|
|
113
|
+
* The explicit id and the one ColumnPreference derives from field +
|
|
114
|
+
* selectedProperty have to be the same string. If they ever drift, a layout
|
|
115
|
+
* saved by one release stops matching the column in the next.
|
|
116
|
+
*/
|
|
117
|
+
test("the explicit id matches what ColumnPreference would derive on its own", () => {
|
|
118
|
+
const [column] = getAttributeColumns<SecurityEvent>({
|
|
119
|
+
attributesColumnKey: ATTRIBUTES,
|
|
120
|
+
attributeKeys: ["device.hostname"],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const derivedId: string = getColumnBaseId<SecurityEvent>({
|
|
124
|
+
...(column as Column<SecurityEvent>),
|
|
125
|
+
id: undefined,
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
expect(column!.id).toBe(derivedId);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test("ids stay unique across a generated set", () => {
|
|
132
|
+
const columns: Columns<SecurityEvent> = getAttributeColumns<SecurityEvent>({
|
|
133
|
+
attributesColumnKey: ATTRIBUTES,
|
|
134
|
+
attributeKeys: ["a", "b", "a.b", "c"],
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const ids: Array<string> = getColumnIds<SecurityEvent>(columns);
|
|
138
|
+
|
|
139
|
+
expect(new Set(ids).size).toBe(ids.length);
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
describe("AttributeColumns - normalizeAttributeKeys", () => {
|
|
144
|
+
test("trims, de-duplicates and sorts", () => {
|
|
145
|
+
expect(
|
|
146
|
+
normalizeAttributeKeys([
|
|
147
|
+
" user.name ",
|
|
148
|
+
"device.hostname",
|
|
149
|
+
"user.name",
|
|
150
|
+
"class_uid",
|
|
151
|
+
]),
|
|
152
|
+
).toEqual(["class_uid", "device.hostname", "user.name"]);
|
|
153
|
+
});
|
|
154
|
+
|
|
155
|
+
test("drops empties and non-strings rather than rendering them as columns", () => {
|
|
156
|
+
expect(
|
|
157
|
+
normalizeAttributeKeys(["", " ", null, undefined, 42, {}, "ok"]),
|
|
158
|
+
).toEqual(["ok"]);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
test("anything that is not an array degrades to an empty list", () => {
|
|
162
|
+
expect(normalizeAttributeKeys(null)).toEqual([]);
|
|
163
|
+
expect(normalizeAttributeKeys(undefined)).toEqual([]);
|
|
164
|
+
expect(
|
|
165
|
+
normalizeAttributeKeys("device.hostname" as unknown as Array<unknown>),
|
|
166
|
+
).toEqual([]);
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
test("the same input always produces the same order", () => {
|
|
170
|
+
const first: Array<string> = normalizeAttributeKeys(["b", "a", "c"]);
|
|
171
|
+
const second: Array<string> = normalizeAttributeKeys(["c", "a", "b"]);
|
|
172
|
+
|
|
173
|
+
expect(first).toEqual(second);
|
|
174
|
+
});
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
describe("AttributeColumns - reading a value off a row", () => {
|
|
178
|
+
test("reads the key out of the map column", () => {
|
|
179
|
+
expect(
|
|
180
|
+
getAttributeValue({
|
|
181
|
+
item: makeEvent({ "device.hostname": "web-1" }),
|
|
182
|
+
attributesColumnKey: ATTRIBUTES,
|
|
183
|
+
attributeKey: "device.hostname",
|
|
184
|
+
}),
|
|
185
|
+
).toBe("web-1");
|
|
186
|
+
});
|
|
187
|
+
|
|
188
|
+
test("a row with no map, or no such key, has no value", () => {
|
|
189
|
+
expect(
|
|
190
|
+
getAttributeValue({
|
|
191
|
+
item: makeEvent(undefined),
|
|
192
|
+
attributesColumnKey: ATTRIBUTES,
|
|
193
|
+
attributeKey: "device.hostname",
|
|
194
|
+
}),
|
|
195
|
+
).toBeUndefined();
|
|
196
|
+
|
|
197
|
+
expect(
|
|
198
|
+
getAttributeValue({
|
|
199
|
+
item: makeEvent({ "user.name": "bob" }),
|
|
200
|
+
attributesColumnKey: ATTRIBUTES,
|
|
201
|
+
attributeKey: "device.hostname",
|
|
202
|
+
}),
|
|
203
|
+
).toBeUndefined();
|
|
204
|
+
|
|
205
|
+
expect(
|
|
206
|
+
getAttributeValue({
|
|
207
|
+
item: undefined,
|
|
208
|
+
attributesColumnKey: ATTRIBUTES,
|
|
209
|
+
attributeKey: "device.hostname",
|
|
210
|
+
}),
|
|
211
|
+
).toBeUndefined();
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
/*
|
|
215
|
+
* The map is typed Map(String,String) in ClickHouse, but rows also reach
|
|
216
|
+
* this code straight from JSON the API returned, and a project can PUT
|
|
217
|
+
* anything over the ingest API - so a non-string must not throw.
|
|
218
|
+
*/
|
|
219
|
+
test("a non-object under the map column is treated as absent", () => {
|
|
220
|
+
expect(
|
|
221
|
+
getAttributeValue({
|
|
222
|
+
item: { attributes: "not-a-map" },
|
|
223
|
+
attributesColumnKey: ATTRIBUTES,
|
|
224
|
+
attributeKey: "device.hostname",
|
|
225
|
+
}),
|
|
226
|
+
).toBeUndefined();
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
describe("AttributeColumns - export values", () => {
|
|
231
|
+
test("scalars stringify", () => {
|
|
232
|
+
expect(getAttributeExportValue("web-1")).toBe("web-1");
|
|
233
|
+
expect(getAttributeExportValue(3002)).toBe("3002");
|
|
234
|
+
expect(getAttributeExportValue(false)).toBe("false");
|
|
235
|
+
});
|
|
236
|
+
|
|
237
|
+
test("null and undefined export as an empty cell, not as the word", () => {
|
|
238
|
+
expect(getAttributeExportValue(null)).toBe("");
|
|
239
|
+
expect(getAttributeExportValue(undefined)).toBe("");
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
test("arrays join, objects serialize", () => {
|
|
243
|
+
expect(getAttributeExportValue(["a", "b"])).toBe("a, b");
|
|
244
|
+
expect(getAttributeExportValue({ a: 1 })).toBe('{"a":1}');
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
/*
|
|
248
|
+
* An empty string is a real stored value; it just has nothing to show. It
|
|
249
|
+
* must not become the literal text "" in a CSV.
|
|
250
|
+
*/
|
|
251
|
+
test("an empty string exports as an empty cell", () => {
|
|
252
|
+
expect(getAttributeExportValue("")).toBe("");
|
|
253
|
+
});
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
describe("AttributeColumns - rendering", () => {
|
|
257
|
+
test("renders the value, with the full text available on hover", () => {
|
|
258
|
+
const { container } = render(
|
|
259
|
+
renderAttributeValue({ value: "web-1.prod.internal" }),
|
|
260
|
+
);
|
|
261
|
+
|
|
262
|
+
expect(container.textContent).toBe("web-1.prod.internal");
|
|
263
|
+
expect(container.querySelector("span")).toHaveAttribute(
|
|
264
|
+
"title",
|
|
265
|
+
"web-1.prod.internal",
|
|
266
|
+
);
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
test("a missing value renders the placeholder rather than nothing", () => {
|
|
270
|
+
const { container } = render(renderAttributeValue({ value: undefined }));
|
|
271
|
+
|
|
272
|
+
expect(container.textContent).toBe("-");
|
|
273
|
+
});
|
|
274
|
+
|
|
275
|
+
test("the placeholder is configurable", () => {
|
|
276
|
+
const { container } = render(
|
|
277
|
+
renderAttributeValue({ value: null, noValueMessage: "not set" }),
|
|
278
|
+
);
|
|
279
|
+
|
|
280
|
+
expect(container.textContent).toBe("not set");
|
|
281
|
+
});
|
|
282
|
+
|
|
283
|
+
test("a nested value renders rather than showing [object Object]", () => {
|
|
284
|
+
const { container } = render(
|
|
285
|
+
renderAttributeValue({ value: { product: "SecOps" } }),
|
|
286
|
+
);
|
|
287
|
+
|
|
288
|
+
expect(container.textContent).toBe('{"product":"SecOps"}');
|
|
289
|
+
});
|
|
290
|
+
});
|
|
291
|
+
|
|
292
|
+
describe("AttributeColumns - generated column shape", () => {
|
|
293
|
+
const columns: Columns<SecurityEvent> = getAttributeColumns<SecurityEvent>({
|
|
294
|
+
attributesColumnKey: ATTRIBUTES,
|
|
295
|
+
attributeKeys: ["device.hostname"],
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const column: Column<SecurityEvent> = columns[0] as Column<SecurityEvent>;
|
|
299
|
+
|
|
300
|
+
test("declares the real map column as its field", () => {
|
|
301
|
+
/*
|
|
302
|
+
* getSelectFromColumns throws a BadDataException for a field the model
|
|
303
|
+
* does not have, which takes the entire table down - so this has to be a
|
|
304
|
+
* column that exists.
|
|
305
|
+
*/
|
|
306
|
+
expect(Object.keys(column.field as JSONObject)).toEqual([ATTRIBUTES]);
|
|
307
|
+
expect(new SecurityEvent().hasColumn(ATTRIBUTES)).toBe(true);
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
test("names the key through selectedProperty, not through the field", () => {
|
|
311
|
+
expect(column.selectedProperty).toBe("device.hostname");
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
test("titles itself with the raw key, so searching for the key finds it", () => {
|
|
315
|
+
expect(column.title).toBe("device.hostname");
|
|
316
|
+
});
|
|
317
|
+
|
|
318
|
+
test("is not sortable", () => {
|
|
319
|
+
expect(column.disableSort).toBe(true);
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
test("renders through getElement", () => {
|
|
323
|
+
expect(column.type).toBe(FieldType.Element);
|
|
324
|
+
expect(typeof column.getElement).toBe("function");
|
|
325
|
+
});
|
|
326
|
+
|
|
327
|
+
test("is removable, so the picker can take it away again", () => {
|
|
328
|
+
expect(column.isRemovable).toBe(true);
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
test("defaults to visible - a column you just added should be on screen", () => {
|
|
332
|
+
expect(column.isHiddenByDefault).toBe(false);
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
test("can be generated hidden when a caller wants a checklist instead", () => {
|
|
336
|
+
const [hidden] = getAttributeColumns<SecurityEvent>({
|
|
337
|
+
attributesColumnKey: ATTRIBUTES,
|
|
338
|
+
attributeKeys: ["device.hostname"],
|
|
339
|
+
isHiddenByDefault: true,
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
expect(hidden!.isHiddenByDefault).toBe(true);
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
test("the cell reads its own key off the row", () => {
|
|
346
|
+
const { container } = render(
|
|
347
|
+
column.getElement!(makeEvent({ "device.hostname": "web-1" })),
|
|
348
|
+
);
|
|
349
|
+
|
|
350
|
+
expect(container.textContent).toBe("web-1");
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("the cell renders the placeholder for a row that lacks the key", () => {
|
|
354
|
+
const { container } = render(
|
|
355
|
+
column.getElement!(makeEvent({ "user.name": "bob" })),
|
|
356
|
+
);
|
|
357
|
+
|
|
358
|
+
expect(container.textContent).toBe("-");
|
|
359
|
+
});
|
|
360
|
+
|
|
361
|
+
test("the export cell reads the same key the rendered cell does", () => {
|
|
362
|
+
expect(
|
|
363
|
+
column.getExportValue!(makeEvent({ "device.hostname": "web-1" })),
|
|
364
|
+
).toBe("web-1");
|
|
365
|
+
|
|
366
|
+
expect(column.getExportValue!(makeEvent({}))).toBe("");
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
test("generates one column per key, in the order given", () => {
|
|
370
|
+
const many: Columns<SecurityEvent> = getAttributeColumns<SecurityEvent>({
|
|
371
|
+
attributesColumnKey: ATTRIBUTES,
|
|
372
|
+
attributeKeys: ["b", "a", "c"],
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
expect(
|
|
376
|
+
many.map((entry: Column<SecurityEvent>) => {
|
|
377
|
+
return entry.title;
|
|
378
|
+
}),
|
|
379
|
+
).toEqual(["b", "a", "c"]);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test("an empty key list generates nothing", () => {
|
|
383
|
+
expect(
|
|
384
|
+
getAttributeColumns<SecurityEvent>({
|
|
385
|
+
attributesColumnKey: ATTRIBUTES,
|
|
386
|
+
attributeKeys: [],
|
|
387
|
+
}),
|
|
388
|
+
).toEqual([]);
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
/*
|
|
392
|
+
* `isRemovable` has to survive the trip into the picker's model, because
|
|
393
|
+
* that is the only thing that tells the modal to offer a remove button.
|
|
394
|
+
*/
|
|
395
|
+
test("isRemovable reaches the picker entry", () => {
|
|
396
|
+
const entries: Array<CustomizableColumn<SecurityEvent>> =
|
|
397
|
+
getCustomizableColumns<SecurityEvent>({ columns, preference: null });
|
|
398
|
+
|
|
399
|
+
expect(entries[0]!.isRemovable).toBe(true);
|
|
400
|
+
});
|
|
401
|
+
|
|
402
|
+
test("a pinned column is never removable, whatever it declares", () => {
|
|
403
|
+
const entries: Array<CustomizableColumn<SecurityEvent>> =
|
|
404
|
+
getCustomizableColumns<SecurityEvent>({
|
|
405
|
+
columns: [
|
|
406
|
+
{
|
|
407
|
+
...(column as Column<SecurityEvent>),
|
|
408
|
+
isNotCustomizable: true,
|
|
409
|
+
},
|
|
410
|
+
],
|
|
411
|
+
preference: null,
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
expect(entries[0]!.isPinned).toBe(true);
|
|
415
|
+
expect(entries[0]!.isRemovable).toBe(false);
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("an ordinary column is not removable", () => {
|
|
419
|
+
const entries: Array<CustomizableColumn<SecurityEvent>> =
|
|
420
|
+
getCustomizableColumns<SecurityEvent>({
|
|
421
|
+
columns: [
|
|
422
|
+
{
|
|
423
|
+
field: { message: true },
|
|
424
|
+
title: "Message",
|
|
425
|
+
type: FieldType.Text,
|
|
426
|
+
},
|
|
427
|
+
],
|
|
428
|
+
preference: null,
|
|
429
|
+
});
|
|
430
|
+
|
|
431
|
+
expect(entries[0]!.isRemovable).toBe(false);
|
|
432
|
+
});
|
|
433
|
+
});
|
|
434
|
+
|
|
435
|
+
describe("AttributeColumns - recovering the viewer's keys from a stored layout", () => {
|
|
436
|
+
/*
|
|
437
|
+
* There is no second store for "which attribute columns did they add": a
|
|
438
|
+
* column exists precisely when its id is in the layout. Everything below
|
|
439
|
+
* pins that recovery, because a bug here silently loses columns someone
|
|
440
|
+
* chose, or invents ones they did not.
|
|
441
|
+
*/
|
|
442
|
+
test("no stored layout means no attribute columns", () => {
|
|
443
|
+
expect(
|
|
444
|
+
getAttributeKeysFromColumnPreference({
|
|
445
|
+
preference: null,
|
|
446
|
+
attributesColumnKey: ATTRIBUTES,
|
|
447
|
+
}),
|
|
448
|
+
).toEqual([]);
|
|
449
|
+
|
|
450
|
+
expect(
|
|
451
|
+
getAttributeKeysFromColumnPreference({
|
|
452
|
+
preference: undefined,
|
|
453
|
+
attributesColumnKey: ATTRIBUTES,
|
|
454
|
+
}),
|
|
455
|
+
).toEqual([]);
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
test("picks the attribute ids out of the order list, in order", () => {
|
|
459
|
+
const preference: ColumnPreference = {
|
|
460
|
+
order: [
|
|
461
|
+
"time",
|
|
462
|
+
"attributes.device.hostname",
|
|
463
|
+
"severityName",
|
|
464
|
+
"attributes.class_uid",
|
|
465
|
+
],
|
|
466
|
+
hidden: [],
|
|
467
|
+
};
|
|
468
|
+
|
|
469
|
+
expect(
|
|
470
|
+
getAttributeKeysFromColumnPreference({
|
|
471
|
+
preference,
|
|
472
|
+
attributesColumnKey: ATTRIBUTES,
|
|
473
|
+
}),
|
|
474
|
+
).toEqual(["device.hostname", "class_uid"]);
|
|
475
|
+
});
|
|
476
|
+
|
|
477
|
+
/*
|
|
478
|
+
* A viewer who switched an attribute column off still chose it. Losing it
|
|
479
|
+
* here would make "hide" behave like "remove", but only after a reload.
|
|
480
|
+
*/
|
|
481
|
+
test("a hidden attribute column is still a column", () => {
|
|
482
|
+
expect(
|
|
483
|
+
getAttributeKeysFromColumnPreference({
|
|
484
|
+
preference: {
|
|
485
|
+
order: ["time", "attributes.device.hostname"],
|
|
486
|
+
hidden: ["attributes.device.hostname"],
|
|
487
|
+
},
|
|
488
|
+
attributesColumnKey: ATTRIBUTES,
|
|
489
|
+
}),
|
|
490
|
+
).toEqual(["device.hostname"]);
|
|
491
|
+
});
|
|
492
|
+
|
|
493
|
+
/*
|
|
494
|
+
* buildColumnPreference always writes both lists, but a payload from an
|
|
495
|
+
* older release - or one hand-edited in devtools - may only carry `hidden`.
|
|
496
|
+
*/
|
|
497
|
+
test("an id that appears only in hidden is still recovered", () => {
|
|
498
|
+
expect(
|
|
499
|
+
getAttributeKeysFromColumnPreference({
|
|
500
|
+
preference: { order: [], hidden: ["attributes.device.hostname"] },
|
|
501
|
+
attributesColumnKey: ATTRIBUTES,
|
|
502
|
+
}),
|
|
503
|
+
).toEqual(["device.hostname"]);
|
|
504
|
+
});
|
|
505
|
+
|
|
506
|
+
test("the same key listed twice produces one column", () => {
|
|
507
|
+
expect(
|
|
508
|
+
getAttributeKeysFromColumnPreference({
|
|
509
|
+
preference: {
|
|
510
|
+
order: ["attributes.a", "attributes.a"],
|
|
511
|
+
hidden: ["attributes.a"],
|
|
512
|
+
},
|
|
513
|
+
attributesColumnKey: ATTRIBUTES,
|
|
514
|
+
}),
|
|
515
|
+
).toEqual(["a"]);
|
|
516
|
+
});
|
|
517
|
+
|
|
518
|
+
test("ids belonging to declared columns are left alone", () => {
|
|
519
|
+
expect(
|
|
520
|
+
getAttributeKeysFromColumnPreference({
|
|
521
|
+
preference: { order: ["attributes.device.hostname"], hidden: [] },
|
|
522
|
+
attributesColumnKey: ATTRIBUTES,
|
|
523
|
+
reservedColumnIds: ["attributes.device.hostname"],
|
|
524
|
+
}),
|
|
525
|
+
).toEqual([]);
|
|
526
|
+
});
|
|
527
|
+
|
|
528
|
+
test("custom field ids are not mistaken for attribute ids", () => {
|
|
529
|
+
expect(
|
|
530
|
+
getAttributeKeysFromColumnPreference({
|
|
531
|
+
preference: {
|
|
532
|
+
order: ["customFields.Severity", "attributes.user.name"],
|
|
533
|
+
hidden: [],
|
|
534
|
+
},
|
|
535
|
+
attributesColumnKey: ATTRIBUTES,
|
|
536
|
+
}),
|
|
537
|
+
).toEqual(["user.name"]);
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
test("a different map column name is honoured", () => {
|
|
541
|
+
expect(
|
|
542
|
+
getAttributeKeysFromColumnPreference({
|
|
543
|
+
preference: { order: ["resourceAttributes.host.name"], hidden: [] },
|
|
544
|
+
attributesColumnKey: "resourceAttributes",
|
|
545
|
+
}),
|
|
546
|
+
).toEqual(["host.name"]);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
/*
|
|
550
|
+
* The full round trip: keys -> columns -> ids -> a stored layout -> keys.
|
|
551
|
+
* This is the loop the feature actually runs every page load.
|
|
552
|
+
*/
|
|
553
|
+
test("keys survive the round trip through generated columns and back", () => {
|
|
554
|
+
const keys: Array<string> = ["device.hostname", "finding_info.title"];
|
|
555
|
+
|
|
556
|
+
const columns: Columns<SecurityEvent> = getAttributeColumns<SecurityEvent>({
|
|
557
|
+
attributesColumnKey: ATTRIBUTES,
|
|
558
|
+
attributeKeys: keys,
|
|
559
|
+
});
|
|
560
|
+
|
|
561
|
+
const preference: ColumnPreference = {
|
|
562
|
+
order: getColumnIds<SecurityEvent>(columns),
|
|
563
|
+
hidden: [],
|
|
564
|
+
};
|
|
565
|
+
|
|
566
|
+
expect(
|
|
567
|
+
getAttributeKeysFromColumnPreference({
|
|
568
|
+
preference,
|
|
569
|
+
attributesColumnKey: ATTRIBUTES,
|
|
570
|
+
}),
|
|
571
|
+
).toEqual(keys);
|
|
572
|
+
});
|
|
573
|
+
});
|