@oneuptime/common 12.0.22 → 12.0.24
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/DatabaseModels/DetectionRule.ts +55 -0
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/NetworkDevice.ts +44 -0
- package/Models/DatabaseModels/NetworkDeviceAutoImportRule.ts +584 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +105 -0
- package/Models/DatabaseModels/Project.ts +68 -0
- package/Models/DatabaseModels/User.ts +68 -0
- package/Server/API/TelemetryAPI.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.ts +60 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.ts +26 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.ts +26 -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 +12 -0
- package/Server/Middleware/GzipRequestBody.ts +311 -0
- package/Server/Middleware/MultipartFormData.ts +87 -3
- package/Server/Services/DetectionRuleService.ts +73 -6
- package/Server/Services/Index.ts +207 -0
- package/Server/Services/NetworkDeviceAutoImportRuleEngineService.ts +917 -0
- package/Server/Services/NetworkDeviceAutoImportRuleService.ts +181 -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/Monitor/NetworkInventoryUtil.ts +39 -1
- 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 +130 -0
- 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/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/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/Services/AddDetectionRuleDistinctCountColumnsMigration.test.ts +311 -0
- package/Tests/Server/Services/AutoImportScanCredentialSelect.test.ts +205 -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/NetworkDeviceAutoImportRuleEngineService.test.ts +933 -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/Monitor/NetworkInventoryUtil.test.ts +97 -1
- 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 +368 -4
- package/Tests/Server/Utils/Telemetry/LlmMetricSpend.test.ts +245 -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 +193 -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/AutoImportRuleMatcher.test.ts +516 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.test.ts +272 -0
- package/Tests/Utils/NetworkDiscovery/DiscoveredHostUtil.test.ts +221 -0
- package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
- package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +164 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
- package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +472 -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/NetworkAutomation/RuleRunResult.ts +84 -0
- package/Types/Permission.ts +47 -0
- package/Types/SecurityEvent/DetectionFindingConstants.ts +11 -0
- package/Types/Telemetry/LlmMetricConventions.ts +124 -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/AutoImportRuleMatcher.ts +257 -0
- package/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.ts +183 -0
- package/Utils/NetworkDiscovery/DiscoveredHostUtil.ts +95 -0
- package/Utils/NetworkDiscovery/DiscoveryImportEligibility.ts +66 -0
- package/Utils/NetworkDiscovery/ScanNameUtil.ts +181 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +82 -0
- package/Utils/StatusPage/ResourceExplorer.ts +258 -0
- package/Utils/Telemetry/LlmMetricQuery.ts +204 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js +57 -0
- package/build/dist/Models/DatabaseModels/DetectionRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDevice.js +45 -0
- package/build/dist/Models/DatabaseModels/NetworkDevice.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js +609 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +97 -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/TelemetryAPI.js +17 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js +35 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789100000000-AddNetworkDeviceAutoImportRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789200000000-AddSysObjectIdPatternToAutoImportRule.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js +19 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789300000000-AddAutoApplyVendorHealthTemplate.js.map +1 -0
- 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 +12 -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/Services/DetectionRuleService.js +49 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +206 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js +711 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleEngineService.js.map +1 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js +155 -0
- package/build/dist/Server/Services/NetworkDeviceAutoImportRuleService.js.map +1 -0
- 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/Monitor/NetworkInventoryUtil.js +26 -0
- package/build/dist/Server/Utils/Monitor/NetworkInventoryUtil.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 +102 -0
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js.map +1 -0
- 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/NetworkAutomation/RuleRunResult.js +24 -0
- package/build/dist/Types/NetworkAutomation/RuleRunResult.js.map +1 -1
- package/build/dist/Types/Permission.js +41 -0
- package/build/dist/Types/Permission.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/LlmMetricConventions.js +98 -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/AutoImportRuleMatcher.js +149 -0
- package/build/dist/Utils/NetworkDiscovery/AutoImportRuleMatcher.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js +121 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredDeviceBuilder.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js +75 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveredHostUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js +59 -0
- package/build/dist/Utils/NetworkDiscovery/DiscoveryImportEligibility.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js +120 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +60 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -1
- 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 +119 -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,321 @@
|
|
|
1
|
+
import Column from "./Column";
|
|
2
|
+
import Columns from "./Columns";
|
|
3
|
+
import { ColumnPreference } from "./ColumnPreference";
|
|
4
|
+
import FieldType from "../Types/FieldType";
|
|
5
|
+
import AnalyticsBaseModel from "../../../Models/AnalyticsModels/AnalyticsBaseModel/AnalyticsBaseModel";
|
|
6
|
+
import BaseModel from "../../../Models/DatabaseModels/DatabaseBaseModel/DatabaseBaseModel";
|
|
7
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
8
|
+
import React, { ReactElement } from "react";
|
|
9
|
+
|
|
10
|
+
/*
|
|
11
|
+
* ---------------------------------------------------------------------------
|
|
12
|
+
* One table column per key inside a Map(String, String) attributes column
|
|
13
|
+
* ---------------------------------------------------------------------------
|
|
14
|
+
*
|
|
15
|
+
* Analytics rows (logs, spans, security events) carry the whole source
|
|
16
|
+
* payload flattened into a single map column - `attributes` - so that
|
|
17
|
+
* arbitrary vendor fields stay queryable without a schema change. Those keys
|
|
18
|
+
* differ per event class and per source, so there is no fixed list to ship as
|
|
19
|
+
* columns: the viewer picks the ones they care about and the table grows a
|
|
20
|
+
* column for each.
|
|
21
|
+
*
|
|
22
|
+
* The shape mirrors CustomFieldColumns, and for the same reasons:
|
|
23
|
+
*
|
|
24
|
+
* field: { attributes: true } <- a real model column
|
|
25
|
+
* selectedProperty: "device.hostname" <- the key inside it
|
|
26
|
+
*
|
|
27
|
+
* which BaseModelTable turns into the column key "attributes.device.hostname".
|
|
28
|
+
*
|
|
29
|
+
* - the declared field has to be the real map column. A synthetic
|
|
30
|
+
* `field: { "attributes.device.hostname": true }` would fail the model's
|
|
31
|
+
* column check in getSelectFromColumns and throw the whole table away.
|
|
32
|
+
*
|
|
33
|
+
* - sorting is off. Nothing between the header and the query builder
|
|
34
|
+
* validates `sort`, so a clickable header here would send a dotted
|
|
35
|
+
* pseudo-column into ClickHouse, which can only order by a map key through
|
|
36
|
+
* an explicit `attributes['key']` subscript the sort path cannot express.
|
|
37
|
+
*
|
|
38
|
+
* - cells render through `getElement`, because the typed cell renderers index
|
|
39
|
+
* `item[column.key]` directly and would look for a literal
|
|
40
|
+
* "attributes.device.hostname" property that no row has.
|
|
41
|
+
*
|
|
42
|
+
* WHERE THE VIEWER'S CHOICE LIVES
|
|
43
|
+
*
|
|
44
|
+
* Nowhere new. A column exists iff its id appears in the stored
|
|
45
|
+
* ColumnPreference, and the id encodes the key, so the list of chosen
|
|
46
|
+
* attribute columns is recovered from the layout the viewer already saves
|
|
47
|
+
* (see getAttributeKeysFromColumnPreference). That keeps "Reset to default"
|
|
48
|
+
* honest, makes a saved TableView carry its attribute columns with it, and
|
|
49
|
+
* means there is no second store that can drift out of sync with the first.
|
|
50
|
+
*/
|
|
51
|
+
|
|
52
|
+
export type GetAttributeColumnIdFunction = (data: {
|
|
53
|
+
attributesColumnKey: string;
|
|
54
|
+
attributeKey: string;
|
|
55
|
+
}) => string;
|
|
56
|
+
|
|
57
|
+
/*
|
|
58
|
+
* Deliberately the same shape ColumnPreference.getColumnBaseId would derive
|
|
59
|
+
* from `field` + `selectedProperty` on its own ("attributes.device.hostname"),
|
|
60
|
+
* so an explicitly-set id and a derived one can never disagree.
|
|
61
|
+
*/
|
|
62
|
+
export const getAttributeColumnId: GetAttributeColumnIdFunction = (data: {
|
|
63
|
+
attributesColumnKey: string;
|
|
64
|
+
attributeKey: string;
|
|
65
|
+
}): string => {
|
|
66
|
+
return `${data.attributesColumnKey}.${data.attributeKey}`;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export type GetAttributeKeyFromColumnIdFunction = (data: {
|
|
70
|
+
attributesColumnKey: string;
|
|
71
|
+
columnId: string;
|
|
72
|
+
}) => string | null;
|
|
73
|
+
|
|
74
|
+
export const getAttributeKeyFromColumnId: GetAttributeKeyFromColumnIdFunction =
|
|
75
|
+
(data: { attributesColumnKey: string; columnId: string }): string | null => {
|
|
76
|
+
const prefix: string = `${data.attributesColumnKey}.`;
|
|
77
|
+
|
|
78
|
+
if (!data.columnId.startsWith(prefix)) {
|
|
79
|
+
return null;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const attributeKey: string = data.columnId.slice(prefix.length);
|
|
83
|
+
|
|
84
|
+
return attributeKey.length > 0 ? attributeKey : null;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
export type NormalizeAttributeKeysFunction = (
|
|
88
|
+
keys: Array<unknown> | null | undefined,
|
|
89
|
+
) => Array<string>;
|
|
90
|
+
|
|
91
|
+
/*
|
|
92
|
+
* Whatever the server said, turned into a stable list: trimmed, de-duplicated,
|
|
93
|
+
* and sorted. The sort is load-bearing rather than cosmetic - the picker shows
|
|
94
|
+
* these in order, and an unsorted list from ClickHouse would reshuffle itself
|
|
95
|
+
* between requests.
|
|
96
|
+
*/
|
|
97
|
+
export const normalizeAttributeKeys: NormalizeAttributeKeysFunction = (
|
|
98
|
+
keys: Array<unknown> | null | undefined,
|
|
99
|
+
): Array<string> => {
|
|
100
|
+
if (!Array.isArray(keys)) {
|
|
101
|
+
return [];
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const seen: Set<string> = new Set();
|
|
105
|
+
const normalized: Array<string> = [];
|
|
106
|
+
|
|
107
|
+
for (const key of keys) {
|
|
108
|
+
if (typeof key !== "string") {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const trimmed: string = key.trim();
|
|
113
|
+
|
|
114
|
+
if (trimmed.length === 0 || seen.has(trimmed)) {
|
|
115
|
+
continue;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
seen.add(trimmed);
|
|
119
|
+
normalized.push(trimmed);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
return normalized.sort((a: string, b: string): number => {
|
|
123
|
+
return a.localeCompare(b);
|
|
124
|
+
});
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
export type GetAttributeValueFunction = (data: {
|
|
128
|
+
item: unknown;
|
|
129
|
+
attributesColumnKey: string;
|
|
130
|
+
attributeKey: string;
|
|
131
|
+
}) => unknown;
|
|
132
|
+
|
|
133
|
+
export const getAttributeValue: GetAttributeValueFunction = (data: {
|
|
134
|
+
item: unknown;
|
|
135
|
+
attributesColumnKey: string;
|
|
136
|
+
attributeKey: string;
|
|
137
|
+
}): unknown => {
|
|
138
|
+
const attributes: unknown = (data.item as JSONObject | undefined)?.[
|
|
139
|
+
data.attributesColumnKey
|
|
140
|
+
];
|
|
141
|
+
|
|
142
|
+
if (!attributes || typeof attributes !== "object") {
|
|
143
|
+
return undefined;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
return (attributes as JSONObject)[data.attributeKey];
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
export type GetAttributeExportValueFunction = (value: unknown) => string;
|
|
150
|
+
|
|
151
|
+
export const getAttributeExportValue: GetAttributeExportValueFunction = (
|
|
152
|
+
value: unknown,
|
|
153
|
+
): string => {
|
|
154
|
+
if (value === undefined || value === null) {
|
|
155
|
+
return "";
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (Array.isArray(value)) {
|
|
159
|
+
return value
|
|
160
|
+
.map((entry: unknown): string => {
|
|
161
|
+
return getAttributeExportValue(entry);
|
|
162
|
+
})
|
|
163
|
+
.join(", ");
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (typeof value === "object") {
|
|
167
|
+
return JSON.stringify(value);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return String(value);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export type RenderAttributeValueFunction = (data: {
|
|
174
|
+
value: unknown;
|
|
175
|
+
noValueMessage?: string | undefined;
|
|
176
|
+
}) => ReactElement;
|
|
177
|
+
|
|
178
|
+
/*
|
|
179
|
+
* Map values arrive as strings, but the ingest side is free to write a
|
|
180
|
+
* flattened object or an array under a key, and a project can PUT anything
|
|
181
|
+
* over the API - so this renders whatever it is handed rather than assuming.
|
|
182
|
+
*/
|
|
183
|
+
export const renderAttributeValue: RenderAttributeValueFunction = (data: {
|
|
184
|
+
value: unknown;
|
|
185
|
+
noValueMessage?: string | undefined;
|
|
186
|
+
}): ReactElement => {
|
|
187
|
+
const noValueMessage: string = data.noValueMessage ?? "-";
|
|
188
|
+
const text: string = getAttributeExportValue(data.value);
|
|
189
|
+
|
|
190
|
+
if (text.length === 0) {
|
|
191
|
+
return <span className="text-gray-400">{noValueMessage}</span>;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
return (
|
|
195
|
+
<span className="break-words" title={text}>
|
|
196
|
+
{text}
|
|
197
|
+
</span>
|
|
198
|
+
);
|
|
199
|
+
};
|
|
200
|
+
|
|
201
|
+
export type GetAttributeColumnsFunction = <
|
|
202
|
+
TBaseModel extends BaseModel | AnalyticsBaseModel,
|
|
203
|
+
>(data: {
|
|
204
|
+
attributeKeys: Array<string>;
|
|
205
|
+
attributesColumnKey: string;
|
|
206
|
+
isHiddenByDefault?: boolean | undefined;
|
|
207
|
+
noValueMessage?: string | undefined;
|
|
208
|
+
}) => Columns<TBaseModel>;
|
|
209
|
+
|
|
210
|
+
/*
|
|
211
|
+
* `isRemovable` is what separates these from every other optional column: a
|
|
212
|
+
* viewer who switches an attribute column off still has it sitting in the
|
|
213
|
+
* picker forever, and a table whose source writes a few hundred keys would
|
|
214
|
+
* accumulate an unusable list. Removing drops the id from the stored layout,
|
|
215
|
+
* which is the same thing as the column never having been added.
|
|
216
|
+
*/
|
|
217
|
+
export const getAttributeColumns: GetAttributeColumnsFunction = <
|
|
218
|
+
TBaseModel extends BaseModel | AnalyticsBaseModel,
|
|
219
|
+
>(data: {
|
|
220
|
+
attributeKeys: Array<string>;
|
|
221
|
+
attributesColumnKey: string;
|
|
222
|
+
isHiddenByDefault?: boolean | undefined;
|
|
223
|
+
noValueMessage?: string | undefined;
|
|
224
|
+
}): Columns<TBaseModel> => {
|
|
225
|
+
const { attributesColumnKey } = data;
|
|
226
|
+
|
|
227
|
+
return (data.attributeKeys || []).map(
|
|
228
|
+
(attributeKey: string): Column<TBaseModel> => {
|
|
229
|
+
return {
|
|
230
|
+
id: getAttributeColumnId({ attributesColumnKey, attributeKey }),
|
|
231
|
+
field: {
|
|
232
|
+
[attributesColumnKey]: true,
|
|
233
|
+
} as Column<TBaseModel>["field"],
|
|
234
|
+
selectedProperty: attributeKey,
|
|
235
|
+
title: attributeKey,
|
|
236
|
+
type: FieldType.Element,
|
|
237
|
+
// See the file header: a map key is not something the sort path can express.
|
|
238
|
+
disableSort: true,
|
|
239
|
+
isHiddenByDefault: Boolean(data.isHiddenByDefault),
|
|
240
|
+
isRemovable: true,
|
|
241
|
+
getExportValue: (item: TBaseModel): string => {
|
|
242
|
+
return getAttributeExportValue(
|
|
243
|
+
getAttributeValue({ item, attributesColumnKey, attributeKey }),
|
|
244
|
+
);
|
|
245
|
+
},
|
|
246
|
+
getElement: (item: TBaseModel): ReactElement => {
|
|
247
|
+
return renderAttributeValue({
|
|
248
|
+
value: getAttributeValue({
|
|
249
|
+
item,
|
|
250
|
+
attributesColumnKey,
|
|
251
|
+
attributeKey,
|
|
252
|
+
}),
|
|
253
|
+
noValueMessage: data.noValueMessage,
|
|
254
|
+
});
|
|
255
|
+
},
|
|
256
|
+
};
|
|
257
|
+
},
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
export type GetAttributeKeysFromColumnPreferenceFunction = (data: {
|
|
262
|
+
preference: ColumnPreference | null | undefined;
|
|
263
|
+
attributesColumnKey: string;
|
|
264
|
+
/*
|
|
265
|
+
* Ids of the columns the table declares for itself. A declared column that
|
|
266
|
+
* happens to sit under the same prefix is not an attribute column, and
|
|
267
|
+
* regenerating one for it would put two columns with the same id in the
|
|
268
|
+
* picker.
|
|
269
|
+
*/
|
|
270
|
+
reservedColumnIds?: Array<string> | undefined;
|
|
271
|
+
}) => Array<string>;
|
|
272
|
+
|
|
273
|
+
/*
|
|
274
|
+
* The attribute columns a stored layout implies.
|
|
275
|
+
*
|
|
276
|
+
* Read from the RAW preference rather than the sanitized one: sanitizing drops
|
|
277
|
+
* ids that name no current column, and an attribute column is only ever a
|
|
278
|
+
* current column *because* its id is in here. `hidden` is scanned as well as
|
|
279
|
+
* `order` - buildColumnPreference always writes both, but a payload from an
|
|
280
|
+
* older release (or one hand-edited in devtools) may carry only the one, and
|
|
281
|
+
* losing the column would silently discard the viewer's choice.
|
|
282
|
+
*/
|
|
283
|
+
export const getAttributeKeysFromColumnPreference: GetAttributeKeysFromColumnPreferenceFunction =
|
|
284
|
+
(data: {
|
|
285
|
+
preference: ColumnPreference | null | undefined;
|
|
286
|
+
attributesColumnKey: string;
|
|
287
|
+
reservedColumnIds?: Array<string> | undefined;
|
|
288
|
+
}): Array<string> => {
|
|
289
|
+
if (!data.preference) {
|
|
290
|
+
return [];
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const reserved: Set<string> = new Set(data.reservedColumnIds || []);
|
|
294
|
+
const seen: Set<string> = new Set();
|
|
295
|
+
const attributeKeys: Array<string> = [];
|
|
296
|
+
|
|
297
|
+
const columnIds: Array<string> = [
|
|
298
|
+
...(data.preference.order || []),
|
|
299
|
+
...(data.preference.hidden || []),
|
|
300
|
+
];
|
|
301
|
+
|
|
302
|
+
for (const columnId of columnIds) {
|
|
303
|
+
if (reserved.has(columnId)) {
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
const attributeKey: string | null = getAttributeKeyFromColumnId({
|
|
308
|
+
attributesColumnKey: data.attributesColumnKey,
|
|
309
|
+
columnId: columnId,
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
if (!attributeKey || seen.has(attributeKey)) {
|
|
313
|
+
continue;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
seen.add(attributeKey);
|
|
317
|
+
attributeKeys.push(attributeKey);
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
return attributeKeys;
|
|
321
|
+
};
|
|
@@ -62,7 +62,14 @@ import TableColumn from "../Table/Types/Column";
|
|
|
62
62
|
import FieldType from "../Types/FieldType";
|
|
63
63
|
import ModelTableColumn from "./Column";
|
|
64
64
|
import Columns from "./Columns";
|
|
65
|
-
import ColumnCustomizationModal
|
|
65
|
+
import ColumnCustomizationModal, {
|
|
66
|
+
AddableColumnsConfig,
|
|
67
|
+
} from "./ColumnCustomizationModal";
|
|
68
|
+
import {
|
|
69
|
+
getAttributeColumns,
|
|
70
|
+
getAttributeKeysFromColumnPreference,
|
|
71
|
+
normalizeAttributeKeys,
|
|
72
|
+
} from "./AttributeColumns";
|
|
66
73
|
import {
|
|
67
74
|
ColumnPreference,
|
|
68
75
|
CustomizableColumn,
|
|
@@ -364,6 +371,40 @@ export interface BaseTableProps<
|
|
|
364
371
|
* Only meaningful for resources with a `customFields` column of their own.
|
|
365
372
|
*/
|
|
366
373
|
customFieldsModelType?: { new (): BaseModel } | undefined;
|
|
374
|
+
|
|
375
|
+
/**
|
|
376
|
+
* Let the viewer add a column for any key inside a Map(String, String)
|
|
377
|
+
* column on the model — `attributes` on logs, spans and security events.
|
|
378
|
+
*
|
|
379
|
+
* Those keys are per-payload rather than per-schema, so they cannot ship as
|
|
380
|
+
* columns and there can be hundreds of them. Instead the picker grows an
|
|
381
|
+
* "add a column" search box fed by `fetchAttributeKeys`, and whatever the
|
|
382
|
+
* viewer adds becomes an ordinary column: hideable, re-orderable, exported,
|
|
383
|
+
* and remembered in the same stored layout as every other column.
|
|
384
|
+
*
|
|
385
|
+
* Requires `userPreferencesKey` and column customization to be on, since the
|
|
386
|
+
* chosen keys are recovered from the stored layout.
|
|
387
|
+
*/
|
|
388
|
+
attributeColumnsProps?:
|
|
389
|
+
| {
|
|
390
|
+
/*
|
|
391
|
+
* The map column the generated columns read from. Must be a real
|
|
392
|
+
* column on the model; anything else is ignored rather than throwing
|
|
393
|
+
* the table away.
|
|
394
|
+
*/
|
|
395
|
+
columnKey: string;
|
|
396
|
+
/*
|
|
397
|
+
* Every key the viewer may pick from. Called the first time the picker
|
|
398
|
+
* opens rather than on mount — it is typically a wide scan, and a
|
|
399
|
+
* table nobody customizes should not pay for it.
|
|
400
|
+
*/
|
|
401
|
+
fetchAttributeKeys: () => Promise<Array<string>>;
|
|
402
|
+
title?: string | undefined;
|
|
403
|
+
description?: string | undefined;
|
|
404
|
+
placeholder?: string | undefined;
|
|
405
|
+
emptyMessage?: string | undefined;
|
|
406
|
+
}
|
|
407
|
+
| undefined;
|
|
367
408
|
}
|
|
368
409
|
|
|
369
410
|
export interface ComponentProps<
|
|
@@ -501,14 +542,6 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
501
542
|
showAs !== ShowAs.OrderedStatesList,
|
|
502
543
|
);
|
|
503
544
|
|
|
504
|
-
const allColumns: Columns<TBaseModel> = useMemo(() => {
|
|
505
|
-
if (customFieldColumns.columns.length === 0) {
|
|
506
|
-
return props.columns || [];
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
return [...(props.columns || []), ...customFieldColumns.columns];
|
|
510
|
-
}, [props.columns, customFieldColumns.columns]);
|
|
511
|
-
|
|
512
545
|
type ReadStoredColumnPreferenceFunction = () => ColumnPreference | null;
|
|
513
546
|
|
|
514
547
|
const readStoredColumnPreference: ReadStoredColumnPreferenceFunction =
|
|
@@ -531,6 +564,68 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
531
564
|
const [showColumnCustomizationModal, setShowColumnCustomizationModal] =
|
|
532
565
|
useState<boolean>(false);
|
|
533
566
|
|
|
567
|
+
/*
|
|
568
|
+
* ---------------------------------------------------------------------
|
|
569
|
+
* Attribute columns
|
|
570
|
+
* ---------------------------------------------------------------------
|
|
571
|
+
*
|
|
572
|
+
* A column per viewer-chosen key inside the model's map column. There is no
|
|
573
|
+
* second store for "which keys did they pick": a column exists precisely
|
|
574
|
+
* when its id is in the stored layout, and the id encodes the key. That is
|
|
575
|
+
* what makes "Reset to default" take them away, a saved TableView bring its
|
|
576
|
+
* own, and the two halves of a layout impossible to desynchronize.
|
|
577
|
+
*
|
|
578
|
+
* Derived from the RAW preference, before sanitizing: sanitizing drops ids
|
|
579
|
+
* that name no current column, and these are only current columns *because*
|
|
580
|
+
* their ids are in there.
|
|
581
|
+
*/
|
|
582
|
+
const attributeColumnKey: string | null = useMemo(() => {
|
|
583
|
+
const columnKey: string | undefined =
|
|
584
|
+
props.attributeColumnsProps?.columnKey;
|
|
585
|
+
|
|
586
|
+
if (!columnKey || !isColumnCustomizationEnabled) {
|
|
587
|
+
return null;
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/*
|
|
591
|
+
* A key that is not a column on the model would make getSelectFromColumns
|
|
592
|
+
* throw and blank the whole table. A misconfigured prop should cost the
|
|
593
|
+
* feature, not the page.
|
|
594
|
+
*/
|
|
595
|
+
return model.hasColumn(columnKey) ? columnKey : null;
|
|
596
|
+
}, [props.attributeColumnsProps?.columnKey, isColumnCustomizationEnabled]);
|
|
597
|
+
|
|
598
|
+
const declaredColumns: Columns<TBaseModel> = useMemo(() => {
|
|
599
|
+
if (customFieldColumns.columns.length === 0) {
|
|
600
|
+
return props.columns || [];
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
return [...(props.columns || []), ...customFieldColumns.columns];
|
|
604
|
+
}, [props.columns, customFieldColumns.columns]);
|
|
605
|
+
|
|
606
|
+
const attributeColumns: Columns<TBaseModel> = useMemo(() => {
|
|
607
|
+
if (!attributeColumnKey) {
|
|
608
|
+
return [];
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
return getAttributeColumns<TBaseModel>({
|
|
612
|
+
attributesColumnKey: attributeColumnKey,
|
|
613
|
+
attributeKeys: getAttributeKeysFromColumnPreference({
|
|
614
|
+
preference: columnPreference,
|
|
615
|
+
attributesColumnKey: attributeColumnKey,
|
|
616
|
+
reservedColumnIds: getColumnIds<TBaseModel>(declaredColumns),
|
|
617
|
+
}),
|
|
618
|
+
});
|
|
619
|
+
}, [attributeColumnKey, columnPreference, declaredColumns]);
|
|
620
|
+
|
|
621
|
+
const allColumns: Columns<TBaseModel> = useMemo(() => {
|
|
622
|
+
if (attributeColumns.length === 0) {
|
|
623
|
+
return declaredColumns;
|
|
624
|
+
}
|
|
625
|
+
|
|
626
|
+
return [...declaredColumns, ...attributeColumns];
|
|
627
|
+
}, [declaredColumns, attributeColumns]);
|
|
628
|
+
|
|
534
629
|
/*
|
|
535
630
|
* Ids are derived over the *whole* declared set, so a column dropping out
|
|
536
631
|
* for lack of permission can never renumber the ones that remain.
|
|
@@ -584,6 +679,18 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
584
679
|
});
|
|
585
680
|
};
|
|
586
681
|
|
|
682
|
+
/*
|
|
683
|
+
* The pool of keys the picker offers. Fetched the first time the picker
|
|
684
|
+
* opens, never on mount: it is a wide scan over the analytics table, and a
|
|
685
|
+
* table nobody customizes should not pay for it on every page load.
|
|
686
|
+
*/
|
|
687
|
+
const [attributeKeyPool, setAttributeKeyPool] = useState<{
|
|
688
|
+
keys: Array<string>;
|
|
689
|
+
isLoading: boolean;
|
|
690
|
+
error: string;
|
|
691
|
+
hasLoaded: boolean;
|
|
692
|
+
}>({ keys: [], isLoading: false, error: "", hasLoaded: false });
|
|
693
|
+
|
|
587
694
|
const [classicTableFilters, setClassicTableFilters] = useState<
|
|
588
695
|
Array<ClassicFilterType<TBaseModel>>
|
|
589
696
|
>([]);
|
|
@@ -1972,6 +2079,20 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
1972
2079
|
(selectFields as Dictionary<boolean>)[CustomFieldsColumnKey] = true;
|
|
1973
2080
|
}
|
|
1974
2081
|
|
|
2082
|
+
/*
|
|
2083
|
+
* Same reasoning for the attribute map column: a column added from the
|
|
2084
|
+
* picker appears without a refetch, so the map it reads has to have been
|
|
2085
|
+
* on the wire since the first request or the new column renders blank
|
|
2086
|
+
* until something else happens to reload the page.
|
|
2087
|
+
*/
|
|
2088
|
+
if (
|
|
2089
|
+
attributeColumnKey &&
|
|
2090
|
+
(hasPermissionToReadField(attributeColumnKey as keyof TBaseModel) ||
|
|
2091
|
+
User.isMasterAdmin())
|
|
2092
|
+
) {
|
|
2093
|
+
(selectFields as Dictionary<boolean>)[attributeColumnKey] = true;
|
|
2094
|
+
}
|
|
2095
|
+
|
|
1975
2096
|
const selectMoreFields: Array<keyof TBaseModel> = props.selectMoreFields
|
|
1976
2097
|
? (Object.keys(props.selectMoreFields) as Array<keyof TBaseModel>)
|
|
1977
2098
|
: [];
|
|
@@ -2431,12 +2552,77 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
2431
2552
|
}, [props.columns]);
|
|
2432
2553
|
|
|
2433
2554
|
/*
|
|
2434
|
-
* The viewer changed their layout,
|
|
2435
|
-
*
|
|
2555
|
+
* The viewer changed their layout, the project's custom field definitions
|
|
2556
|
+
* finally arrived, or an attribute column was added or removed.
|
|
2436
2557
|
*/
|
|
2437
2558
|
useEffect(() => {
|
|
2438
2559
|
serializeToTableColumns();
|
|
2439
|
-
}, [effectiveColumnPreference, customFieldColumns.columns]);
|
|
2560
|
+
}, [effectiveColumnPreference, customFieldColumns.columns, attributeColumns]);
|
|
2561
|
+
|
|
2562
|
+
/*
|
|
2563
|
+
* The attribute key pool is only ever needed by the picker, so it is fetched
|
|
2564
|
+
* when the picker first opens and kept for the rest of the page's life.
|
|
2565
|
+
* A failure costs the "add a column" search box and nothing else — the
|
|
2566
|
+
* columns the viewer already added are rebuilt from their stored layout and
|
|
2567
|
+
* do not depend on this at all.
|
|
2568
|
+
*/
|
|
2569
|
+
useEffect(() => {
|
|
2570
|
+
if (
|
|
2571
|
+
!showColumnCustomizationModal ||
|
|
2572
|
+
!attributeColumnKey ||
|
|
2573
|
+
!props.attributeColumnsProps ||
|
|
2574
|
+
attributeKeyPool.hasLoaded ||
|
|
2575
|
+
attributeKeyPool.isLoading
|
|
2576
|
+
) {
|
|
2577
|
+
return;
|
|
2578
|
+
}
|
|
2579
|
+
|
|
2580
|
+
const fetchAttributeKeys: () => Promise<Array<string>> =
|
|
2581
|
+
props.attributeColumnsProps.fetchAttributeKeys;
|
|
2582
|
+
|
|
2583
|
+
let isCancelled: boolean = false;
|
|
2584
|
+
|
|
2585
|
+
setAttributeKeyPool({
|
|
2586
|
+
keys: [],
|
|
2587
|
+
isLoading: true,
|
|
2588
|
+
error: "",
|
|
2589
|
+
hasLoaded: false,
|
|
2590
|
+
});
|
|
2591
|
+
|
|
2592
|
+
fetchAttributeKeys()
|
|
2593
|
+
.then((keys: Array<string>) => {
|
|
2594
|
+
if (isCancelled) {
|
|
2595
|
+
return;
|
|
2596
|
+
}
|
|
2597
|
+
|
|
2598
|
+
setAttributeKeyPool({
|
|
2599
|
+
keys: normalizeAttributeKeys(keys),
|
|
2600
|
+
isLoading: false,
|
|
2601
|
+
error: "",
|
|
2602
|
+
hasLoaded: true,
|
|
2603
|
+
});
|
|
2604
|
+
})
|
|
2605
|
+
.catch((err: Error) => {
|
|
2606
|
+
if (isCancelled) {
|
|
2607
|
+
return;
|
|
2608
|
+
}
|
|
2609
|
+
|
|
2610
|
+
setAttributeKeyPool({
|
|
2611
|
+
keys: [],
|
|
2612
|
+
isLoading: false,
|
|
2613
|
+
error: API.getFriendlyMessage(err),
|
|
2614
|
+
/*
|
|
2615
|
+
* Not "loaded": closing and reopening the picker is the only retry
|
|
2616
|
+
* anyone would think to try, so let it be one.
|
|
2617
|
+
*/
|
|
2618
|
+
hasLoaded: false,
|
|
2619
|
+
});
|
|
2620
|
+
});
|
|
2621
|
+
|
|
2622
|
+
return () => {
|
|
2623
|
+
isCancelled = true;
|
|
2624
|
+
};
|
|
2625
|
+
}, [showColumnCustomizationModal, attributeColumnKey]);
|
|
2440
2626
|
|
|
2441
2627
|
const setActionSchema: VoidFunction = () => {
|
|
2442
2628
|
const permissions: Array<Permission> = PermissionUtil.getAllPermissions();
|
|
@@ -4007,13 +4193,15 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
4007
4193
|
|
|
4008
4194
|
type GetPickerEntriesFunction = (
|
|
4009
4195
|
preference: ColumnPreference | null,
|
|
4196
|
+
columns?: Columns<TBaseModel> | undefined,
|
|
4010
4197
|
) => Array<CustomizableColumn<TBaseModel>>;
|
|
4011
4198
|
|
|
4012
4199
|
const getPickerEntries: GetPickerEntriesFunction = (
|
|
4013
4200
|
preference: ColumnPreference | null,
|
|
4201
|
+
columns?: Columns<TBaseModel> | undefined,
|
|
4014
4202
|
): Array<CustomizableColumn<TBaseModel>> => {
|
|
4015
4203
|
return getCustomizableColumns<TBaseModel>({
|
|
4016
|
-
columns: allColumns,
|
|
4204
|
+
columns: columns || allColumns,
|
|
4017
4205
|
preference: preference,
|
|
4018
4206
|
}).filter((entry: CustomizableColumn<TBaseModel>) => {
|
|
4019
4207
|
return isPickableColumn(entry.column);
|
|
@@ -4055,8 +4243,16 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
4055
4243
|
* would be pinned to the column set of the release they opened it in, and
|
|
4056
4244
|
* columns added later would arrive already stale.
|
|
4057
4245
|
*/
|
|
4246
|
+
/*
|
|
4247
|
+
* Over the DECLARED columns only. An attribute column exists because the
|
|
4248
|
+
* viewer added it, so the layout that has none of them is the default one
|
|
4249
|
+
* — otherwise adding a column and then removing it again would leave the
|
|
4250
|
+
* table pinned to a stored layout it no longer differs from.
|
|
4251
|
+
*/
|
|
4058
4252
|
const defaultPreference: ColumnPreference =
|
|
4059
|
-
buildColumnPreference<TBaseModel>(
|
|
4253
|
+
buildColumnPreference<TBaseModel>(
|
|
4254
|
+
getPickerEntries(null, declaredColumns),
|
|
4255
|
+
);
|
|
4060
4256
|
|
|
4061
4257
|
const isBackToDefault: boolean =
|
|
4062
4258
|
JSON.stringify(preference) === JSON.stringify(defaultPreference);
|
|
@@ -4089,6 +4285,58 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
4089
4285
|
setShowColumnCustomizationModal(false);
|
|
4090
4286
|
};
|
|
4091
4287
|
|
|
4288
|
+
/*
|
|
4289
|
+
* The pool the picker offers, as picker entries rather than model columns,
|
|
4290
|
+
* so the modal can stage one straight into its list. Every one starts
|
|
4291
|
+
* removable — the whole point is that the viewer can take it away again.
|
|
4292
|
+
*/
|
|
4293
|
+
const addableAttributeColumns: Array<CustomizableColumn<TBaseModel>> =
|
|
4294
|
+
useMemo(() => {
|
|
4295
|
+
if (!attributeColumnKey || attributeKeyPool.keys.length === 0) {
|
|
4296
|
+
return [];
|
|
4297
|
+
}
|
|
4298
|
+
|
|
4299
|
+
return getAttributeColumns<TBaseModel>({
|
|
4300
|
+
attributesColumnKey: attributeColumnKey,
|
|
4301
|
+
attributeKeys: attributeKeyPool.keys,
|
|
4302
|
+
}).map(
|
|
4303
|
+
(
|
|
4304
|
+
column: ModelTableColumn<TBaseModel>,
|
|
4305
|
+
index: number,
|
|
4306
|
+
): CustomizableColumn<TBaseModel> => {
|
|
4307
|
+
return {
|
|
4308
|
+
id: column.id || `${attributeColumnKey}.${index}`,
|
|
4309
|
+
column: column,
|
|
4310
|
+
isVisible: true,
|
|
4311
|
+
isPinned: false,
|
|
4312
|
+
isRemovable: true,
|
|
4313
|
+
};
|
|
4314
|
+
},
|
|
4315
|
+
);
|
|
4316
|
+
}, [attributeColumnKey, attributeKeyPool.keys]);
|
|
4317
|
+
|
|
4318
|
+
const getAddableColumnsConfig: () =>
|
|
4319
|
+
| AddableColumnsConfig<TBaseModel>
|
|
4320
|
+
| undefined = (): AddableColumnsConfig<TBaseModel> | undefined => {
|
|
4321
|
+
if (!attributeColumnKey || !props.attributeColumnsProps) {
|
|
4322
|
+
return undefined;
|
|
4323
|
+
}
|
|
4324
|
+
|
|
4325
|
+
return {
|
|
4326
|
+
title: props.attributeColumnsProps.title || tx("Add Attribute Column"),
|
|
4327
|
+
description:
|
|
4328
|
+
props.attributeColumnsProps.description ||
|
|
4329
|
+
tx(
|
|
4330
|
+
"Attributes vary per event, so they are not listed above. Search for one to add it as a column.",
|
|
4331
|
+
),
|
|
4332
|
+
placeholder: props.attributeColumnsProps.placeholder || "Search...",
|
|
4333
|
+
emptyMessage: props.attributeColumnsProps.emptyMessage,
|
|
4334
|
+
isLoading: attributeKeyPool.isLoading,
|
|
4335
|
+
errorMessage: attributeKeyPool.error,
|
|
4336
|
+
columns: addableAttributeColumns,
|
|
4337
|
+
};
|
|
4338
|
+
};
|
|
4339
|
+
|
|
4092
4340
|
const getColumnCustomizationModal: () => ReactElement | null =
|
|
4093
4341
|
(): ReactElement | null => {
|
|
4094
4342
|
if (!showColumnCustomizationModal || !isColumnCustomizationEnabled) {
|
|
@@ -4098,6 +4346,7 @@ const BaseModelTable: <TBaseModel extends BaseModel | AnalyticsBaseModel>(
|
|
|
4098
4346
|
return (
|
|
4099
4347
|
<ColumnCustomizationModal<TBaseModel>
|
|
4100
4348
|
columns={getPickerEntries(effectiveColumnPreference)}
|
|
4349
|
+
addableColumns={getAddableColumnsConfig()}
|
|
4101
4350
|
isDefaultLayout={isEmptyColumnPreference(effectiveColumnPreference)}
|
|
4102
4351
|
title={tx("Customize Columns")}
|
|
4103
4352
|
onSave={onColumnCustomizationSave}
|
|
@@ -36,6 +36,14 @@ export default interface Columns<
|
|
|
36
36
|
isNotCustomizable?: boolean | undefined;
|
|
37
37
|
// Start hidden. The viewer can still switch it on from the picker.
|
|
38
38
|
isHiddenByDefault?: boolean | undefined;
|
|
39
|
+
/*
|
|
40
|
+
* This column exists only because the viewer added it, so the picker offers
|
|
41
|
+
* to take it away again rather than only switching it off. Set it on columns
|
|
42
|
+
* generated from viewer-chosen keys (see AttributeColumns) - a table that
|
|
43
|
+
* ships a column should never be removable, because nothing would put it
|
|
44
|
+
* back.
|
|
45
|
+
*/
|
|
46
|
+
isRemovable?: boolean | undefined;
|
|
39
47
|
contentClassName?: string | undefined;
|
|
40
48
|
colSpan?: number | undefined;
|
|
41
49
|
disableSort?: boolean;
|