@oneuptime/common 12.0.23 → 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/NetworkDeviceAutoImportRule.ts +27 -7
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +67 -0
- package/Models/DatabaseModels/Project.ts +68 -0
- package/Models/DatabaseModels/User.ts +68 -0
- package/Server/API/TelemetryAPI.ts +29 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.ts +28 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.ts +49 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.ts +33 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/GzipRequestBody.ts +311 -0
- package/Server/Middleware/MultipartFormData.ts +87 -3
- package/Server/Services/DetectionRuleService.ts +73 -6
- package/Server/Services/Index.ts +205 -0
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +59 -0
- package/Server/Services/NetworkDeviceService.ts +148 -0
- package/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.ts +65 -0
- package/Server/Services/OnCallDutyPolicyFeedService.ts +1 -1
- package/Server/Services/ProjectService.ts +29 -19
- package/Server/Services/SecurityEventService.ts +90 -13
- package/Server/Services/StatusPageResourceService.ts +223 -0
- package/Server/Services/TelemetryAttributeService.ts +17 -0
- package/Server/Services/UserService.ts +4 -6
- package/Server/Types/Markdown.ts +161 -67
- package/Server/Utils/AI/Toolbox/OnCallTools.ts +12 -4
- package/Server/Utils/AnalyticsDatabase/Statement.ts +13 -1
- package/Server/Utils/AnalyticsDatabase/StatementGenerator.ts +297 -6
- package/Server/Utils/Attribution.ts +4 -0
- package/Server/Utils/Marketing/MarketingEventUtil.ts +45 -7
- package/Server/Utils/SecurityEvent/DetectionRuleEvaluator.ts +55 -3
- package/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.ts +21 -0
- package/Server/Utils/StartServer.ts +12 -32
- package/Server/Utils/Telemetry/LlmCostBudgetEvaluator.ts +101 -4
- package/Server/Utils/Telemetry/LlmMetricSpend.ts +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/DetectionRuleService.test.ts +134 -2
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +7 -1
- package/Tests/Server/Services/FeedRetentionConsistency.test.ts +131 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +194 -0
- package/Tests/Server/Services/NetworkDeviceMonitorStatusStamp.test.ts +800 -0
- package/Tests/Server/Services/OnCallScheduleAttachRosterRefresh.test.ts +244 -0
- package/Tests/Server/Services/SecurityEventService.test.ts +376 -0
- package/Tests/Server/Services/StatusPageResourceDuplicates.test.ts +703 -0
- package/Tests/Server/Services/TelemetryAttributeServiceSecurityEvent.test.ts +256 -0
- package/Tests/Server/Types/Workflow/Components/ComponentRegistryParity.test.ts +417 -0
- package/Tests/Server/Utils/AnalyticsDatabase/StatementGeneratorWhereOperators.test.ts +677 -0
- package/Tests/Server/Utils/Marketing/MarketingEventUtil.test.ts +43 -0
- package/Tests/Server/Utils/Monitor/Criteria/ProfileMonitorCriteria.test.ts +15 -11
- package/Tests/Server/Utils/SecurityEvent/DetectionRuleEvaluator.test.ts +369 -8
- package/Tests/Server/Utils/SecurityEvent/SigmaClickhouseCompiler.test.ts +35 -0
- package/Tests/Server/Utils/Telemetry/LlmCostBudgetEvaluator.test.ts +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/DiscoveredHostUtil.test.ts +221 -0
- package/Tests/Utils/NetworkDiscovery/ScanNameUtil.test.ts +312 -0
- package/Tests/Utils/StatusPage/ResourceExplorer.test.ts +468 -0
- package/Tests/Utils/Telemetry/LlmMetricQuery.test.ts +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/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/ScanNameUtil.ts +181 -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/NetworkDeviceAutoImportRule.js +27 -7
- package/build/dist/Models/DatabaseModels/NetworkDeviceAutoImportRule.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +69 -0
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +76 -0
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/User.js +76 -0
- package/build/dist/Models/DatabaseModels/User.js.map +1 -1
- package/build/dist/Server/API/TelemetryAPI.js +17 -0
- package/build/dist/Server/API/TelemetryAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js +21 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789400000000-AddNameToNetworkDeviceDiscoveryScan.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js +42 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789500000000-AddCampaignUtmFields.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js +22 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1789512000000-AddDetectionRuleDistinctCountColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/GzipRequestBody.js +224 -0
- package/build/dist/Server/Middleware/GzipRequestBody.js.map +1 -0
- package/build/dist/Server/Middleware/MultipartFormData.js +71 -2
- package/build/dist/Server/Middleware/MultipartFormData.js.map +1 -1
- package/build/dist/Server/Services/DetectionRuleService.js +49 -4
- package/build/dist/Server/Services/DetectionRuleService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +204 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +50 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceService.js +124 -0
- package/build/dist/Server/Services/NetworkDeviceService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js +68 -13
- package/build/dist/Server/Services/OnCallDutyPolicyEscalationRuleScheduleService.js.map +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js +1 -1
- package/build/dist/Server/Services/OnCallDutyPolicyFeedService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +25 -45
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/SecurityEventService.js +57 -5
- package/build/dist/Server/Services/SecurityEventService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageResourceService.js +140 -0
- package/build/dist/Server/Services/StatusPageResourceService.js.map +1 -1
- package/build/dist/Server/Services/TelemetryAttributeService.js +17 -0
- package/build/dist/Server/Services/TelemetryAttributeService.js.map +1 -1
- package/build/dist/Server/Services/UserService.js +2 -12
- package/build/dist/Server/Services/UserService.js.map +1 -1
- package/build/dist/Server/Types/Markdown.js +147 -61
- package/build/dist/Server/Types/Markdown.js.map +1 -1
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js +12 -4
- package/build/dist/Server/Utils/AI/Toolbox/OnCallTools.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js +12 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/Statement.js.map +1 -1
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js +246 -9
- package/build/dist/Server/Utils/AnalyticsDatabase/StatementGenerator.js.map +1 -1
- package/build/dist/Server/Utils/Attribution.js.map +1 -1
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js +27 -7
- package/build/dist/Server/Utils/Marketing/MarketingEventUtil.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js +42 -7
- package/build/dist/Server/Utils/SecurityEvent/DetectionRuleEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js +18 -0
- package/build/dist/Server/Utils/SecurityEvent/Sigma/SigmaClickhouseCompiler.js.map +1 -1
- package/build/dist/Server/Utils/StartServer.js +12 -21
- package/build/dist/Server/Utils/StartServer.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js +76 -5
- package/build/dist/Server/Utils/Telemetry/LlmCostBudgetEvaluator.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/LlmMetricSpend.js +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/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/ScanNameUtil.js +120 -0
- package/build/dist/Utils/NetworkDiscovery/ScanNameUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js +171 -0
- package/build/dist/Utils/StatusPage/ResourceExplorer.js.map +1 -1
- package/build/dist/Utils/Telemetry/LlmMetricQuery.js +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,598 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import FilterCondition from "../../../Types/Filter/FilterCondition";
|
|
3
|
+
import { CriteriaAlert } from "../../../Types/Monitor/CriteriaAlert";
|
|
4
|
+
import { CheckOn, FilterType } from "../../../Types/Monitor/CriteriaFilter";
|
|
5
|
+
import { CriteriaIncident } from "../../../Types/Monitor/CriteriaIncident";
|
|
6
|
+
import MonitorCriteria from "../../../Types/Monitor/MonitorCriteria";
|
|
7
|
+
import MonitorCriteriaInstance from "../../../Types/Monitor/MonitorCriteriaInstance";
|
|
8
|
+
import MonitorStep from "../../../Types/Monitor/MonitorStep";
|
|
9
|
+
import MonitorSteps from "../../../Types/Monitor/MonitorSteps";
|
|
10
|
+
import MonitorType from "../../../Types/Monitor/MonitorType";
|
|
11
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
12
|
+
import { JSONObject } from "../../../Types/JSON";
|
|
13
|
+
|
|
14
|
+
/*
|
|
15
|
+
* Issues #3410 and #3413: "Alert title is required for criteria X" / "Incident
|
|
16
|
+
* title is required for criteria X" blocked Next on the monitor criteria step
|
|
17
|
+
* with the matching switch turned OFF, and there was no way out of it.
|
|
18
|
+
*
|
|
19
|
+
* The mechanism was a mismatch between three layers that all describe the same
|
|
20
|
+
* thing:
|
|
21
|
+
*
|
|
22
|
+
* - the form seeds a blank incident / alert row when you flip a switch on,
|
|
23
|
+
* and deliberately keeps that row when you flip it back off so your typing
|
|
24
|
+
* survives a mis-click;
|
|
25
|
+
* - the evaluator ignores those rows entirely while the flag is off
|
|
26
|
+
* (MonitorIncident / MonitorAlert both return early on a falsy flag);
|
|
27
|
+
* - getValidationError walked both arrays unconditionally.
|
|
28
|
+
*
|
|
29
|
+
* So the form demanded a title for a row that does nothing, and rendered the
|
|
30
|
+
* field to type it into only while the switch was on. Turning the switch back
|
|
31
|
+
* on to clear the error re-armed an action the user did not want.
|
|
32
|
+
*
|
|
33
|
+
* This file pins the fixed contract at every level of the chain, because the
|
|
34
|
+
* error the user sees is produced four levels above where the decision is made
|
|
35
|
+
* (MonitorSteps -> MonitorStep -> MonitorCriteria -> MonitorCriteriaInstance)
|
|
36
|
+
* and only the top level is what the form actually calls.
|
|
37
|
+
*/
|
|
38
|
+
|
|
39
|
+
const MONITOR_STATUS_ID: ObjectID = new ObjectID(
|
|
40
|
+
"11111111-1111-4111-8111-111111111111",
|
|
41
|
+
);
|
|
42
|
+
const INCIDENT_SEVERITY_ID: ObjectID = new ObjectID(
|
|
43
|
+
"22222222-2222-4222-8222-222222222222",
|
|
44
|
+
);
|
|
45
|
+
const ALERT_SEVERITY_ID: ObjectID = new ObjectID(
|
|
46
|
+
"33333333-3333-4333-8333-333333333333",
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
// The row the criteria form seeds the moment a switch is flipped on.
|
|
50
|
+
function blankIncident(): CriteriaIncident {
|
|
51
|
+
return {
|
|
52
|
+
title: "",
|
|
53
|
+
description: "",
|
|
54
|
+
incidentSeverityId: undefined,
|
|
55
|
+
id: ObjectID.generate().toString(),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function blankAlert(): CriteriaAlert {
|
|
60
|
+
return {
|
|
61
|
+
title: "",
|
|
62
|
+
description: "",
|
|
63
|
+
alertSeverityId: undefined,
|
|
64
|
+
id: ObjectID.generate().toString(),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function filledIncident(): CriteriaIncident {
|
|
69
|
+
return {
|
|
70
|
+
title: "AWS Status Page has an active incident",
|
|
71
|
+
description: "",
|
|
72
|
+
incidentSeverityId: INCIDENT_SEVERITY_ID,
|
|
73
|
+
id: ObjectID.generate().toString(),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function filledAlert(): CriteriaAlert {
|
|
78
|
+
return {
|
|
79
|
+
title: "UND66DWANRTR01 is offline",
|
|
80
|
+
description: "",
|
|
81
|
+
alertSeverityId: ALERT_SEVERITY_ID,
|
|
82
|
+
id: ObjectID.generate().toString(),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/*
|
|
87
|
+
* A criteria in the state the reporter's screenshot shows: only "change
|
|
88
|
+
* monitor status" is on, and the two other actions are off.
|
|
89
|
+
*/
|
|
90
|
+
function buildCriteria(
|
|
91
|
+
overrides?: Partial<MonitorCriteriaInstance["data"]>,
|
|
92
|
+
): MonitorCriteriaInstance {
|
|
93
|
+
const instance: MonitorCriteriaInstance = new MonitorCriteriaInstance();
|
|
94
|
+
instance.data = {
|
|
95
|
+
id: ObjectID.generate().toString(),
|
|
96
|
+
monitorStatusId: MONITOR_STATUS_ID,
|
|
97
|
+
filterCondition: FilterCondition.All,
|
|
98
|
+
filters: [
|
|
99
|
+
{
|
|
100
|
+
checkOn: CheckOn.IsOnline,
|
|
101
|
+
filterType: FilterType.True,
|
|
102
|
+
value: undefined,
|
|
103
|
+
},
|
|
104
|
+
],
|
|
105
|
+
incidents: [],
|
|
106
|
+
alerts: [],
|
|
107
|
+
changeMonitorStatus: true,
|
|
108
|
+
createIncidents: false,
|
|
109
|
+
createAlerts: false,
|
|
110
|
+
isEnabled: true,
|
|
111
|
+
name: "Check if AWS Status Page is operational",
|
|
112
|
+
description: "Checks whether the external status page is healthy",
|
|
113
|
+
...(overrides || {}),
|
|
114
|
+
};
|
|
115
|
+
return instance;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function validate(instance: MonitorCriteriaInstance): string | null {
|
|
119
|
+
return MonitorCriteriaInstance.getValidationError(instance, MonitorType.Ping);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
describe("MonitorCriteriaInstance.getValidationError - per-action gating", () => {
|
|
123
|
+
describe("declare an incident (createIncidents)", () => {
|
|
124
|
+
test("a blank incident row left behind by the switch does not block while the switch is off", () => {
|
|
125
|
+
const instance: MonitorCriteriaInstance = buildCriteria({
|
|
126
|
+
createIncidents: false,
|
|
127
|
+
incidents: [blankIncident()],
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
expect(validate(instance)).toBeNull();
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
test("the same blank row does block once the switch is on", () => {
|
|
134
|
+
const instance: MonitorCriteriaInstance = buildCriteria({
|
|
135
|
+
createIncidents: true,
|
|
136
|
+
incidents: [blankIncident()],
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
expect(validate(instance)).toBe(
|
|
140
|
+
'Incident title is required for criteria "Check if AWS Status Page is operational"',
|
|
141
|
+
);
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test("a missing severity blocks only while the switch is on", () => {
|
|
145
|
+
const row: CriteriaIncident = {
|
|
146
|
+
...filledIncident(),
|
|
147
|
+
incidentSeverityId: undefined,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
expect(
|
|
151
|
+
validate(buildCriteria({ createIncidents: false, incidents: [row] })),
|
|
152
|
+
).toBeNull();
|
|
153
|
+
expect(
|
|
154
|
+
validate(buildCriteria({ createIncidents: true, incidents: [row] })),
|
|
155
|
+
).toBe(
|
|
156
|
+
'Incident severity is required for criteria "Check if AWS Status Page is operational"',
|
|
157
|
+
);
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
test("a fully filled incident passes with the switch on", () => {
|
|
161
|
+
expect(
|
|
162
|
+
validate(
|
|
163
|
+
buildCriteria({
|
|
164
|
+
createIncidents: true,
|
|
165
|
+
incidents: [filledIncident()],
|
|
166
|
+
}),
|
|
167
|
+
),
|
|
168
|
+
).toBeNull();
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
describe("the two actions are independent", () => {
|
|
173
|
+
/*
|
|
174
|
+
* The recommendation flow ships exactly this shape: both payloads
|
|
175
|
+
* authored, one flag on. Whichever side is off must not be able to block
|
|
176
|
+
* the other, in either direction.
|
|
177
|
+
*/
|
|
178
|
+
test("incidents on and valid, alerts off and blank", () => {
|
|
179
|
+
expect(
|
|
180
|
+
validate(
|
|
181
|
+
buildCriteria({
|
|
182
|
+
createIncidents: true,
|
|
183
|
+
incidents: [filledIncident()],
|
|
184
|
+
createAlerts: false,
|
|
185
|
+
alerts: [blankAlert()],
|
|
186
|
+
}),
|
|
187
|
+
),
|
|
188
|
+
).toBeNull();
|
|
189
|
+
});
|
|
190
|
+
|
|
191
|
+
test("alerts on and valid, incidents off and blank", () => {
|
|
192
|
+
expect(
|
|
193
|
+
validate(
|
|
194
|
+
buildCriteria({
|
|
195
|
+
createAlerts: true,
|
|
196
|
+
alerts: [filledAlert()],
|
|
197
|
+
createIncidents: false,
|
|
198
|
+
incidents: [blankIncident()],
|
|
199
|
+
}),
|
|
200
|
+
),
|
|
201
|
+
).toBeNull();
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
test("both on and both blank reports the incident first", () => {
|
|
205
|
+
expect(
|
|
206
|
+
validate(
|
|
207
|
+
buildCriteria({
|
|
208
|
+
createIncidents: true,
|
|
209
|
+
incidents: [blankIncident()],
|
|
210
|
+
createAlerts: true,
|
|
211
|
+
alerts: [blankAlert()],
|
|
212
|
+
}),
|
|
213
|
+
),
|
|
214
|
+
).toBe(
|
|
215
|
+
'Incident title is required for criteria "Check if AWS Status Page is operational"',
|
|
216
|
+
);
|
|
217
|
+
});
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
describe("create an alert (createAlerts)", () => {
|
|
221
|
+
test("a blank alert row left behind by the switch does not block while the switch is off", () => {
|
|
222
|
+
const instance: MonitorCriteriaInstance = buildCriteria({
|
|
223
|
+
createAlerts: false,
|
|
224
|
+
alerts: [blankAlert()],
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
expect(validate(instance)).toBeNull();
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
test("the same blank row does block once the switch is on", () => {
|
|
231
|
+
const instance: MonitorCriteriaInstance = buildCriteria({
|
|
232
|
+
createAlerts: true,
|
|
233
|
+
alerts: [blankAlert()],
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
expect(validate(instance)).toBe(
|
|
237
|
+
'Alert title is required for criteria "Check if AWS Status Page is operational"',
|
|
238
|
+
);
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
test("typing the title clears the error - the whole of #3410 step 3", () => {
|
|
242
|
+
const instance: MonitorCriteriaInstance = buildCriteria({
|
|
243
|
+
createAlerts: true,
|
|
244
|
+
alerts: [blankAlert()],
|
|
245
|
+
});
|
|
246
|
+
expect(validate(instance)).toContain("Alert title is required");
|
|
247
|
+
|
|
248
|
+
instance.data!.alerts = [
|
|
249
|
+
{ ...instance.data!.alerts[0]!, title: "UND66DWANRTR01 is offline" },
|
|
250
|
+
];
|
|
251
|
+
expect(validate(instance)).toContain("Alert severity is required");
|
|
252
|
+
|
|
253
|
+
instance.data!.alerts = [
|
|
254
|
+
{ ...instance.data!.alerts[0]!, alertSeverityId: ALERT_SEVERITY_ID },
|
|
255
|
+
];
|
|
256
|
+
expect(validate(instance)).toBeNull();
|
|
257
|
+
});
|
|
258
|
+
|
|
259
|
+
test("a missing severity blocks only while the switch is on", () => {
|
|
260
|
+
const row: CriteriaAlert = {
|
|
261
|
+
...filledAlert(),
|
|
262
|
+
alertSeverityId: undefined,
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
expect(
|
|
266
|
+
validate(buildCriteria({ createAlerts: false, alerts: [row] })),
|
|
267
|
+
).toBeNull();
|
|
268
|
+
expect(
|
|
269
|
+
validate(buildCriteria({ createAlerts: true, alerts: [row] })),
|
|
270
|
+
).toBe(
|
|
271
|
+
'Alert severity is required for criteria "Check if AWS Status Page is operational"',
|
|
272
|
+
);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
describe("description is optional", () => {
|
|
277
|
+
/*
|
|
278
|
+
* The criteria form marks Title and Severity `required`; the description
|
|
279
|
+
* sits in a collapsed section labelled "Optional incident description" /
|
|
280
|
+
* "Optional alert description", and Incident.description and
|
|
281
|
+
* Alert.description are both nullable columns. Requiring it here left the
|
|
282
|
+
* form stuck on a field the UI calls optional and folds out of sight,
|
|
283
|
+
* which is why #3410 reads "permanently stuck with no way to proceed".
|
|
284
|
+
*/
|
|
285
|
+
test("an incident with a title and a severity passes with no description", () => {
|
|
286
|
+
expect(
|
|
287
|
+
validate(
|
|
288
|
+
buildCriteria({
|
|
289
|
+
createIncidents: true,
|
|
290
|
+
incidents: [{ ...filledIncident(), description: "" }],
|
|
291
|
+
}),
|
|
292
|
+
),
|
|
293
|
+
).toBeNull();
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
test("an alert with a title and a severity passes with no description", () => {
|
|
297
|
+
expect(
|
|
298
|
+
validate(
|
|
299
|
+
buildCriteria({
|
|
300
|
+
createAlerts: true,
|
|
301
|
+
alerts: [{ ...filledAlert(), description: "" }],
|
|
302
|
+
}),
|
|
303
|
+
),
|
|
304
|
+
).toBeNull();
|
|
305
|
+
});
|
|
306
|
+
|
|
307
|
+
test("an absent description is accepted too", () => {
|
|
308
|
+
/*
|
|
309
|
+
* CriteriaAlert types description as a string, but nothing enforces
|
|
310
|
+
* that on the way in from the REST API, so the absent case is reachable
|
|
311
|
+
* with a payload authored outside the form.
|
|
312
|
+
*/
|
|
313
|
+
const row: CriteriaAlert = {
|
|
314
|
+
...filledAlert(),
|
|
315
|
+
description: undefined as unknown as string,
|
|
316
|
+
};
|
|
317
|
+
|
|
318
|
+
expect(
|
|
319
|
+
validate(buildCriteria({ createAlerts: true, alerts: [row] })),
|
|
320
|
+
).toBeNull();
|
|
321
|
+
});
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
describe("every monitor type", () => {
|
|
325
|
+
/*
|
|
326
|
+
* #3410 reports this "affects most/all Monitor types during creation".
|
|
327
|
+
* The gate lives above the per-type filter rules, so it should hold for
|
|
328
|
+
* every type in the enum - driven from Object.values so a new monitor
|
|
329
|
+
* type is covered the day it is added. IsOnline/True is the one filter
|
|
330
|
+
* shape no type rejects.
|
|
331
|
+
*/
|
|
332
|
+
const ALL_MONITOR_TYPES: Array<MonitorType> = Object.values(MonitorType);
|
|
333
|
+
|
|
334
|
+
test("the enum is not empty, so the loops below mean something", () => {
|
|
335
|
+
expect(ALL_MONITOR_TYPES.length).toBeGreaterThan(10);
|
|
336
|
+
});
|
|
337
|
+
|
|
338
|
+
test("a switched-off action never blocks, whatever the monitor type", () => {
|
|
339
|
+
for (const monitorType of ALL_MONITOR_TYPES) {
|
|
340
|
+
const instance: MonitorCriteriaInstance = buildCriteria({
|
|
341
|
+
createIncidents: false,
|
|
342
|
+
incidents: [blankIncident()],
|
|
343
|
+
createAlerts: false,
|
|
344
|
+
alerts: [blankAlert()],
|
|
345
|
+
});
|
|
346
|
+
|
|
347
|
+
expect(
|
|
348
|
+
MonitorCriteriaInstance.getValidationError(instance, monitorType),
|
|
349
|
+
).toBeNull();
|
|
350
|
+
}
|
|
351
|
+
});
|
|
352
|
+
|
|
353
|
+
test("a switched-on action still blocks, whatever the monitor type", () => {
|
|
354
|
+
for (const monitorType of ALL_MONITOR_TYPES) {
|
|
355
|
+
const instance: MonitorCriteriaInstance = buildCriteria({
|
|
356
|
+
createAlerts: true,
|
|
357
|
+
alerts: [blankAlert()],
|
|
358
|
+
});
|
|
359
|
+
|
|
360
|
+
expect(
|
|
361
|
+
MonitorCriteriaInstance.getValidationError(instance, monitorType),
|
|
362
|
+
).toContain("Alert title is required");
|
|
363
|
+
}
|
|
364
|
+
});
|
|
365
|
+
});
|
|
366
|
+
|
|
367
|
+
describe("checks that are not action-gated still run", () => {
|
|
368
|
+
test("the criteria's own name and description are still required", () => {
|
|
369
|
+
expect(validate(buildCriteria({ name: "" }))).toContain(
|
|
370
|
+
"Name is required",
|
|
371
|
+
);
|
|
372
|
+
expect(validate(buildCriteria({ description: "" }))).toContain(
|
|
373
|
+
"Description is required",
|
|
374
|
+
);
|
|
375
|
+
});
|
|
376
|
+
|
|
377
|
+
test("filters are still required and still type-checked", () => {
|
|
378
|
+
expect(validate(buildCriteria({ filters: [] }))).toContain(
|
|
379
|
+
"Filter is required",
|
|
380
|
+
);
|
|
381
|
+
expect(
|
|
382
|
+
MonitorCriteriaInstance.getValidationError(
|
|
383
|
+
buildCriteria({
|
|
384
|
+
filters: [
|
|
385
|
+
{
|
|
386
|
+
checkOn: CheckOn.DiskUsagePercent,
|
|
387
|
+
filterType: FilterType.GreaterThan,
|
|
388
|
+
value: 90,
|
|
389
|
+
},
|
|
390
|
+
],
|
|
391
|
+
}),
|
|
392
|
+
MonitorType.Ping,
|
|
393
|
+
),
|
|
394
|
+
).toContain("Ping Monitor cannot have filter type");
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
test("an empty array with the switch on is not an error", () => {
|
|
398
|
+
/*
|
|
399
|
+
* NetworkDeviceAlertPackUtil.buildCriteriaInstances ships exactly this:
|
|
400
|
+
* createIncidents / createAlerts on with empty arrays, for the user to
|
|
401
|
+
* fill in. Requiring a row here would break "Add Recommended Alerts".
|
|
402
|
+
*/
|
|
403
|
+
expect(
|
|
404
|
+
validate(
|
|
405
|
+
buildCriteria({
|
|
406
|
+
createIncidents: true,
|
|
407
|
+
incidents: [],
|
|
408
|
+
createAlerts: true,
|
|
409
|
+
alerts: [],
|
|
410
|
+
}),
|
|
411
|
+
),
|
|
412
|
+
).toBeNull();
|
|
413
|
+
});
|
|
414
|
+
|
|
415
|
+
test("a null row inside a populated array is still skipped", () => {
|
|
416
|
+
const instance: MonitorCriteriaInstance = buildCriteria({
|
|
417
|
+
createAlerts: true,
|
|
418
|
+
alerts: [
|
|
419
|
+
undefined as unknown as CriteriaAlert,
|
|
420
|
+
filledAlert(),
|
|
421
|
+
] as Array<CriteriaAlert>,
|
|
422
|
+
});
|
|
423
|
+
|
|
424
|
+
expect(validate(instance)).toBeNull();
|
|
425
|
+
});
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
describe("flag provenance", () => {
|
|
429
|
+
test("a criteria saved before the flags existed is treated as off, exactly as the evaluator treats it", () => {
|
|
430
|
+
/*
|
|
431
|
+
* The predicate is the falsy one (`!data.createAlerts`), matching
|
|
432
|
+
* MonitorAlert / MonitorIncident. A row written before these flags
|
|
433
|
+
* shipped deserialises with the flag absent -> false, so it creates
|
|
434
|
+
* nothing at runtime; validating it would block a save over a payload
|
|
435
|
+
* that does nothing.
|
|
436
|
+
*/
|
|
437
|
+
const json: JSONObject = buildCriteria({
|
|
438
|
+
alerts: [blankAlert()],
|
|
439
|
+
}).toJSON();
|
|
440
|
+
delete (json["value"] as JSONObject)["createAlerts"];
|
|
441
|
+
delete (json["value"] as JSONObject)["createIncidents"];
|
|
442
|
+
|
|
443
|
+
const restored: MonitorCriteriaInstance =
|
|
444
|
+
MonitorCriteriaInstance.fromJSON(json);
|
|
445
|
+
|
|
446
|
+
expect(restored.data?.createAlerts).toBe(false);
|
|
447
|
+
expect(validate(restored)).toBeNull();
|
|
448
|
+
});
|
|
449
|
+
|
|
450
|
+
test("the gate survives a toJSON / fromJSON round-trip in both directions", () => {
|
|
451
|
+
const on: MonitorCriteriaInstance = MonitorCriteriaInstance.fromJSON(
|
|
452
|
+
buildCriteria({
|
|
453
|
+
createAlerts: true,
|
|
454
|
+
alerts: [blankAlert()],
|
|
455
|
+
}).toJSON(),
|
|
456
|
+
);
|
|
457
|
+
expect(validate(on)).toContain("Alert title is required");
|
|
458
|
+
|
|
459
|
+
const off: MonitorCriteriaInstance = MonitorCriteriaInstance.fromJSON(
|
|
460
|
+
buildCriteria({
|
|
461
|
+
createAlerts: false,
|
|
462
|
+
alerts: [blankAlert()],
|
|
463
|
+
}).toJSON(),
|
|
464
|
+
);
|
|
465
|
+
expect(validate(off)).toBeNull();
|
|
466
|
+
});
|
|
467
|
+
|
|
468
|
+
test("clone() carries the gate", () => {
|
|
469
|
+
const off: MonitorCriteriaInstance = MonitorCriteriaInstance.clone(
|
|
470
|
+
buildCriteria({ createIncidents: false, incidents: [blankIncident()] }),
|
|
471
|
+
);
|
|
472
|
+
expect(validate(off)).toBeNull();
|
|
473
|
+
});
|
|
474
|
+
|
|
475
|
+
test("the setters flip the gate", () => {
|
|
476
|
+
const instance: MonitorCriteriaInstance = buildCriteria({
|
|
477
|
+
alerts: [blankAlert()],
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
instance.setCreateAlerts(true);
|
|
481
|
+
expect(validate(instance)).toContain("Alert title is required");
|
|
482
|
+
|
|
483
|
+
instance.setCreateAlerts(false);
|
|
484
|
+
expect(validate(instance)).toBeNull();
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
});
|
|
488
|
+
|
|
489
|
+
/*
|
|
490
|
+
* The form never calls MonitorCriteriaInstance.getValidationError directly -
|
|
491
|
+
* it calls MonitorSteps.getValidationError, which walks down through
|
|
492
|
+
* MonitorStep and MonitorCriteria. A gate that worked only at the bottom
|
|
493
|
+
* level would still block Next, so each level is pinned here.
|
|
494
|
+
*/
|
|
495
|
+
describe("the gate reaches the error the form actually shows", () => {
|
|
496
|
+
function stepsWith(instance: MonitorCriteriaInstance): MonitorSteps {
|
|
497
|
+
const criteria: MonitorCriteria = new MonitorCriteria();
|
|
498
|
+
criteria.data = { monitorCriteriaInstanceArray: [instance] };
|
|
499
|
+
|
|
500
|
+
const step: MonitorStep = new MonitorStep();
|
|
501
|
+
step.data = {
|
|
502
|
+
...step.data!,
|
|
503
|
+
monitorDestination: undefined,
|
|
504
|
+
monitorCriteria: criteria,
|
|
505
|
+
};
|
|
506
|
+
|
|
507
|
+
const steps: MonitorSteps = new MonitorSteps();
|
|
508
|
+
steps.data = {
|
|
509
|
+
monitorStepsInstanceArray: [step],
|
|
510
|
+
defaultMonitorStatusId: MONITOR_STATUS_ID,
|
|
511
|
+
};
|
|
512
|
+
return steps;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
test("MonitorCriteria.getValidationError respects the switch", () => {
|
|
516
|
+
const criteria: MonitorCriteria = new MonitorCriteria();
|
|
517
|
+
criteria.data = {
|
|
518
|
+
monitorCriteriaInstanceArray: [
|
|
519
|
+
buildCriteria({ createIncidents: false, incidents: [blankIncident()] }),
|
|
520
|
+
],
|
|
521
|
+
};
|
|
522
|
+
|
|
523
|
+
expect(
|
|
524
|
+
MonitorCriteria.getValidationError(criteria, MonitorType.Manual),
|
|
525
|
+
).toBeNull();
|
|
526
|
+
|
|
527
|
+
criteria.data.monitorCriteriaInstanceArray[0]!.setCreateIncidents(true);
|
|
528
|
+
expect(
|
|
529
|
+
MonitorCriteria.getValidationError(criteria, MonitorType.Manual),
|
|
530
|
+
).toContain("Incident title is required");
|
|
531
|
+
});
|
|
532
|
+
|
|
533
|
+
test("MonitorStep.getValidationError respects the switch", () => {
|
|
534
|
+
const offInstance: MonitorCriteriaInstance = buildCriteria({
|
|
535
|
+
createAlerts: false,
|
|
536
|
+
alerts: [blankAlert()],
|
|
537
|
+
});
|
|
538
|
+
const step: MonitorStep =
|
|
539
|
+
stepsWith(offInstance).data!.monitorStepsInstanceArray[0]!;
|
|
540
|
+
|
|
541
|
+
expect(MonitorStep.getValidationError(step, MonitorType.Manual)).toBeNull();
|
|
542
|
+
|
|
543
|
+
offInstance.setCreateAlerts(true);
|
|
544
|
+
expect(MonitorStep.getValidationError(step, MonitorType.Manual)).toContain(
|
|
545
|
+
"Alert title is required",
|
|
546
|
+
);
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
test("MonitorSteps.getValidationError - the exact call the create form makes - respects the switch", () => {
|
|
550
|
+
const instance: MonitorCriteriaInstance = buildCriteria({
|
|
551
|
+
createIncidents: false,
|
|
552
|
+
incidents: [blankIncident()],
|
|
553
|
+
createAlerts: false,
|
|
554
|
+
alerts: [blankAlert()],
|
|
555
|
+
});
|
|
556
|
+
|
|
557
|
+
const steps: MonitorSteps = stepsWith(instance);
|
|
558
|
+
|
|
559
|
+
expect(
|
|
560
|
+
MonitorSteps.getValidationError(steps, MonitorType.Manual),
|
|
561
|
+
).toBeNull();
|
|
562
|
+
|
|
563
|
+
/*
|
|
564
|
+
* And the other direction from the same object, so this pins the gate
|
|
565
|
+
* rather than just "these steps happen to be valid".
|
|
566
|
+
*/
|
|
567
|
+
instance.setCreateIncidents(true);
|
|
568
|
+
expect(MonitorSteps.getValidationError(steps, MonitorType.Manual)).toBe(
|
|
569
|
+
'Incident title is required for criteria "Check if AWS Status Page is operational"',
|
|
570
|
+
);
|
|
571
|
+
|
|
572
|
+
instance.setCreateIncidents(false);
|
|
573
|
+
instance.setCreateAlerts(true);
|
|
574
|
+
expect(MonitorSteps.getValidationError(steps, MonitorType.Manual)).toBe(
|
|
575
|
+
'Alert title is required for criteria "Check if AWS Status Page is operational"',
|
|
576
|
+
);
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
test("one criteria with its switch on still blocks the whole form", () => {
|
|
580
|
+
const criteria: MonitorCriteria = new MonitorCriteria();
|
|
581
|
+
criteria.data = {
|
|
582
|
+
monitorCriteriaInstanceArray: [
|
|
583
|
+
buildCriteria({ createAlerts: false, alerts: [blankAlert()] }),
|
|
584
|
+
buildCriteria({
|
|
585
|
+
name: "Check if AWS Status Page is down",
|
|
586
|
+
createIncidents: true,
|
|
587
|
+
incidents: [blankIncident()],
|
|
588
|
+
}),
|
|
589
|
+
],
|
|
590
|
+
};
|
|
591
|
+
|
|
592
|
+
expect(
|
|
593
|
+
MonitorCriteria.getValidationError(criteria, MonitorType.Manual),
|
|
594
|
+
).toBe(
|
|
595
|
+
'Incident title is required for criteria "Check if AWS Status Page is down"',
|
|
596
|
+
);
|
|
597
|
+
});
|
|
598
|
+
});
|
|
@@ -58,6 +58,14 @@ describe("MonitorCriteriaInstance", () => {
|
|
|
58
58
|
expect(instance.data?.filterCondition).toBe(FilterCondition.All);
|
|
59
59
|
expect(instance.data?.filters).toHaveLength(1);
|
|
60
60
|
expect(instance.data?.filters[0]?.checkOn).toBe(CheckOn.IsOnline);
|
|
61
|
+
/*
|
|
62
|
+
* The condition is seeded too. A filter carrying only a check shows
|
|
63
|
+
* an empty "Filter Condition" dropdown in the criteria form, and is
|
|
64
|
+
* dead on the server - every comparator in CompareCriteria switches
|
|
65
|
+
* on the filter type and treats one it does not recognise as "no
|
|
66
|
+
* match", so the criteria never fires. See #3412.
|
|
67
|
+
*/
|
|
68
|
+
expect(instance.data?.filters[0]?.filterType).toBe(FilterType.True);
|
|
61
69
|
expect(instance.data?.isEnabled).toBe(true);
|
|
62
70
|
// id should be a valid, non-empty ObjectID string.
|
|
63
71
|
expect(typeof instance.data?.id).toBe("string");
|
|
@@ -268,6 +276,7 @@ describe("MonitorCriteriaInstance", () => {
|
|
|
268
276
|
|
|
269
277
|
test("returns error when an incident is missing its severity", () => {
|
|
270
278
|
const instance: MonitorCriteriaInstance = buildValidInstance();
|
|
279
|
+
instance.data!.createIncidents = true;
|
|
271
280
|
instance.data!.incidents = [
|
|
272
281
|
{
|
|
273
282
|
title: "Down",
|
|
@@ -286,6 +295,7 @@ describe("MonitorCriteriaInstance", () => {
|
|
|
286
295
|
|
|
287
296
|
test("returns error when an alert is missing its title", () => {
|
|
288
297
|
const instance: MonitorCriteriaInstance = buildValidInstance();
|
|
298
|
+
instance.data!.createAlerts = true;
|
|
289
299
|
instance.data!.alerts = [
|
|
290
300
|
{
|
|
291
301
|
title: "",
|
|
@@ -350,6 +360,45 @@ describe("MonitorCriteriaInstance", () => {
|
|
|
350
360
|
expect(error).toContain("Value is required");
|
|
351
361
|
});
|
|
352
362
|
|
|
363
|
+
test("returns error when a filter has no condition", () => {
|
|
364
|
+
/*
|
|
365
|
+
* The blank "Filter Condition" dropdown reported in #3412 sat next to
|
|
366
|
+
* fields that were all filled in, so it was easy to save past. A
|
|
367
|
+
* criteria with no condition can never match, so refuse it rather
|
|
368
|
+
* than accept a rule that silently does nothing.
|
|
369
|
+
*/
|
|
370
|
+
const instance: MonitorCriteriaInstance = buildValidInstance();
|
|
371
|
+
instance.data!.filters = [
|
|
372
|
+
{
|
|
373
|
+
checkOn: CheckOn.ExternalStatusPageActiveIncidents,
|
|
374
|
+
filterType: undefined,
|
|
375
|
+
value: 0,
|
|
376
|
+
},
|
|
377
|
+
];
|
|
378
|
+
const error: string | null = MonitorCriteriaInstance.getValidationError(
|
|
379
|
+
instance,
|
|
380
|
+
MonitorType.ExternalStatusPage,
|
|
381
|
+
);
|
|
382
|
+
expect(error).toContain("Filter Condition is required");
|
|
383
|
+
expect(error).toContain(CheckOn.ExternalStatusPageActiveIncidents);
|
|
384
|
+
});
|
|
385
|
+
|
|
386
|
+
test("accepts the criteria an External Status Page monitor is created with", () => {
|
|
387
|
+
const instance: MonitorCriteriaInstance =
|
|
388
|
+
MonitorCriteriaInstance.getDefaultOnlineMonitorCriteriaInstance({
|
|
389
|
+
monitorType: MonitorType.ExternalStatusPage,
|
|
390
|
+
monitorStatusId: new ObjectID("aaaaaaaaaaaaaaaaaaaaaaaa"),
|
|
391
|
+
monitorName: "Acme Status",
|
|
392
|
+
})!;
|
|
393
|
+
|
|
394
|
+
expect(
|
|
395
|
+
MonitorCriteriaInstance.getValidationError(
|
|
396
|
+
instance,
|
|
397
|
+
MonitorType.ExternalStatusPage,
|
|
398
|
+
),
|
|
399
|
+
).toBeNull();
|
|
400
|
+
});
|
|
401
|
+
|
|
353
402
|
test("returns null for a fully valid instance", () => {
|
|
354
403
|
const instance: MonitorCriteriaInstance = buildValidInstance();
|
|
355
404
|
expect(
|
|
@@ -540,12 +540,12 @@ describe("MonitorRecommendationNotificationMode", () => {
|
|
|
540
540
|
it("still passes MonitorSteps.getValidationError under every mode, for every recommendation", () => {
|
|
541
541
|
/*
|
|
542
542
|
* `MonitorSteps.getValidationError` is the same client-side gate the
|
|
543
|
-
* monitor create form runs before POSTing
|
|
544
|
-
*
|
|
545
|
-
* mode implementation that
|
|
546
|
-
*
|
|
547
|
-
*
|
|
548
|
-
*
|
|
543
|
+
* monitor create form runs before POSTing. It validates the incident and
|
|
544
|
+
* alert payloads of whichever side the mode leaves switched on, so a
|
|
545
|
+
* mode implementation that blanked a title or a severity id on the side
|
|
546
|
+
* it kept enabled would fail here — at submit time in production, with a
|
|
547
|
+
* message about a field the user was never shown, and only for the mode
|
|
548
|
+
* they happened to pick.
|
|
549
549
|
*/
|
|
550
550
|
for (const recommendation of ALL_RECOMMENDATIONS) {
|
|
551
551
|
for (const notificationMode of ALL_MODES) {
|