@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
|
@@ -5,7 +5,15 @@ import { beforeEach, describe, expect, jest, test } from "@jest/globals";
|
|
|
5
5
|
|
|
6
6
|
const MONITOR_ONE_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194161010";
|
|
7
7
|
const MONITOR_TWO_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194161011";
|
|
8
|
-
|
|
8
|
+
const LABEL_ONE_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194161020";
|
|
9
|
+
const LABEL_TWO_ID: string = "0198c8ec-2a1d-7f0c-9e75-384194161021";
|
|
10
|
+
|
|
11
|
+
/*
|
|
12
|
+
* The picker, reduced to its two ways of filling the selection. "Bulk-add by
|
|
13
|
+
* label" is the one that matters here: the real control expands a label into
|
|
14
|
+
* the monitors carrying it and then discards the label, so onLabelsBulkAdded
|
|
15
|
+
* is the only thing that tells the modal a selection came from a label at all.
|
|
16
|
+
*/
|
|
9
17
|
jest.mock("Common/UI/Components/EntityDropdown/EntityDropdown", () => {
|
|
10
18
|
return {
|
|
11
19
|
__esModule: true,
|
|
@@ -13,6 +21,7 @@ jest.mock("Common/UI/Components/EntityDropdown/EntityDropdown", () => {
|
|
|
13
21
|
isMultiSelect?: boolean;
|
|
14
22
|
error?: string;
|
|
15
23
|
onChange?: (value: Array<string>) => void;
|
|
24
|
+
onLabelsBulkAdded?: (labels: Array<{ id: string; name: string }>) => void;
|
|
16
25
|
}) => {
|
|
17
26
|
return (
|
|
18
27
|
<div>
|
|
@@ -24,6 +33,28 @@ jest.mock("Common/UI/Components/EntityDropdown/EntityDropdown", () => {
|
|
|
24
33
|
>
|
|
25
34
|
{props.isMultiSelect ? "Select two monitors" : "Select one monitor"}
|
|
26
35
|
</button>
|
|
36
|
+
<button
|
|
37
|
+
type="button"
|
|
38
|
+
onClick={() => {
|
|
39
|
+
props.onChange?.([MONITOR_TWO_ID, MONITOR_ONE_ID]);
|
|
40
|
+
props.onLabelsBulkAdded?.([
|
|
41
|
+
{ id: LABEL_ONE_ID, name: "WB Digital" },
|
|
42
|
+
]);
|
|
43
|
+
}}
|
|
44
|
+
>
|
|
45
|
+
Bulk-add by label
|
|
46
|
+
</button>
|
|
47
|
+
<button
|
|
48
|
+
type="button"
|
|
49
|
+
onClick={() => {
|
|
50
|
+
props.onChange?.([MONITOR_TWO_ID, MONITOR_ONE_ID]);
|
|
51
|
+
props.onLabelsBulkAdded?.([
|
|
52
|
+
{ id: LABEL_TWO_ID, name: "WB Digital Service" },
|
|
53
|
+
]);
|
|
54
|
+
}}
|
|
55
|
+
>
|
|
56
|
+
Bulk-add by a second label
|
|
57
|
+
</button>
|
|
27
58
|
{props.error ? <p>{props.error}</p> : null}
|
|
28
59
|
</div>
|
|
29
60
|
);
|
|
@@ -63,6 +94,7 @@ jest.mock("Common/UI/Utils/ModelAPI/ModelAPI", () => {
|
|
|
63
94
|
__esModule: true,
|
|
64
95
|
default: {
|
|
65
96
|
getList: jest.fn(),
|
|
97
|
+
create: jest.fn(),
|
|
66
98
|
},
|
|
67
99
|
};
|
|
68
100
|
});
|
|
@@ -82,7 +114,10 @@ import bulkAddStatusPageMonitors, {
|
|
|
82
114
|
BulkAddStatusPageMonitorsResult,
|
|
83
115
|
} from "../../../../App/FeatureSet/Dashboard/src/Components/StatusPage/BulkAddStatusPageMonitors";
|
|
84
116
|
import BulkAddStatusPageMonitorsModal from "../../../../App/FeatureSet/Dashboard/src/Components/StatusPage/BulkAddStatusPageMonitorsModal";
|
|
117
|
+
import Label from "../../../Models/DatabaseModels/Label";
|
|
85
118
|
import Monitor from "../../../Models/DatabaseModels/Monitor";
|
|
119
|
+
import StatusPageMonitorRule from "../../../Models/DatabaseModels/StatusPageMonitorRule";
|
|
120
|
+
import StatusPageResource from "../../../Models/DatabaseModels/StatusPageResource";
|
|
86
121
|
import ObjectID from "../../../Types/ObjectID";
|
|
87
122
|
import ModelAPI from "../../../UI/Utils/ModelAPI/ModelAPI";
|
|
88
123
|
|
|
@@ -102,6 +137,9 @@ const mockBulkAdd: jest.MockedFunction<typeof bulkAddStatusPageMonitors> =
|
|
|
102
137
|
const mockGetList: jest.MockedFunction<any> =
|
|
103
138
|
ModelAPI.getList as unknown as jest.MockedFunction<any>;
|
|
104
139
|
|
|
140
|
+
const mockCreate: jest.MockedFunction<any> =
|
|
141
|
+
ModelAPI.create as unknown as jest.MockedFunction<any>;
|
|
142
|
+
|
|
105
143
|
type MakeMonitorFunction = (
|
|
106
144
|
id: string,
|
|
107
145
|
name: string,
|
|
@@ -120,6 +158,43 @@ const makeMonitor: MakeMonitorFunction = (
|
|
|
120
158
|
return monitor;
|
|
121
159
|
};
|
|
122
160
|
|
|
161
|
+
type MakeResourceFunction = (monitorId: string) => StatusPageResource;
|
|
162
|
+
|
|
163
|
+
const makeResource: MakeResourceFunction = (
|
|
164
|
+
monitorId: string,
|
|
165
|
+
): StatusPageResource => {
|
|
166
|
+
const resource: StatusPageResource = new StatusPageResource();
|
|
167
|
+
resource._id = `resource-for-${monitorId}`;
|
|
168
|
+
resource.monitorId = new ObjectID(monitorId);
|
|
169
|
+
return resource;
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
/*
|
|
173
|
+
* The modal reads the monitors it was handed and, separately, the resources
|
|
174
|
+
* the status page already has - so the list mock answers by model.
|
|
175
|
+
*/
|
|
176
|
+
type MockListsFunction = (
|
|
177
|
+
existingResources?: Array<StatusPageResource>,
|
|
178
|
+
) => void;
|
|
179
|
+
|
|
180
|
+
const mockLists: MockListsFunction = (
|
|
181
|
+
existingResources: Array<StatusPageResource> = [],
|
|
182
|
+
): void => {
|
|
183
|
+
mockGetList.mockImplementation(async (data: any): Promise<any> => {
|
|
184
|
+
if (data.modelType === StatusPageResource) {
|
|
185
|
+
return { data: existingResources, count: existingResources.length };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
data: [
|
|
190
|
+
makeMonitor(MONITOR_ONE_ID, "Checkout API", "Takes payments"),
|
|
191
|
+
makeMonitor(MONITOR_TWO_ID, "Billing Worker", "Sends invoices"),
|
|
192
|
+
],
|
|
193
|
+
count: 2,
|
|
194
|
+
};
|
|
195
|
+
});
|
|
196
|
+
};
|
|
197
|
+
|
|
123
198
|
type RenderModalFunction = (
|
|
124
199
|
overrides?: Partial<
|
|
125
200
|
React.ComponentProps<typeof BulkAddStatusPageMonitorsModal>
|
|
@@ -158,27 +233,38 @@ const goToLastStep: GoToLastStepFunction = async (
|
|
|
158
233
|
});
|
|
159
234
|
};
|
|
160
235
|
|
|
236
|
+
const callOrder: Array<string> = [];
|
|
237
|
+
|
|
238
|
+
/** The StatusPageMonitorRule the modal asked ModelAPI to create, if any. */
|
|
239
|
+
type CreatedRuleFunction = () => StatusPageMonitorRule | null;
|
|
240
|
+
|
|
241
|
+
const createdRule: CreatedRuleFunction = (): StatusPageMonitorRule | null => {
|
|
242
|
+
const call: any = mockCreate.mock.calls[0];
|
|
243
|
+
|
|
244
|
+
return call ? (call[0].model as StatusPageMonitorRule) : null;
|
|
245
|
+
};
|
|
246
|
+
|
|
161
247
|
describe("BulkAddStatusPageMonitorsModal", () => {
|
|
162
248
|
beforeEach(() => {
|
|
163
249
|
jest.clearAllMocks();
|
|
164
250
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
makeMonitor(MONITOR_ONE_ID, "Checkout API", "Takes payments"),
|
|
169
|
-
makeMonitor(MONITOR_TWO_ID, "Billing Worker", "Sends invoices"),
|
|
170
|
-
],
|
|
171
|
-
count: 2,
|
|
172
|
-
};
|
|
173
|
-
});
|
|
251
|
+
mockLists();
|
|
252
|
+
|
|
253
|
+
callOrder.length = 0;
|
|
174
254
|
|
|
175
255
|
mockBulkAdd.mockImplementation(
|
|
176
256
|
async (
|
|
177
257
|
options: BulkAddStatusPageMonitorsOptions,
|
|
178
258
|
): Promise<BulkAddStatusPageMonitorsResult> => {
|
|
179
|
-
|
|
259
|
+
callOrder.push("bulkAdd");
|
|
260
|
+
return { succeeded: options.monitors, failed: [], skipped: [] };
|
|
180
261
|
},
|
|
181
262
|
);
|
|
263
|
+
|
|
264
|
+
mockCreate.mockImplementation(async (request: any): Promise<any> => {
|
|
265
|
+
callOrder.push("create");
|
|
266
|
+
return request?.model;
|
|
267
|
+
});
|
|
182
268
|
});
|
|
183
269
|
|
|
184
270
|
test("asks for monitors and the resource options, but never a display name", () => {
|
|
@@ -279,6 +365,222 @@ describe("BulkAddStatusPageMonitorsModal", () => {
|
|
|
279
365
|
expect(options.columnAxisValue).toBe("US East");
|
|
280
366
|
});
|
|
281
367
|
|
|
368
|
+
/*
|
|
369
|
+
* Issue #3420: picking monitors by label re-selects every monitor carrying
|
|
370
|
+
* that label, the ones already on the page included. The modal has to tell
|
|
371
|
+
* the add which monitors are already there, or each of them gets a second
|
|
372
|
+
* resource and the public page lists it twice.
|
|
373
|
+
*/
|
|
374
|
+
test("hands the status page's existing monitors to the bulk add", async () => {
|
|
375
|
+
mockLists([makeResource(MONITOR_ONE_ID)]);
|
|
376
|
+
|
|
377
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
378
|
+
|
|
379
|
+
fireEvent.click(view.getByRole("button", { name: "Select two monitors" }));
|
|
380
|
+
await goToLastStep(view);
|
|
381
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
382
|
+
|
|
383
|
+
await waitFor(() => {
|
|
384
|
+
expect(mockBulkAdd).toHaveBeenCalledTimes(1);
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
const options: BulkAddStatusPageMonitorsOptions =
|
|
388
|
+
mockBulkAdd.mock.calls[0]![0];
|
|
389
|
+
expect(options.existingMonitorIds).toEqual([MONITOR_ONE_ID]);
|
|
390
|
+
});
|
|
391
|
+
|
|
392
|
+
test("reads the whole status page, not only the group being added to", async () => {
|
|
393
|
+
mockLists([makeResource(MONITOR_ONE_ID)]);
|
|
394
|
+
|
|
395
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
396
|
+
|
|
397
|
+
fireEvent.click(view.getByRole("button", { name: "Select two monitors" }));
|
|
398
|
+
await goToLastStep(view);
|
|
399
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
400
|
+
|
|
401
|
+
await waitFor(() => {
|
|
402
|
+
expect(mockBulkAdd).toHaveBeenCalledTimes(1);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
const resourceListCall: any = mockGetList.mock.calls.find((call: any) => {
|
|
406
|
+
return call[0].modelType === StatusPageResource;
|
|
407
|
+
});
|
|
408
|
+
|
|
409
|
+
expect(resourceListCall).toBeDefined();
|
|
410
|
+
expect(resourceListCall[0].query).toEqual({
|
|
411
|
+
statusPageId: STATUS_PAGE_ID,
|
|
412
|
+
projectId: PROJECT_ID,
|
|
413
|
+
});
|
|
414
|
+
expect(resourceListCall[0].query.statusPageGroupId).toBeUndefined();
|
|
415
|
+
expect(resourceListCall[0].select).toMatchObject({ monitorId: true });
|
|
416
|
+
});
|
|
417
|
+
|
|
418
|
+
test("passes no existing monitors when the status page has none", async () => {
|
|
419
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
420
|
+
|
|
421
|
+
fireEvent.click(view.getByRole("button", { name: "Select two monitors" }));
|
|
422
|
+
await goToLastStep(view);
|
|
423
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
424
|
+
|
|
425
|
+
await waitFor(() => {
|
|
426
|
+
expect(mockBulkAdd).toHaveBeenCalledTimes(1);
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
expect(mockBulkAdd.mock.calls[0]![0].existingMonitorIds).toEqual([]);
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
test("ignores resources that point at a monitor group rather than a monitor", async () => {
|
|
433
|
+
const monitorGroupResource: StatusPageResource = new StatusPageResource();
|
|
434
|
+
monitorGroupResource._id = "resource-for-a-monitor-group";
|
|
435
|
+
|
|
436
|
+
mockLists([monitorGroupResource, makeResource(MONITOR_TWO_ID)]);
|
|
437
|
+
|
|
438
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
439
|
+
|
|
440
|
+
fireEvent.click(view.getByRole("button", { name: "Select two monitors" }));
|
|
441
|
+
await goToLastStep(view);
|
|
442
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
443
|
+
|
|
444
|
+
await waitFor(() => {
|
|
445
|
+
expect(mockBulkAdd).toHaveBeenCalledTimes(1);
|
|
446
|
+
});
|
|
447
|
+
|
|
448
|
+
expect(mockBulkAdd.mock.calls[0]![0].existingMonitorIds).toEqual([
|
|
449
|
+
MONITOR_TWO_ID,
|
|
450
|
+
]);
|
|
451
|
+
});
|
|
452
|
+
|
|
453
|
+
test("lists the monitors that were already on the page instead of failing them", async () => {
|
|
454
|
+
mockBulkAdd.mockImplementationOnce(
|
|
455
|
+
async (
|
|
456
|
+
options: BulkAddStatusPageMonitorsOptions,
|
|
457
|
+
): Promise<BulkAddStatusPageMonitorsResult> => {
|
|
458
|
+
return {
|
|
459
|
+
succeeded: [options.monitors[0]!],
|
|
460
|
+
failed: [],
|
|
461
|
+
skipped: [options.monitors[1]!],
|
|
462
|
+
};
|
|
463
|
+
},
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
467
|
+
|
|
468
|
+
fireEvent.click(view.getByRole("button", { name: "Select two monitors" }));
|
|
469
|
+
await goToLastStep(view);
|
|
470
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
471
|
+
|
|
472
|
+
await waitFor(() => {
|
|
473
|
+
expect(
|
|
474
|
+
view.getByText(
|
|
475
|
+
"Added 1 of 2 selected monitors. 1 monitor was already on this status page and was skipped.",
|
|
476
|
+
),
|
|
477
|
+
).toBeVisible();
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
// Skipped monitors are not an error, so the summary is the "Monitors Added" one.
|
|
481
|
+
expect(view.getByText("Monitors Added")).toBeVisible();
|
|
482
|
+
expect(view.getByText("Added (1)")).toBeVisible();
|
|
483
|
+
expect(view.getByText("Already Added (1)")).toBeVisible();
|
|
484
|
+
expect(view.queryByText(/^Failed/)).not.toBeInTheDocument();
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
test("says nothing was added when every selected monitor is already on the page", async () => {
|
|
488
|
+
mockBulkAdd.mockImplementationOnce(
|
|
489
|
+
async (
|
|
490
|
+
options: BulkAddStatusPageMonitorsOptions,
|
|
491
|
+
): Promise<BulkAddStatusPageMonitorsResult> => {
|
|
492
|
+
return {
|
|
493
|
+
succeeded: [],
|
|
494
|
+
failed: [],
|
|
495
|
+
skipped: options.monitors,
|
|
496
|
+
};
|
|
497
|
+
},
|
|
498
|
+
);
|
|
499
|
+
|
|
500
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
501
|
+
|
|
502
|
+
fireEvent.click(view.getByRole("button", { name: "Select two monitors" }));
|
|
503
|
+
await goToLastStep(view);
|
|
504
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
505
|
+
|
|
506
|
+
await waitFor(() => {
|
|
507
|
+
expect(
|
|
508
|
+
view.getByText(
|
|
509
|
+
"Added 0 of 2 selected monitors. 2 monitors were already on this status page and were skipped.",
|
|
510
|
+
),
|
|
511
|
+
).toBeVisible();
|
|
512
|
+
});
|
|
513
|
+
|
|
514
|
+
expect(view.queryByText(/^Added \(/)).not.toBeInTheDocument();
|
|
515
|
+
expect(view.getByText("Already Added (2)")).toBeVisible();
|
|
516
|
+
expect(view.getByText("Billing Worker")).toBeVisible();
|
|
517
|
+
expect(view.getByText("Checkout API")).toBeVisible();
|
|
518
|
+
});
|
|
519
|
+
|
|
520
|
+
test("counts skipped, added and failed monitors in one summary", async () => {
|
|
521
|
+
mockBulkAdd.mockImplementationOnce(
|
|
522
|
+
async (
|
|
523
|
+
options: BulkAddStatusPageMonitorsOptions,
|
|
524
|
+
): Promise<BulkAddStatusPageMonitorsResult> => {
|
|
525
|
+
return {
|
|
526
|
+
succeeded: [options.monitors[0]!],
|
|
527
|
+
failed: [
|
|
528
|
+
{
|
|
529
|
+
monitor: options.monitors[1]!,
|
|
530
|
+
error: new Error("Permission denied"),
|
|
531
|
+
},
|
|
532
|
+
],
|
|
533
|
+
skipped: [makeMonitor(MONITOR_ONE_ID, "Legacy API", "Already there")],
|
|
534
|
+
};
|
|
535
|
+
},
|
|
536
|
+
);
|
|
537
|
+
|
|
538
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
539
|
+
|
|
540
|
+
fireEvent.click(view.getByRole("button", { name: "Select two monitors" }));
|
|
541
|
+
await goToLastStep(view);
|
|
542
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
543
|
+
|
|
544
|
+
await waitFor(() => {
|
|
545
|
+
expect(
|
|
546
|
+
view.getByText(
|
|
547
|
+
"Added 1 of 3 selected monitors. 1 monitor was already on this status page and was skipped.",
|
|
548
|
+
),
|
|
549
|
+
).toBeVisible();
|
|
550
|
+
});
|
|
551
|
+
|
|
552
|
+
expect(view.getByText("Monitor Add Results")).toBeVisible();
|
|
553
|
+
expect(view.getByText("Added (1)")).toBeVisible();
|
|
554
|
+
expect(view.getByText("Already Added (1)")).toBeVisible();
|
|
555
|
+
expect(view.getByText("Failed (1)")).toBeVisible();
|
|
556
|
+
});
|
|
557
|
+
|
|
558
|
+
test("surfaces a failure to read the status page's existing resources", async () => {
|
|
559
|
+
mockGetList.mockImplementation(async (data: any): Promise<any> => {
|
|
560
|
+
if (data.modelType === StatusPageResource) {
|
|
561
|
+
throw new Error("Cannot read status page resources");
|
|
562
|
+
}
|
|
563
|
+
|
|
564
|
+
return {
|
|
565
|
+
data: [makeMonitor(MONITOR_ONE_ID, "Checkout API", "Takes payments")],
|
|
566
|
+
count: 1,
|
|
567
|
+
};
|
|
568
|
+
});
|
|
569
|
+
|
|
570
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
571
|
+
|
|
572
|
+
fireEvent.click(view.getByRole("button", { name: "Select two monitors" }));
|
|
573
|
+
await goToLastStep(view);
|
|
574
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
575
|
+
|
|
576
|
+
await waitFor(() => {
|
|
577
|
+
expect(view.getByText("Cannot read status page resources")).toBeVisible();
|
|
578
|
+
});
|
|
579
|
+
|
|
580
|
+
// Nothing is written when we cannot tell what is already there.
|
|
581
|
+
expect(mockBulkAdd).not.toHaveBeenCalled();
|
|
582
|
+
});
|
|
583
|
+
|
|
282
584
|
test("shows a non-retryable completion summary for partial failures", async () => {
|
|
283
585
|
mockBulkAdd.mockImplementationOnce(
|
|
284
586
|
async (
|
|
@@ -292,6 +594,7 @@ describe("BulkAddStatusPageMonitorsModal", () => {
|
|
|
292
594
|
error: new Error("Permission denied"),
|
|
293
595
|
},
|
|
294
596
|
],
|
|
597
|
+
skipped: [],
|
|
295
598
|
};
|
|
296
599
|
},
|
|
297
600
|
);
|
|
@@ -318,3 +621,279 @@ describe("BulkAddStatusPageMonitorsModal", () => {
|
|
|
318
621
|
expect(mockBulkAdd).toHaveBeenCalledTimes(1);
|
|
319
622
|
});
|
|
320
623
|
});
|
|
624
|
+
|
|
625
|
+
/*
|
|
626
|
+
* #3418. Filling a group from a label used to be a one-time expansion: the
|
|
627
|
+
* label became a list of monitors, each became a resource, and the label was
|
|
628
|
+
* thrown away - so a monitor given that label the next day landed on no page,
|
|
629
|
+
* and with ten status pages that meant re-populating groups by hand daily.
|
|
630
|
+
*
|
|
631
|
+
* A StatusPageMonitorRule is the durable form of the same intent; the server
|
|
632
|
+
* re-runs it on every monitor create and relabel. These tests drive the real
|
|
633
|
+
* modal and assert on what it actually posts.
|
|
634
|
+
*/
|
|
635
|
+
describe("BulkAddStatusPageMonitorsModal - keeping a label-filled group filled", () => {
|
|
636
|
+
beforeEach(() => {
|
|
637
|
+
jest.clearAllMocks();
|
|
638
|
+
callOrder.length = 0;
|
|
639
|
+
|
|
640
|
+
mockLists();
|
|
641
|
+
|
|
642
|
+
mockBulkAdd.mockImplementation(
|
|
643
|
+
async (
|
|
644
|
+
options: BulkAddStatusPageMonitorsOptions,
|
|
645
|
+
): Promise<BulkAddStatusPageMonitorsResult> => {
|
|
646
|
+
callOrder.push("bulkAdd");
|
|
647
|
+
return { succeeded: options.monitors, failed: [], skipped: [] };
|
|
648
|
+
},
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
mockCreate.mockImplementation(async (request: any): Promise<any> => {
|
|
652
|
+
callOrder.push("create");
|
|
653
|
+
return request?.model;
|
|
654
|
+
});
|
|
655
|
+
});
|
|
656
|
+
|
|
657
|
+
/*
|
|
658
|
+
* The question only means something once a label has been used, so it is not
|
|
659
|
+
* asked of somebody picking monitors one at a time.
|
|
660
|
+
*/
|
|
661
|
+
test("does not ask about syncing until a label has been used", () => {
|
|
662
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
663
|
+
|
|
664
|
+
expect(
|
|
665
|
+
view.queryByText("Keep This Group In Sync With These Labels"),
|
|
666
|
+
).not.toBeInTheDocument();
|
|
667
|
+
|
|
668
|
+
fireEvent.click(view.getByRole("button", { name: "Bulk-add by label" }));
|
|
669
|
+
|
|
670
|
+
expect(
|
|
671
|
+
view.getByText("Keep This Group In Sync With These Labels"),
|
|
672
|
+
).toBeVisible();
|
|
673
|
+
});
|
|
674
|
+
|
|
675
|
+
test("writes no rule for monitors picked one at a time", async () => {
|
|
676
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
677
|
+
|
|
678
|
+
fireEvent.click(view.getByRole("button", { name: "Select two monitors" }));
|
|
679
|
+
await goToLastStep(view);
|
|
680
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
681
|
+
|
|
682
|
+
await waitFor(() => {
|
|
683
|
+
expect(mockBulkAdd).toHaveBeenCalledTimes(1);
|
|
684
|
+
});
|
|
685
|
+
|
|
686
|
+
expect(mockCreate).not.toHaveBeenCalled();
|
|
687
|
+
});
|
|
688
|
+
|
|
689
|
+
/*
|
|
690
|
+
* The headline. Before the fix nothing at all was written here, and the
|
|
691
|
+
* label was gone the moment the dialog closed.
|
|
692
|
+
*/
|
|
693
|
+
test("writes a rule for the label the group was filled from", async () => {
|
|
694
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
695
|
+
|
|
696
|
+
fireEvent.click(view.getByRole("button", { name: "Bulk-add by label" }));
|
|
697
|
+
await goToLastStep(view);
|
|
698
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
699
|
+
|
|
700
|
+
await waitFor(() => {
|
|
701
|
+
expect(mockCreate).toHaveBeenCalledTimes(1);
|
|
702
|
+
});
|
|
703
|
+
|
|
704
|
+
const rule: StatusPageMonitorRule | null = createdRule();
|
|
705
|
+
|
|
706
|
+
expect(rule?.statusPageId?.toString()).toBe(STATUS_PAGE_ID.toString());
|
|
707
|
+
expect(rule?.statusPageGroupId?.toString()).toBe(GROUP_ID.toString());
|
|
708
|
+
expect(rule?.projectId?.toString()).toBe(PROJECT_ID.toString());
|
|
709
|
+
expect(rule?.isEnabled).toBe(true);
|
|
710
|
+
expect(
|
|
711
|
+
(rule?.monitorLabels || []).map((label: Label) => {
|
|
712
|
+
return label.id?.toString();
|
|
713
|
+
}),
|
|
714
|
+
).toEqual([LABEL_ONE_ID]);
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
/*
|
|
718
|
+
* name is a required, non-nullable column. A rule built without one is
|
|
719
|
+
* rejected by the server, and the only symptom is the group quietly not
|
|
720
|
+
* being kept in sync - the original bug, wearing a hat.
|
|
721
|
+
*/
|
|
722
|
+
test("gives the rule every column the model requires", async () => {
|
|
723
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
724
|
+
|
|
725
|
+
fireEvent.click(view.getByRole("button", { name: "Bulk-add by label" }));
|
|
726
|
+
await goToLastStep(view);
|
|
727
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
728
|
+
|
|
729
|
+
await waitFor(() => {
|
|
730
|
+
expect(mockCreate).toHaveBeenCalledTimes(1);
|
|
731
|
+
});
|
|
732
|
+
|
|
733
|
+
const rule: StatusPageMonitorRule = createdRule()!;
|
|
734
|
+
|
|
735
|
+
for (const column of rule.getRequiredColumns().columns) {
|
|
736
|
+
expect({
|
|
737
|
+
column: column,
|
|
738
|
+
value: (rule as unknown as Record<string, unknown>)[column],
|
|
739
|
+
}).toEqual({ column: column, value: expect.anything() });
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
expect(rule.name).toBe("Label: WB Digital");
|
|
743
|
+
});
|
|
744
|
+
|
|
745
|
+
/*
|
|
746
|
+
* The server's backfill skips monitors already on the page, so writing the
|
|
747
|
+
* rule second is the only thing stopping every monitor this add just created
|
|
748
|
+
* getting a second resource - and a public page listing each one twice.
|
|
749
|
+
*/
|
|
750
|
+
test("writes the rule only after the resources exist", async () => {
|
|
751
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
752
|
+
|
|
753
|
+
fireEvent.click(view.getByRole("button", { name: "Bulk-add by label" }));
|
|
754
|
+
await goToLastStep(view);
|
|
755
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
756
|
+
|
|
757
|
+
await waitFor(() => {
|
|
758
|
+
expect(mockCreate).toHaveBeenCalledTimes(1);
|
|
759
|
+
});
|
|
760
|
+
|
|
761
|
+
expect(callOrder).toEqual(["bulkAdd", "create"]);
|
|
762
|
+
});
|
|
763
|
+
|
|
764
|
+
test("carries every label when the picker was used twice", async () => {
|
|
765
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
766
|
+
|
|
767
|
+
fireEvent.click(view.getByRole("button", { name: "Bulk-add by label" }));
|
|
768
|
+
fireEvent.click(
|
|
769
|
+
view.getByRole("button", { name: "Bulk-add by a second label" }),
|
|
770
|
+
);
|
|
771
|
+
await goToLastStep(view);
|
|
772
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
773
|
+
|
|
774
|
+
await waitFor(() => {
|
|
775
|
+
expect(mockCreate).toHaveBeenCalledTimes(1);
|
|
776
|
+
});
|
|
777
|
+
|
|
778
|
+
expect(
|
|
779
|
+
(createdRule()?.monitorLabels || []).map((label: Label) => {
|
|
780
|
+
return label.id?.toString();
|
|
781
|
+
}),
|
|
782
|
+
).toEqual([LABEL_ONE_ID, LABEL_TWO_ID]);
|
|
783
|
+
});
|
|
784
|
+
|
|
785
|
+
test("names the labels it is keeping in sync with", async () => {
|
|
786
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
787
|
+
|
|
788
|
+
fireEvent.click(view.getByRole("button", { name: "Bulk-add by label" }));
|
|
789
|
+
fireEvent.click(
|
|
790
|
+
view.getByRole("button", { name: "Bulk-add by a second label" }),
|
|
791
|
+
);
|
|
792
|
+
await goToLastStep(view);
|
|
793
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
794
|
+
|
|
795
|
+
await waitFor(() => {
|
|
796
|
+
expect(mockCreate).toHaveBeenCalledTimes(1);
|
|
797
|
+
});
|
|
798
|
+
|
|
799
|
+
expect(createdRule()?.name).toBe("Labels: WB Digital, WB Digital Service");
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
/*
|
|
803
|
+
* Somebody who pruned the expanded list by hand is saying they want these
|
|
804
|
+
* monitors and not the label. Turning it off has to mean exactly the old
|
|
805
|
+
* behaviour.
|
|
806
|
+
*/
|
|
807
|
+
test("writes no rule when the sync is turned off", async () => {
|
|
808
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
809
|
+
|
|
810
|
+
fireEvent.click(view.getByRole("button", { name: "Bulk-add by label" }));
|
|
811
|
+
|
|
812
|
+
const toggle: HTMLElement = view.getByRole("switch", {
|
|
813
|
+
name: /Keep This Group In Sync With These Labels/,
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
expect(toggle.getAttribute("aria-checked")).toBe("true");
|
|
817
|
+
fireEvent.click(toggle);
|
|
818
|
+
expect(toggle.getAttribute("aria-checked")).toBe("false");
|
|
819
|
+
|
|
820
|
+
await goToLastStep(view);
|
|
821
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
822
|
+
|
|
823
|
+
await waitFor(() => {
|
|
824
|
+
expect(mockBulkAdd).toHaveBeenCalledTimes(1);
|
|
825
|
+
});
|
|
826
|
+
|
|
827
|
+
expect(mockCreate).not.toHaveBeenCalled();
|
|
828
|
+
});
|
|
829
|
+
|
|
830
|
+
/*
|
|
831
|
+
* A grid group places every resource in a row/column cell, and the public
|
|
832
|
+
* page skips one that has neither. A rule has nowhere to record the cell, so
|
|
833
|
+
* every monitor it added later would be on the page and rendered nowhere -
|
|
834
|
+
* better not to offer the promise at all.
|
|
835
|
+
*/
|
|
836
|
+
test("never offers the sync for a grid group", async () => {
|
|
837
|
+
const view: ReturnType<typeof render> = renderModal({
|
|
838
|
+
gridPlacement: {
|
|
839
|
+
rowLabel: "Environment",
|
|
840
|
+
rowValues: ["Production", "Staging"],
|
|
841
|
+
columnLabel: "Region",
|
|
842
|
+
columnValues: ["US East", "EU West"],
|
|
843
|
+
},
|
|
844
|
+
});
|
|
845
|
+
|
|
846
|
+
fireEvent.click(view.getByRole("button", { name: "Bulk-add by label" }));
|
|
847
|
+
|
|
848
|
+
expect(
|
|
849
|
+
view.queryByText("Keep This Group In Sync With These Labels"),
|
|
850
|
+
).not.toBeInTheDocument();
|
|
851
|
+
|
|
852
|
+
fireEvent.click(
|
|
853
|
+
view.getByRole("button", { name: "Choose Select environment" }),
|
|
854
|
+
);
|
|
855
|
+
fireEvent.click(view.getByRole("button", { name: "Choose Select region" }));
|
|
856
|
+
await goToLastStep(view);
|
|
857
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
858
|
+
|
|
859
|
+
await waitFor(() => {
|
|
860
|
+
expect(mockBulkAdd).toHaveBeenCalledTimes(1);
|
|
861
|
+
});
|
|
862
|
+
|
|
863
|
+
expect(mockCreate).not.toHaveBeenCalled();
|
|
864
|
+
});
|
|
865
|
+
|
|
866
|
+
test("tells the user the group is now kept in sync", async () => {
|
|
867
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
868
|
+
|
|
869
|
+
fireEvent.click(view.getByRole("button", { name: "Bulk-add by label" }));
|
|
870
|
+
await goToLastStep(view);
|
|
871
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
872
|
+
|
|
873
|
+
expect(
|
|
874
|
+
await view.findByText(/stays in sync with the labels you used/),
|
|
875
|
+
).toBeVisible();
|
|
876
|
+
});
|
|
877
|
+
|
|
878
|
+
/*
|
|
879
|
+
* Creating a rule needs a permission adding a resource does not, so this
|
|
880
|
+
* happens to real people. It must not read as a failed add - the resources
|
|
881
|
+
* are there.
|
|
882
|
+
*/
|
|
883
|
+
test("keeps the monitors it added when the rule is refused", async () => {
|
|
884
|
+
mockCreate.mockRejectedValue(new Error("Permission denied"));
|
|
885
|
+
|
|
886
|
+
const view: ReturnType<typeof render> = renderModal();
|
|
887
|
+
|
|
888
|
+
fireEvent.click(view.getByRole("button", { name: "Bulk-add by label" }));
|
|
889
|
+
await goToLastStep(view);
|
|
890
|
+
fireEvent.click(view.getByTestId("modal-footer-submit-button"));
|
|
891
|
+
|
|
892
|
+
expect(
|
|
893
|
+
await view.findByText("Added 2 of 2 selected monitors."),
|
|
894
|
+
).toBeVisible();
|
|
895
|
+
expect(
|
|
896
|
+
view.getByText(/could not be kept in sync with the labels you used/),
|
|
897
|
+
).toBeVisible();
|
|
898
|
+
});
|
|
899
|
+
});
|