@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,369 @@
|
|
|
1
|
+
import CodeEditor from "./CodeEditor";
|
|
2
|
+
import Icon from "../Icon/Icon";
|
|
3
|
+
import CodeType from "../../../Types/Code/CodeType";
|
|
4
|
+
import IconProp from "../../../Types/Icon/IconProp";
|
|
5
|
+
import {
|
|
6
|
+
YamlSyntaxCheckResult,
|
|
7
|
+
checkYamlSyntax,
|
|
8
|
+
describeYamlSyntaxError,
|
|
9
|
+
} from "../../../Types/Code/YamlSyntax";
|
|
10
|
+
import React, {
|
|
11
|
+
FunctionComponent,
|
|
12
|
+
ReactElement,
|
|
13
|
+
useEffect,
|
|
14
|
+
useId,
|
|
15
|
+
useMemo,
|
|
16
|
+
useRef,
|
|
17
|
+
useState,
|
|
18
|
+
} from "react";
|
|
19
|
+
|
|
20
|
+
export interface ComponentProps {
|
|
21
|
+
initialValue?: string | undefined;
|
|
22
|
+
value?: string | undefined;
|
|
23
|
+
onChange?: ((value: string) => void) | undefined;
|
|
24
|
+
onBlur?: (() => void) | undefined;
|
|
25
|
+
onFocus?: (() => void) | undefined;
|
|
26
|
+
/** Shown in the header bar as a hint. Never written into the document. */
|
|
27
|
+
placeholder?: string | undefined;
|
|
28
|
+
error?: string | undefined;
|
|
29
|
+
readOnly?: boolean | undefined;
|
|
30
|
+
tabIndex?: number | undefined;
|
|
31
|
+
dataTestId?: string | undefined;
|
|
32
|
+
ariaLabelledby?: string | undefined;
|
|
33
|
+
height?: string | undefined;
|
|
34
|
+
className?: string | undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** How long the document sits still before it is parsed again. */
|
|
38
|
+
export const YAML_VALIDATION_DEBOUNCE_MS: number = 250;
|
|
39
|
+
|
|
40
|
+
const DEFAULT_HINT: string =
|
|
41
|
+
"Indentation is significant — use 2 spaces per level. Tabs are not valid YAML.";
|
|
42
|
+
|
|
43
|
+
const MAC_PLATFORM_PATTERN: RegExp = /Mac|iPhone|iPad|iPod/i;
|
|
44
|
+
|
|
45
|
+
type IsMacFunction = () => boolean;
|
|
46
|
+
|
|
47
|
+
/*
|
|
48
|
+
* Monaco binds toggleTabFocusMode to Ctrl+M everywhere except macOS, where the
|
|
49
|
+
* `mac` override in its keybinding makes it Ctrl+Shift+M. Naming the wrong key
|
|
50
|
+
* turns the keyboard-trap advisory (WCAG 2.1.2) into a false one.
|
|
51
|
+
*/
|
|
52
|
+
const isMac: IsMacFunction = (): boolean => {
|
|
53
|
+
if (typeof navigator === "undefined") {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const platform: string =
|
|
58
|
+
(navigator as { platform?: string }).platform || navigator.userAgent || "";
|
|
59
|
+
|
|
60
|
+
return MAC_PLATFORM_PATTERN.test(platform);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
/*
|
|
64
|
+
* Callers reach us through Form's currentValues, which is `any`, so a
|
|
65
|
+
* non-string can arrive on a prop typed as string. CodeEditor stringifies for
|
|
66
|
+
* display; we mirror that so the status bar judges the same text the user
|
|
67
|
+
* sees.
|
|
68
|
+
*/
|
|
69
|
+
type ToTextFunction = (value: string | undefined) => string;
|
|
70
|
+
|
|
71
|
+
const toText: ToTextFunction = (value: string | undefined): string => {
|
|
72
|
+
if (value === undefined || value === null) {
|
|
73
|
+
return "";
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (typeof value !== "string") {
|
|
77
|
+
return JSON.stringify(value, null, 4);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return value;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
type CountLinesFunction = (text: string) => number;
|
|
84
|
+
|
|
85
|
+
const countLines: CountLinesFunction = (text: string): number => {
|
|
86
|
+
return text.split("\n").length;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* A YAML document editor: Monaco with the YAML grammar and YAML-safe
|
|
91
|
+
* indentation, wrapped in chrome that names the language, copies the document
|
|
92
|
+
* and reports - as you type - whether what is in the box actually parses.
|
|
93
|
+
*
|
|
94
|
+
* Exists because YAML fields used to render the rich-text Markdown editor.
|
|
95
|
+
* That was not only the wrong affordance (a Bold button over a Sigma rule);
|
|
96
|
+
* the Markdown editor round-trips its value through HTML and back, and
|
|
97
|
+
* indentation-sensitive YAML does not survive that intact.
|
|
98
|
+
*/
|
|
99
|
+
const YamlEditor: FunctionComponent<ComponentProps> = (
|
|
100
|
+
props: ComponentProps,
|
|
101
|
+
): ReactElement => {
|
|
102
|
+
const [text, setText] = useState<string>(() => {
|
|
103
|
+
return toText(props.value ?? props.initialValue);
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
/*
|
|
107
|
+
* The parse runs on a timer so it does not re-run on every keystroke, and
|
|
108
|
+
* so a half-typed line is not judged the instant it is typed.
|
|
109
|
+
*/
|
|
110
|
+
const [checkedText, setCheckedText] = useState<string>(() => {
|
|
111
|
+
return toText(props.value ?? props.initialValue);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
const instanceId: string = useId();
|
|
115
|
+
const hintId: string = `${instanceId}-yaml-hint`;
|
|
116
|
+
const statusId: string = `${instanceId}-yaml-status`;
|
|
117
|
+
const escapeHintId: string = `${instanceId}-yaml-escape`;
|
|
118
|
+
|
|
119
|
+
const [copied, setCopied] = useState<boolean>(false);
|
|
120
|
+
const copyResetTimer: React.MutableRefObject<ReturnType<
|
|
121
|
+
typeof setTimeout
|
|
122
|
+
> | null> = useRef<ReturnType<typeof setTimeout> | null>(null);
|
|
123
|
+
|
|
124
|
+
// `value` is the controlled prop; `initialValue` only seeds it.
|
|
125
|
+
useEffect(() => {
|
|
126
|
+
setText(
|
|
127
|
+
toText(props.value === undefined ? props.initialValue : props.value),
|
|
128
|
+
);
|
|
129
|
+
}, [props.value, props.initialValue]);
|
|
130
|
+
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
const timer: ReturnType<typeof setTimeout> = setTimeout(() => {
|
|
133
|
+
setCheckedText(text);
|
|
134
|
+
}, YAML_VALIDATION_DEBOUNCE_MS);
|
|
135
|
+
|
|
136
|
+
return () => {
|
|
137
|
+
clearTimeout(timer);
|
|
138
|
+
};
|
|
139
|
+
}, [text]);
|
|
140
|
+
|
|
141
|
+
useEffect(() => {
|
|
142
|
+
return () => {
|
|
143
|
+
if (copyResetTimer.current) {
|
|
144
|
+
clearTimeout(copyResetTimer.current);
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
}, []);
|
|
148
|
+
|
|
149
|
+
const syntax: YamlSyntaxCheckResult = useMemo(() => {
|
|
150
|
+
return checkYamlSyntax(checkedText);
|
|
151
|
+
}, [checkedText]);
|
|
152
|
+
|
|
153
|
+
const isEmpty: boolean = text.trim() === "";
|
|
154
|
+
|
|
155
|
+
type HandleCopyFunction = () => void;
|
|
156
|
+
|
|
157
|
+
const handleCopy: HandleCopyFunction = (): void => {
|
|
158
|
+
if (
|
|
159
|
+
typeof navigator === "undefined" ||
|
|
160
|
+
!navigator.clipboard ||
|
|
161
|
+
typeof navigator.clipboard.writeText !== "function"
|
|
162
|
+
) {
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
void Promise.resolve(navigator.clipboard.writeText(text)).catch(() => {
|
|
167
|
+
/* A denied clipboard permission is not worth an error state. */
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
setCopied(true);
|
|
171
|
+
|
|
172
|
+
if (copyResetTimer.current) {
|
|
173
|
+
clearTimeout(copyResetTimer.current);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
copyResetTimer.current = setTimeout(() => {
|
|
177
|
+
setCopied(false);
|
|
178
|
+
}, 2000);
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
/*
|
|
182
|
+
* One line, one message, five states. Whichever arm wins, the status bar is
|
|
183
|
+
* the only place the field says anything - the editor below is not given
|
|
184
|
+
* the error, so nothing is printed twice.
|
|
185
|
+
*/
|
|
186
|
+
type StatusShape = {
|
|
187
|
+
tone: "error" | "valid" | "invalid" | "empty";
|
|
188
|
+
icon: IconProp;
|
|
189
|
+
message: string;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
const status: StatusShape = useMemo(() => {
|
|
193
|
+
/*
|
|
194
|
+
* The form re-validates synchronously on every keystroke and the field is
|
|
195
|
+
* marked touched from the first one, so props.error arrives undebounced.
|
|
196
|
+
* Deferring to it only once the parse has caught up with the text keeps a
|
|
197
|
+
* red message off a half-typed line - and the moment they agree, the
|
|
198
|
+
* form's sentence wins, because it is the one blocking Save.
|
|
199
|
+
*/
|
|
200
|
+
if (props.error && checkedText === text) {
|
|
201
|
+
return {
|
|
202
|
+
tone: "error",
|
|
203
|
+
icon: IconProp.Error,
|
|
204
|
+
message: props.error,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
if (isEmpty) {
|
|
209
|
+
return {
|
|
210
|
+
tone: "empty",
|
|
211
|
+
icon: IconProp.Info,
|
|
212
|
+
message: "Nothing entered yet.",
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/*
|
|
217
|
+
* checkYamlSyntax declines to judge a document whose shape is only known
|
|
218
|
+
* at run time. Saying "Valid YAML" about text nobody parsed would be a
|
|
219
|
+
* claim this component cannot make.
|
|
220
|
+
*/
|
|
221
|
+
if (syntax.wasSkipped) {
|
|
222
|
+
return {
|
|
223
|
+
tone: "empty",
|
|
224
|
+
icon: IconProp.Info,
|
|
225
|
+
message: "Contains template expressions — syntax not checked.",
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (syntax.isValid) {
|
|
230
|
+
const lines: number = countLines(text);
|
|
231
|
+
|
|
232
|
+
return {
|
|
233
|
+
tone: "valid",
|
|
234
|
+
icon: IconProp.CheckCircle,
|
|
235
|
+
message: `Valid YAML · ${lines} ${lines === 1 ? "line" : "lines"}`,
|
|
236
|
+
};
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
return {
|
|
240
|
+
tone: "invalid",
|
|
241
|
+
icon: IconProp.Alert,
|
|
242
|
+
message: describeYamlSyntaxError(syntax),
|
|
243
|
+
};
|
|
244
|
+
}, [props.error, isEmpty, syntax, text, checkedText]);
|
|
245
|
+
|
|
246
|
+
/*
|
|
247
|
+
* Light-theme values only, one step darker than the obvious choice so the
|
|
248
|
+
* 12px status text clears 4.5:1 on bg-gray-50 (WCAG 1.4.3). Dark mode is
|
|
249
|
+
* not written here: Styles/Theme.css remaps these very classes globally
|
|
250
|
+
* under html.dark, at a specificity that beats any dark: variant a
|
|
251
|
+
* component could add - which is why no sibling component uses them.
|
|
252
|
+
*/
|
|
253
|
+
const statusToneClassName: string = {
|
|
254
|
+
error: "text-red-700",
|
|
255
|
+
invalid: "text-amber-700",
|
|
256
|
+
valid: "text-green-700",
|
|
257
|
+
empty: "text-gray-500",
|
|
258
|
+
}[status.tone];
|
|
259
|
+
|
|
260
|
+
const borderClassName: string = props.error
|
|
261
|
+
? "border-red-300 focus-within:border-red-500 focus-within:ring-red-500"
|
|
262
|
+
: "border-gray-300 focus-within:border-indigo-500 focus-within:ring-indigo-500";
|
|
263
|
+
|
|
264
|
+
const escapeHint: string = isMac()
|
|
265
|
+
? "Press Control plus Shift plus M to toggle whether the Tab key indents or moves focus out of this editor."
|
|
266
|
+
: "Press Control plus M to toggle whether the Tab key indents or moves focus out of this editor.";
|
|
267
|
+
|
|
268
|
+
return (
|
|
269
|
+
<div
|
|
270
|
+
data-testid="yaml-editor"
|
|
271
|
+
className={
|
|
272
|
+
props.className ||
|
|
273
|
+
`overflow-hidden rounded-md border bg-white shadow-sm transition-shadow focus-within:ring-1 ${borderClassName}`
|
|
274
|
+
}
|
|
275
|
+
>
|
|
276
|
+
<div className="flex items-center justify-between gap-3 border-b border-gray-200 bg-gray-50 px-3 py-1">
|
|
277
|
+
<div className="flex min-w-0 items-center gap-2">
|
|
278
|
+
<span className="inline-flex shrink-0 items-center rounded border border-indigo-100 bg-indigo-50 px-1.5 py-0.5 text-[10px] font-semibold uppercase tracking-wide text-indigo-700">
|
|
279
|
+
YAML
|
|
280
|
+
</span>
|
|
281
|
+
<span
|
|
282
|
+
id={hintId}
|
|
283
|
+
data-testid="yaml-editor-hint"
|
|
284
|
+
className="truncate text-xs text-gray-500"
|
|
285
|
+
>
|
|
286
|
+
{props.placeholder || DEFAULT_HINT}
|
|
287
|
+
</span>
|
|
288
|
+
</div>
|
|
289
|
+
|
|
290
|
+
<button
|
|
291
|
+
type="button"
|
|
292
|
+
data-testid="yaml-editor-copy-button"
|
|
293
|
+
aria-label={
|
|
294
|
+
copied ? "YAML copied to clipboard" : "Copy YAML to clipboard"
|
|
295
|
+
}
|
|
296
|
+
disabled={isEmpty}
|
|
297
|
+
onClick={handleCopy}
|
|
298
|
+
className="inline-flex shrink-0 items-center gap-1 rounded px-2 py-1 text-xs text-gray-500 transition-colors hover:bg-gray-100 hover:text-gray-900 focus:outline-none focus:ring-2 focus:ring-indigo-500 focus:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-40 disabled:hover:bg-transparent disabled:hover:text-gray-500"
|
|
299
|
+
>
|
|
300
|
+
<Icon
|
|
301
|
+
icon={copied ? IconProp.Check : IconProp.Copy}
|
|
302
|
+
className="h-3.5 w-3.5"
|
|
303
|
+
/>
|
|
304
|
+
{copied ? "Copied" : "Copy"}
|
|
305
|
+
</button>
|
|
306
|
+
</div>
|
|
307
|
+
|
|
308
|
+
{/*
|
|
309
|
+
* Ahead of the editor in reading order, and referenced from Monaco's own
|
|
310
|
+
* input through aria-describedby - Monaco takes Tab to indent, so this
|
|
311
|
+
* is the only way out, and it has to be heard on the way in rather than
|
|
312
|
+
* discovered on the way past.
|
|
313
|
+
*/}
|
|
314
|
+
<span id={escapeHintId} className="sr-only">
|
|
315
|
+
{escapeHint}
|
|
316
|
+
</span>
|
|
317
|
+
|
|
318
|
+
<CodeEditor
|
|
319
|
+
type={CodeType.YAML}
|
|
320
|
+
ariaLabelledby={props.ariaLabelledby}
|
|
321
|
+
ariaDescribedby={`${escapeHintId} ${hintId} ${statusId}`}
|
|
322
|
+
ariaInvalid={Boolean(props.error)}
|
|
323
|
+
dataTestId={props.dataTestId}
|
|
324
|
+
tabIndex={props.tabIndex}
|
|
325
|
+
readOnly={props.readOnly}
|
|
326
|
+
height={props.height || "22rem"}
|
|
327
|
+
/*
|
|
328
|
+
* The chrome above owns the border, the background and the focus ring,
|
|
329
|
+
* so the editor contributes none of its own. `error` is deliberately
|
|
330
|
+
* not forwarded either - it would paint a second red border and print
|
|
331
|
+
* the message a second time under the box; the status bar below says
|
|
332
|
+
* it once.
|
|
333
|
+
*/
|
|
334
|
+
className="block w-full text-sm"
|
|
335
|
+
value={text}
|
|
336
|
+
onChange={(value: string) => {
|
|
337
|
+
setText(value);
|
|
338
|
+
|
|
339
|
+
if (props.onChange) {
|
|
340
|
+
props.onChange(value);
|
|
341
|
+
}
|
|
342
|
+
}}
|
|
343
|
+
onBlur={props.onBlur}
|
|
344
|
+
onFocus={props.onFocus}
|
|
345
|
+
/>
|
|
346
|
+
|
|
347
|
+
<div
|
|
348
|
+
id={statusId}
|
|
349
|
+
data-testid="yaml-editor-status"
|
|
350
|
+
role="status"
|
|
351
|
+
aria-live="polite"
|
|
352
|
+
className={`flex items-center gap-1.5 border-t border-gray-200 bg-gray-50 px-3 py-1.5 text-xs ${statusToneClassName}`}
|
|
353
|
+
>
|
|
354
|
+
<Icon icon={status.icon} className="h-3.5 w-3.5 shrink-0" />
|
|
355
|
+
<span className="min-w-0 break-words">{status.message}</span>
|
|
356
|
+
</div>
|
|
357
|
+
|
|
358
|
+
{/*
|
|
359
|
+
* The copy button's own name changes, but a button name is not a live
|
|
360
|
+
* region - nothing would speak the confirmation. This does.
|
|
361
|
+
*/}
|
|
362
|
+
<span role="status" aria-live="polite" className="sr-only">
|
|
363
|
+
{copied ? "YAML copied to clipboard" : ""}
|
|
364
|
+
</span>
|
|
365
|
+
</div>
|
|
366
|
+
);
|
|
367
|
+
};
|
|
368
|
+
|
|
369
|
+
export default YamlEditor;
|
|
@@ -631,6 +631,7 @@ const Detail: DetailFunction = <T extends GenericObject>(
|
|
|
631
631
|
field.fieldType === FieldType.CSS ||
|
|
632
632
|
field.fieldType === FieldType.JSON ||
|
|
633
633
|
field.fieldType === FieldType.JavaScript ||
|
|
634
|
+
field.fieldType === FieldType.YAML ||
|
|
634
635
|
field.fieldType === FieldType.Code)
|
|
635
636
|
) {
|
|
636
637
|
let language: string = "html";
|
|
@@ -682,6 +683,15 @@ const Detail: DetailFunction = <T extends GenericObject>(
|
|
|
682
683
|
language = "plaintext";
|
|
683
684
|
}
|
|
684
685
|
|
|
686
|
+
/*
|
|
687
|
+
* Rendered verbatim, unlike the JSON arm above: re-emitting YAML through
|
|
688
|
+
* a parser would drop the comments, anchors and key ordering that a hand
|
|
689
|
+
* written document (a Sigma rule, a collector config) is written with.
|
|
690
|
+
*/
|
|
691
|
+
if (field.fieldType === FieldType.YAML) {
|
|
692
|
+
language = "yaml";
|
|
693
|
+
}
|
|
694
|
+
|
|
685
695
|
data = (
|
|
686
696
|
<CodeBlock
|
|
687
697
|
code={data as string}
|
|
@@ -65,6 +65,12 @@ export type EntityDropdownValue =
|
|
|
65
65
|
| null
|
|
66
66
|
| undefined;
|
|
67
67
|
|
|
68
|
+
/** A label the Labels tab expanded, as handed to onLabelsBulkAdded. */
|
|
69
|
+
export interface BulkAddedLabel {
|
|
70
|
+
id: string;
|
|
71
|
+
name: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
68
74
|
export interface EntityDropdownProps {
|
|
69
75
|
/*
|
|
70
76
|
* Static options (enum-like). Used as initial cache when modelType is set;
|
|
@@ -104,6 +110,15 @@ export interface EntityDropdownProps {
|
|
|
104
110
|
* labeled entity, or force-show it.
|
|
105
111
|
*/
|
|
106
112
|
enableLabelsTab?: boolean | undefined;
|
|
113
|
+
/*
|
|
114
|
+
* Fired when the Labels tab actually expands a label into entries, with the
|
|
115
|
+
* labels that were applied. Bulk-add by tag is a one-time expansion - the
|
|
116
|
+
* labels are dropped the moment the entries land in the selection - so a
|
|
117
|
+
* caller that wants to remember which label a selection came from (to keep
|
|
118
|
+
* it in sync later, say) has no other way to find out. Names come along
|
|
119
|
+
* because the caller has no cheap way back from an id to one.
|
|
120
|
+
*/
|
|
121
|
+
onLabelsBulkAdded?: ((labels: Array<BulkAddedLabel>) => void) | undefined;
|
|
107
122
|
}
|
|
108
123
|
|
|
109
124
|
const SEARCH_DEBOUNCE_MS: number = 250;
|
|
@@ -969,6 +984,24 @@ const EntityDropdown: FunctionComponent<EntityDropdownProps> = (
|
|
|
969
984
|
additions.push(key);
|
|
970
985
|
}
|
|
971
986
|
}
|
|
987
|
+
/*
|
|
988
|
+
* Tell the caller which labels the user applied. Reported before the
|
|
989
|
+
* empty check below, and before the selection is cleared further down:
|
|
990
|
+
* the interesting case is a label whose entries are ALL already
|
|
991
|
+
* selected, which adds nothing here but is still a label the user asked
|
|
992
|
+
* this selection to be built from.
|
|
993
|
+
*/
|
|
994
|
+
props.onLabelsBulkAdded?.(
|
|
995
|
+
selectedLabelIds.map((labelId: string): BulkAddedLabel => {
|
|
996
|
+
const label: Label | undefined = allLabels.find(
|
|
997
|
+
(candidate: Label): boolean => {
|
|
998
|
+
return candidate._id?.toString() === labelId;
|
|
999
|
+
},
|
|
1000
|
+
);
|
|
1001
|
+
|
|
1002
|
+
return { id: labelId, name: label?.name || "" };
|
|
1003
|
+
}),
|
|
1004
|
+
);
|
|
972
1005
|
if (additions.length === 0) {
|
|
973
1006
|
setLabelError(
|
|
974
1007
|
"No new entries matched the selected labels (or you don't have read access).",
|
|
@@ -40,6 +40,7 @@ import Radio, { RadioValue } from "../../Radio/Radio";
|
|
|
40
40
|
import { BasicRadioButtonOption } from "../../RadioButtons/BasicRadioButtons";
|
|
41
41
|
import HorizontalRule from "../../HorizontalRule/HorizontalRule";
|
|
42
42
|
import MarkdownEditor from "../../Markdown.tsx/MarkdownEditor";
|
|
43
|
+
import YamlEditor from "../../CodeEditor/YamlEditor";
|
|
43
44
|
import useTranslateValue from "../../../Utils/Translation";
|
|
44
45
|
|
|
45
46
|
export interface ComponentProps<T extends GenericObject> {
|
|
@@ -474,6 +475,7 @@ const FormField: <T extends GenericObject>(
|
|
|
474
475
|
modelType={props.field.dropdownModal.type}
|
|
475
476
|
labelField={props.field.dropdownModal.labelField}
|
|
476
477
|
valueField={props.field.dropdownModal.valueField}
|
|
478
|
+
onLabelsBulkAdded={props.field.onLabelsBulkAdded}
|
|
477
479
|
options={props.field.dropdownOptions || []}
|
|
478
480
|
placeholder={translatedPlaceholder || ""}
|
|
479
481
|
value={
|
|
@@ -688,6 +690,35 @@ const FormField: <T extends GenericObject>(
|
|
|
688
690
|
/>
|
|
689
691
|
)}
|
|
690
692
|
|
|
693
|
+
{props.field.fieldType === FormFieldSchemaType.YAML && (
|
|
694
|
+
<YamlEditor
|
|
695
|
+
ariaLabelledby={fieldLabelId}
|
|
696
|
+
error={props.touched && props.error ? props.error : undefined}
|
|
697
|
+
tabIndex={0}
|
|
698
|
+
dataTestId={props.field.dataTestId}
|
|
699
|
+
onChange={async (value: string) => {
|
|
700
|
+
onChange(value);
|
|
701
|
+
props.setFieldValue(props.fieldName, value);
|
|
702
|
+
}}
|
|
703
|
+
onBlur={async () => {
|
|
704
|
+
props.setFieldTouched(props.fieldName, true);
|
|
705
|
+
}}
|
|
706
|
+
initialValue={
|
|
707
|
+
props.currentValues &&
|
|
708
|
+
(props.currentValues as any)[props.fieldName]
|
|
709
|
+
? (props.currentValues as any)[props.fieldName]
|
|
710
|
+
: ""
|
|
711
|
+
}
|
|
712
|
+
value={
|
|
713
|
+
props.currentValues &&
|
|
714
|
+
(props.currentValues as any)[props.fieldName]
|
|
715
|
+
? (props.currentValues as any)[props.fieldName]
|
|
716
|
+
: ""
|
|
717
|
+
}
|
|
718
|
+
placeholder={translatedPlaceholder || ""}
|
|
719
|
+
/>
|
|
720
|
+
)}
|
|
721
|
+
|
|
691
722
|
{props.field.fieldType === FormFieldSchemaType.Markdown && (
|
|
692
723
|
<MarkdownEditor
|
|
693
724
|
ariaLabelledby={fieldLabelId}
|
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
CardSelectOptionGroup,
|
|
9
9
|
} from "../../CardSelect/CardSelect";
|
|
10
10
|
import { DropdownOption, DropdownOptionGroup } from "../../Dropdown/Dropdown";
|
|
11
|
+
import { BulkAddedLabel } from "../../EntityDropdown/EntityDropdown";
|
|
11
12
|
import { RadioButton } from "../../RadioButtons/GroupRadioButtons";
|
|
12
13
|
import FormFieldSchemaType from "./FormFieldSchemaType";
|
|
13
14
|
import FormValues from "./FormValues";
|
|
@@ -78,6 +79,13 @@ export default interface Field<TEntity> {
|
|
|
78
79
|
labelField: string;
|
|
79
80
|
valueField: string;
|
|
80
81
|
};
|
|
82
|
+
/*
|
|
83
|
+
* Entity dropdowns can bulk-add every entry carrying a label. That is a
|
|
84
|
+
* one-time expansion and the label is not part of the field's value, so a
|
|
85
|
+
* form that needs to know which label a selection came from asks for it
|
|
86
|
+
* here.
|
|
87
|
+
*/
|
|
88
|
+
onLabelsBulkAdded?: ((labels: Array<BulkAddedLabel>) => void) | undefined;
|
|
81
89
|
selectByAccessControlProps?: {
|
|
82
90
|
categoryCheckboxProps: CategoryCheckboxProps;
|
|
83
91
|
accessControlColumnTitle: string;
|
|
@@ -68,6 +68,8 @@ export default class FormFieldSchemaTypeUtil {
|
|
|
68
68
|
return FieldType.Element;
|
|
69
69
|
case FormFieldSchemaType.JSON:
|
|
70
70
|
return FieldType.JSON;
|
|
71
|
+
case FormFieldSchemaType.YAML:
|
|
72
|
+
return FieldType.YAML;
|
|
71
73
|
case FormFieldSchemaType.Query:
|
|
72
74
|
return FieldType.Element;
|
|
73
75
|
case FormFieldSchemaType.CustomComponent:
|
|
@@ -18,6 +18,11 @@ import {
|
|
|
18
18
|
JSONSyntaxCheckResult,
|
|
19
19
|
checkJSONSyntax,
|
|
20
20
|
} from "../../../Types/Workflow/TemplateSyntax";
|
|
21
|
+
import {
|
|
22
|
+
YamlSyntaxCheckResult,
|
|
23
|
+
checkYamlSyntax,
|
|
24
|
+
describeYamlSyntaxError,
|
|
25
|
+
} from "../../../Types/Code/YamlSyntax";
|
|
21
26
|
import { Logger } from "../../Utils/Logger";
|
|
22
27
|
import Port from "../../../Types/Port";
|
|
23
28
|
import Typeof from "../../../Types/Typeof";
|
|
@@ -368,6 +373,37 @@ export default class Validation {
|
|
|
368
373
|
);
|
|
369
374
|
}
|
|
370
375
|
|
|
376
|
+
/**
|
|
377
|
+
* Reject a YAML-typed field whose text does not parse.
|
|
378
|
+
*
|
|
379
|
+
* The server validates too (a Sigma rule is compiled on save), but a round
|
|
380
|
+
* trip to hear "bad indentation on line 4" is a poor way to find out. Like
|
|
381
|
+
* validateJSONSyntax this takes the raw value rather than the stringified
|
|
382
|
+
* `content`, and defers to checkYamlSyntax for what it declines to judge.
|
|
383
|
+
*/
|
|
384
|
+
public static validateYAMLSyntax<T extends GenericObject>(
|
|
385
|
+
value: JSONValue | undefined,
|
|
386
|
+
field: Field<T>,
|
|
387
|
+
): string | null {
|
|
388
|
+
if (field.fieldType !== FormFieldSchemaType.YAML) {
|
|
389
|
+
return null;
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
const result: YamlSyntaxCheckResult = checkYamlSyntax(value);
|
|
393
|
+
|
|
394
|
+
if (result.isValid) {
|
|
395
|
+
return null;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
return translateValidationMessage(
|
|
399
|
+
"{{field}} is not valid YAML. {{parserMessage}}",
|
|
400
|
+
{
|
|
401
|
+
field: field.title || field.name || "",
|
|
402
|
+
parserMessage: describeYamlSyntaxError(result),
|
|
403
|
+
},
|
|
404
|
+
);
|
|
405
|
+
}
|
|
406
|
+
|
|
371
407
|
public static validate<T extends GenericObject>(args: {
|
|
372
408
|
formFields: Array<Field<T>>;
|
|
373
409
|
values: FormValues<T>;
|
|
@@ -437,6 +473,16 @@ export default class Validation {
|
|
|
437
473
|
errors[name] = resultJSONSyntax;
|
|
438
474
|
}
|
|
439
475
|
|
|
476
|
+
// Fed the raw value for the same reason validateJSONSyntax is.
|
|
477
|
+
const resultYAMLSyntax: string | null = this.validateYAMLSyntax(
|
|
478
|
+
(entries as JSONObject)[name] as JSONValue | undefined,
|
|
479
|
+
field,
|
|
480
|
+
);
|
|
481
|
+
|
|
482
|
+
if (resultYAMLSyntax) {
|
|
483
|
+
errors[name] = resultYAMLSyntax;
|
|
484
|
+
}
|
|
485
|
+
|
|
440
486
|
const resultMatch: string | null = this.validateMatchField(
|
|
441
487
|
content,
|
|
442
488
|
field,
|
|
@@ -30,6 +30,8 @@ export interface ComponentProps {
|
|
|
30
30
|
ariaHasPopup?: "dialog" | "listbox" | "menu" | "grid" | "tree" | undefined;
|
|
31
31
|
ariaExpanded?: boolean | undefined;
|
|
32
32
|
ariaControls?: string | undefined;
|
|
33
|
+
// For inputs with no visible label element to point ariaLabelledby at.
|
|
34
|
+
ariaLabel?: string | undefined;
|
|
33
35
|
/*
|
|
34
36
|
* For fields that render their own error text - a picker whose message sits
|
|
35
37
|
* below the whole control rather than below this input.
|
|
@@ -177,6 +179,7 @@ const Input: FunctionComponent<ComponentProps> = (
|
|
|
177
179
|
data-testid={props.dataTestId}
|
|
178
180
|
spellCheck={!props.disableSpellCheck}
|
|
179
181
|
autoComplete={props.autoComplete}
|
|
182
|
+
aria-label={props.ariaLabel}
|
|
180
183
|
aria-labelledby={props.ariaLabelledby}
|
|
181
184
|
aria-haspopup={props.ariaHasPopup}
|
|
182
185
|
aria-expanded={props.ariaExpanded}
|