@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,346 @@
|
|
|
1
|
+
import { describe, expect, test } from "@jest/globals";
|
|
2
|
+
import SecurityEvent from "../../../Models/AnalyticsModels/SecurityEvent";
|
|
3
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
4
|
+
import OcsfSeverity from "../../../Types/SecurityEvent/OcsfSeverity";
|
|
5
|
+
import {
|
|
6
|
+
CENTER_NODE_ID,
|
|
7
|
+
CLASS_NODE_PREFIX,
|
|
8
|
+
CorrelationGraphData,
|
|
9
|
+
CorrelationGraphEdge,
|
|
10
|
+
CorrelationGraphNode,
|
|
11
|
+
OBSERVABLE_NODE_PREFIX,
|
|
12
|
+
buildCorrelationGraph,
|
|
13
|
+
dedupeSecurityEvents,
|
|
14
|
+
severityRank,
|
|
15
|
+
} from "../../../../App/FeatureSet/Dashboard/src/Utils/CorrelationGraph";
|
|
16
|
+
|
|
17
|
+
/*
|
|
18
|
+
* The correlate graph builder aggregates fetched events into the
|
|
19
|
+
* center/class/observable neighborhood: event counts per class, worst
|
|
20
|
+
* severity per class (for node tinting), co-observable counting with
|
|
21
|
+
* per-event dedupe, exclusion of the searched values, the top-N cap with
|
|
22
|
+
* honest drop accounting, and the union-dedupe used when OR mode runs one
|
|
23
|
+
* query per condition. All pinned here as pure data — no React, no
|
|
24
|
+
* ReactFlow.
|
|
25
|
+
*/
|
|
26
|
+
|
|
27
|
+
interface EventInput {
|
|
28
|
+
id?: string;
|
|
29
|
+
eventUid?: string;
|
|
30
|
+
className?: string;
|
|
31
|
+
severityName?: OcsfSeverity;
|
|
32
|
+
observables?: Array<string>;
|
|
33
|
+
time?: Date;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function buildEvent(input: EventInput): SecurityEvent {
|
|
37
|
+
const event: SecurityEvent = new SecurityEvent();
|
|
38
|
+
if (input.id) {
|
|
39
|
+
event._id = new ObjectID(input.id);
|
|
40
|
+
}
|
|
41
|
+
if (input.eventUid) {
|
|
42
|
+
event.eventUid = input.eventUid;
|
|
43
|
+
}
|
|
44
|
+
if (input.className) {
|
|
45
|
+
event.className = input.className;
|
|
46
|
+
}
|
|
47
|
+
if (input.severityName) {
|
|
48
|
+
event.severityName = input.severityName;
|
|
49
|
+
}
|
|
50
|
+
event.observables = input.observables || [];
|
|
51
|
+
if (input.time) {
|
|
52
|
+
event.time = input.time;
|
|
53
|
+
}
|
|
54
|
+
return event;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function nodeById(
|
|
58
|
+
graph: CorrelationGraphData,
|
|
59
|
+
id: string,
|
|
60
|
+
): CorrelationGraphNode | undefined {
|
|
61
|
+
return graph.nodes.find((node: CorrelationGraphNode) => {
|
|
62
|
+
return node.id === id;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function edgeById(
|
|
67
|
+
graph: CorrelationGraphData,
|
|
68
|
+
id: string,
|
|
69
|
+
): CorrelationGraphEdge | undefined {
|
|
70
|
+
return graph.edges.find((edge: CorrelationGraphEdge) => {
|
|
71
|
+
return edge.id === id;
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
describe("severityRank", () => {
|
|
76
|
+
test("orders real severities and puts Unknown/Other/blank at the bottom", () => {
|
|
77
|
+
expect(severityRank(OcsfSeverity.Fatal)).toBeGreaterThan(
|
|
78
|
+
severityRank(OcsfSeverity.Critical),
|
|
79
|
+
);
|
|
80
|
+
expect(severityRank(OcsfSeverity.Critical)).toBeGreaterThan(
|
|
81
|
+
severityRank(OcsfSeverity.High),
|
|
82
|
+
);
|
|
83
|
+
expect(severityRank(OcsfSeverity.High)).toBeGreaterThan(
|
|
84
|
+
severityRank(OcsfSeverity.Medium),
|
|
85
|
+
);
|
|
86
|
+
expect(severityRank(OcsfSeverity.Medium)).toBeGreaterThan(
|
|
87
|
+
severityRank(OcsfSeverity.Low),
|
|
88
|
+
);
|
|
89
|
+
expect(severityRank(OcsfSeverity.Low)).toBeGreaterThan(
|
|
90
|
+
severityRank(OcsfSeverity.Informational),
|
|
91
|
+
);
|
|
92
|
+
expect(severityRank(OcsfSeverity.Unknown)).toBe(0);
|
|
93
|
+
expect(severityRank(OcsfSeverity.Other)).toBe(0);
|
|
94
|
+
expect(severityRank(undefined)).toBe(0);
|
|
95
|
+
expect(severityRank("")).toBe(0);
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
describe("buildCorrelationGraph", () => {
|
|
100
|
+
test("no events → empty graph", () => {
|
|
101
|
+
const graph: CorrelationGraphData = buildCorrelationGraph({
|
|
102
|
+
events: [],
|
|
103
|
+
centerLabel: 'Observable is "x"',
|
|
104
|
+
excludedObservables: ["x"],
|
|
105
|
+
});
|
|
106
|
+
expect(graph.nodes).toHaveLength(0);
|
|
107
|
+
expect(graph.edges).toHaveLength(0);
|
|
108
|
+
expect(graph.droppedCoObservableCount).toBe(0);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
test("one node per class with event counts on the center edges", () => {
|
|
112
|
+
const graph: CorrelationGraphData = buildCorrelationGraph({
|
|
113
|
+
events: [
|
|
114
|
+
buildEvent({ className: "Authentication", observables: ["host-1"] }),
|
|
115
|
+
buildEvent({ className: "Authentication", observables: ["host-1"] }),
|
|
116
|
+
buildEvent({ className: "Process Activity", observables: ["host-1"] }),
|
|
117
|
+
buildEvent({ observables: ["host-1"] }), // no class → Unclassified
|
|
118
|
+
],
|
|
119
|
+
centerLabel: "center-label",
|
|
120
|
+
excludedObservables: ["host-1"],
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
const center: CorrelationGraphNode | undefined = nodeById(
|
|
124
|
+
graph,
|
|
125
|
+
CENTER_NODE_ID,
|
|
126
|
+
);
|
|
127
|
+
expect(center).toBeTruthy();
|
|
128
|
+
expect(center?.label).toBe("center-label");
|
|
129
|
+
expect(center?.kind).toBe("center");
|
|
130
|
+
|
|
131
|
+
const authClass: CorrelationGraphNode | undefined = nodeById(
|
|
132
|
+
graph,
|
|
133
|
+
`${CLASS_NODE_PREFIX}Authentication`,
|
|
134
|
+
);
|
|
135
|
+
expect(authClass?.count).toBe(2);
|
|
136
|
+
expect(
|
|
137
|
+
edgeById(graph, `${CENTER_NODE_ID}->${CLASS_NODE_PREFIX}Authentication`)
|
|
138
|
+
?.count,
|
|
139
|
+
).toBe(2);
|
|
140
|
+
|
|
141
|
+
expect(nodeById(graph, `${CLASS_NODE_PREFIX}Unclassified`)?.count).toBe(1);
|
|
142
|
+
expect(nodeById(graph, `${CLASS_NODE_PREFIX}Process Activity`)?.count).toBe(
|
|
143
|
+
1,
|
|
144
|
+
);
|
|
145
|
+
|
|
146
|
+
// Excluded observable never becomes a node.
|
|
147
|
+
expect(nodeById(graph, `${OBSERVABLE_NODE_PREFIX}host-1`)).toBeUndefined();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
test("class nodes carry the WORST severity among their events", () => {
|
|
151
|
+
const graph: CorrelationGraphData = buildCorrelationGraph({
|
|
152
|
+
events: [
|
|
153
|
+
buildEvent({
|
|
154
|
+
className: "Authentication",
|
|
155
|
+
severityName: OcsfSeverity.Low,
|
|
156
|
+
}),
|
|
157
|
+
buildEvent({
|
|
158
|
+
className: "Authentication",
|
|
159
|
+
severityName: OcsfSeverity.Critical,
|
|
160
|
+
}),
|
|
161
|
+
buildEvent({
|
|
162
|
+
className: "Authentication",
|
|
163
|
+
severityName: OcsfSeverity.Medium,
|
|
164
|
+
}),
|
|
165
|
+
buildEvent({
|
|
166
|
+
className: "Compliance Finding",
|
|
167
|
+
severityName: OcsfSeverity.Unknown,
|
|
168
|
+
}),
|
|
169
|
+
],
|
|
170
|
+
centerLabel: "c",
|
|
171
|
+
excludedObservables: [],
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
expect(
|
|
175
|
+
nodeById(graph, `${CLASS_NODE_PREFIX}Authentication`)?.worstSeverity,
|
|
176
|
+
).toBe(OcsfSeverity.Critical);
|
|
177
|
+
/*
|
|
178
|
+
* Unknown ranks zero — the node gets NO severity rather than
|
|
179
|
+
* pretending "Unknown" is a color-worthy signal.
|
|
180
|
+
*/
|
|
181
|
+
expect(
|
|
182
|
+
nodeById(graph, `${CLASS_NODE_PREFIX}Compliance Finding`)?.worstSeverity,
|
|
183
|
+
).toBeUndefined();
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
test("co-observables count once per event even when repeated in one row", () => {
|
|
187
|
+
const graph: CorrelationGraphData = buildCorrelationGraph({
|
|
188
|
+
events: [
|
|
189
|
+
buildEvent({
|
|
190
|
+
className: "Authentication",
|
|
191
|
+
observables: ["searched", "alice", "alice", "alice"],
|
|
192
|
+
}),
|
|
193
|
+
buildEvent({
|
|
194
|
+
className: "Authentication",
|
|
195
|
+
observables: ["searched", "alice"],
|
|
196
|
+
}),
|
|
197
|
+
],
|
|
198
|
+
centerLabel: "c",
|
|
199
|
+
excludedObservables: ["searched"],
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
expect(
|
|
203
|
+
edgeById(
|
|
204
|
+
graph,
|
|
205
|
+
`${CLASS_NODE_PREFIX}Authentication->${OBSERVABLE_NODE_PREFIX}alice`,
|
|
206
|
+
)?.count,
|
|
207
|
+
).toBe(2);
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
test("empty observable strings are ignored", () => {
|
|
211
|
+
const graph: CorrelationGraphData = buildCorrelationGraph({
|
|
212
|
+
events: [
|
|
213
|
+
buildEvent({ className: "Authentication", observables: ["", "alice"] }),
|
|
214
|
+
],
|
|
215
|
+
centerLabel: "c",
|
|
216
|
+
excludedObservables: [],
|
|
217
|
+
});
|
|
218
|
+
expect(nodeById(graph, `${OBSERVABLE_NODE_PREFIX}alice`)).toBeTruthy();
|
|
219
|
+
expect(nodeById(graph, `${OBSERVABLE_NODE_PREFIX}`)).toBeUndefined();
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
test("an observable shared by two classes gets ONE node and two edges", () => {
|
|
223
|
+
const graph: CorrelationGraphData = buildCorrelationGraph({
|
|
224
|
+
events: [
|
|
225
|
+
buildEvent({ className: "Authentication", observables: ["alice"] }),
|
|
226
|
+
buildEvent({ className: "Process Activity", observables: ["alice"] }),
|
|
227
|
+
],
|
|
228
|
+
centerLabel: "c",
|
|
229
|
+
excludedObservables: [],
|
|
230
|
+
});
|
|
231
|
+
|
|
232
|
+
const observableNodes: Array<CorrelationGraphNode> = graph.nodes.filter(
|
|
233
|
+
(node: CorrelationGraphNode) => {
|
|
234
|
+
return node.kind === "observable";
|
|
235
|
+
},
|
|
236
|
+
);
|
|
237
|
+
expect(observableNodes).toHaveLength(1);
|
|
238
|
+
expect(
|
|
239
|
+
edgeById(
|
|
240
|
+
graph,
|
|
241
|
+
`${CLASS_NODE_PREFIX}Authentication->${OBSERVABLE_NODE_PREFIX}alice`,
|
|
242
|
+
),
|
|
243
|
+
).toBeTruthy();
|
|
244
|
+
expect(
|
|
245
|
+
edgeById(
|
|
246
|
+
graph,
|
|
247
|
+
`${CLASS_NODE_PREFIX}Process Activity->${OBSERVABLE_NODE_PREFIX}alice`,
|
|
248
|
+
),
|
|
249
|
+
).toBeTruthy();
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
test("caps co-observables at the most frequent N and reports the drop count", () => {
|
|
253
|
+
const events: Array<SecurityEvent> = [];
|
|
254
|
+
// "popular" appears in 3 events, every "rare-N" in exactly one.
|
|
255
|
+
for (let eventIndex: number = 0; eventIndex < 3; eventIndex++) {
|
|
256
|
+
events.push(
|
|
257
|
+
buildEvent({
|
|
258
|
+
className: "Authentication",
|
|
259
|
+
observables: ["popular", `rare-${eventIndex}`],
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
events.push(
|
|
264
|
+
buildEvent({ className: "Authentication", observables: ["rare-3"] }),
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
const graph: CorrelationGraphData = buildCorrelationGraph({
|
|
268
|
+
events: events,
|
|
269
|
+
centerLabel: "c",
|
|
270
|
+
excludedObservables: [],
|
|
271
|
+
maxCoObservables: 2,
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
const observableNodes: Array<CorrelationGraphNode> = graph.nodes.filter(
|
|
275
|
+
(node: CorrelationGraphNode) => {
|
|
276
|
+
return node.kind === "observable";
|
|
277
|
+
},
|
|
278
|
+
);
|
|
279
|
+
expect(observableNodes).toHaveLength(2);
|
|
280
|
+
// Highest count first, then alphabetical among the tied rares.
|
|
281
|
+
expect(nodeById(graph, `${OBSERVABLE_NODE_PREFIX}popular`)).toBeTruthy();
|
|
282
|
+
expect(nodeById(graph, `${OBSERVABLE_NODE_PREFIX}rare-0`)).toBeTruthy();
|
|
283
|
+
expect(graph.droppedCoObservableCount).toBe(3);
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
test("cap exactly met → nothing reported dropped", () => {
|
|
287
|
+
const graph: CorrelationGraphData = buildCorrelationGraph({
|
|
288
|
+
events: [
|
|
289
|
+
buildEvent({ className: "Authentication", observables: ["a", "b"] }),
|
|
290
|
+
],
|
|
291
|
+
centerLabel: "c",
|
|
292
|
+
excludedObservables: [],
|
|
293
|
+
maxCoObservables: 2,
|
|
294
|
+
});
|
|
295
|
+
expect(graph.droppedCoObservableCount).toBe(0);
|
|
296
|
+
});
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
describe("dedupeSecurityEvents", () => {
|
|
300
|
+
test("unions result sets by row id", () => {
|
|
301
|
+
const shared: SecurityEvent = buildEvent({
|
|
302
|
+
id: "11111111-1111-4111-8111-111111111111",
|
|
303
|
+
className: "Authentication",
|
|
304
|
+
});
|
|
305
|
+
const onlyFirst: SecurityEvent = buildEvent({
|
|
306
|
+
id: "22222222-2222-4222-8222-222222222222",
|
|
307
|
+
});
|
|
308
|
+
const onlySecond: SecurityEvent = buildEvent({
|
|
309
|
+
id: "33333333-3333-4333-8333-333333333333",
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const deduped: Array<SecurityEvent> = dedupeSecurityEvents([
|
|
313
|
+
[onlyFirst, shared],
|
|
314
|
+
[shared, onlySecond],
|
|
315
|
+
]);
|
|
316
|
+
|
|
317
|
+
expect(deduped).toHaveLength(3);
|
|
318
|
+
expect(deduped[0]).toBe(onlyFirst);
|
|
319
|
+
expect(deduped[1]).toBe(shared);
|
|
320
|
+
expect(deduped[2]).toBe(onlySecond);
|
|
321
|
+
});
|
|
322
|
+
|
|
323
|
+
test("two model instances with the SAME id count once", () => {
|
|
324
|
+
const first: SecurityEvent = buildEvent({
|
|
325
|
+
id: "11111111-1111-4111-8111-111111111111",
|
|
326
|
+
});
|
|
327
|
+
const second: SecurityEvent = buildEvent({
|
|
328
|
+
id: "11111111-1111-4111-8111-111111111111",
|
|
329
|
+
});
|
|
330
|
+
expect(dedupeSecurityEvents([[first], [second]])).toHaveLength(1);
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
test("falls back to eventUid + time when _id was not selected", () => {
|
|
334
|
+
const time: Date = new Date("2026-08-25T10:00:00.000Z");
|
|
335
|
+
const first: SecurityEvent = buildEvent({ eventUid: "uid-1", time });
|
|
336
|
+
const duplicate: SecurityEvent = buildEvent({ eventUid: "uid-1", time });
|
|
337
|
+
const differentTime: SecurityEvent = buildEvent({
|
|
338
|
+
eventUid: "uid-1",
|
|
339
|
+
time: new Date("2026-08-25T11:00:00.000Z"),
|
|
340
|
+
});
|
|
341
|
+
|
|
342
|
+
expect(
|
|
343
|
+
dedupeSecurityEvents([[first], [duplicate], [differentTime]]),
|
|
344
|
+
).toHaveLength(2);
|
|
345
|
+
});
|
|
346
|
+
});
|
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { render, screen } from "@testing-library/react";
|
|
2
|
+
import userEvent from "@testing-library/user-event";
|
|
3
|
+
import * as React from "react";
|
|
4
|
+
import { describe, expect, test } from "@jest/globals";
|
|
5
|
+
import CriteriaFilterElement from "../../../../App/FeatureSet/Dashboard/src/Components/Form/Monitor/CriteriaFilter";
|
|
6
|
+
import CriteriaFiltersElement from "../../../../App/FeatureSet/Dashboard/src/Components/Form/Monitor/CriteriaFilters";
|
|
7
|
+
import {
|
|
8
|
+
CheckOn,
|
|
9
|
+
CriteriaFilter,
|
|
10
|
+
FilterType,
|
|
11
|
+
} from "../../../Types/Monitor/CriteriaFilter";
|
|
12
|
+
import MonitorCriteriaInstance from "../../../Types/Monitor/MonitorCriteriaInstance";
|
|
13
|
+
import MonitorStep from "../../../Types/Monitor/MonitorStep";
|
|
14
|
+
import MonitorType from "../../../Types/Monitor/MonitorType";
|
|
15
|
+
import ObjectID from "../../../Types/ObjectID";
|
|
16
|
+
import getJestMockFunction, { MockFunction } from "../../MockType";
|
|
17
|
+
|
|
18
|
+
/*
|
|
19
|
+
* #3412 as the user met it: the "Filter Condition" dropdown on the
|
|
20
|
+
* "External Status Page Active Incidents" filter showed an empty "Select...",
|
|
21
|
+
* while the sibling filter's condition read "True" and this filter's own
|
|
22
|
+
* value box already showed 0.
|
|
23
|
+
*
|
|
24
|
+
* The option-list tests in ExternalStatusPageCriteriaFilter.test.ts and
|
|
25
|
+
* CriteriaFilterDefaults.test.ts pin the data the dropdown is built from.
|
|
26
|
+
* This file drives the real component instead, because "Select..." is
|
|
27
|
+
* something react-select decides to render when it cannot find an option
|
|
28
|
+
* matching the value it was handed — a rule about lists cannot say whether
|
|
29
|
+
* that happens on screen, only the rendered row can.
|
|
30
|
+
*
|
|
31
|
+
* Assertions stay on plain DOM text: jest-dom's matchers do not typecheck
|
|
32
|
+
* repo-wide.
|
|
33
|
+
*/
|
|
34
|
+
|
|
35
|
+
// react-select's placeholder, i.e. what "unset" looks like to the user.
|
|
36
|
+
const UNSET: string = "Select...";
|
|
37
|
+
|
|
38
|
+
function monitorStep(): MonitorStep {
|
|
39
|
+
return new MonitorStep();
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function seededExternalStatusPageFilters(): Array<CriteriaFilter> {
|
|
43
|
+
return MonitorCriteriaInstance.getDefaultOnlineMonitorCriteriaInstance({
|
|
44
|
+
monitorType: MonitorType.ExternalStatusPage,
|
|
45
|
+
monitorStatusId: new ObjectID("aaaaaaaaaaaaaaaaaaaaaaaa"),
|
|
46
|
+
monitorName: "Acme Status",
|
|
47
|
+
})!.data!.filters;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function renderFilter(
|
|
51
|
+
criteriaFilter: CriteriaFilter,
|
|
52
|
+
monitorType: MonitorType,
|
|
53
|
+
): {
|
|
54
|
+
onChange: MockFunction;
|
|
55
|
+
} {
|
|
56
|
+
const onChange: MockFunction = getJestMockFunction();
|
|
57
|
+
|
|
58
|
+
render(
|
|
59
|
+
<CriteriaFilterElement
|
|
60
|
+
monitorType={monitorType}
|
|
61
|
+
monitorStep={monitorStep()}
|
|
62
|
+
value={criteriaFilter}
|
|
63
|
+
onChange={onChange as unknown as (value: CriteriaFilter) => void}
|
|
64
|
+
/>,
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
return { onChange: onChange };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/*
|
|
71
|
+
* The last filter each onChange handed back. The component replaces the whole
|
|
72
|
+
* filter object on every edit, so the newest call is the current state.
|
|
73
|
+
*/
|
|
74
|
+
function lastFilterFrom(onChange: MockFunction): CriteriaFilter {
|
|
75
|
+
expect(onChange.mock.calls.length).toBeGreaterThan(0);
|
|
76
|
+
|
|
77
|
+
return onChange.mock.calls[
|
|
78
|
+
onChange.mock.calls.length - 1
|
|
79
|
+
]![0] as CriteriaFilter;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// The filter list the last onChange handed back, after "Add Filter" ran.
|
|
83
|
+
function lastFilterListFrom(onChange: MockFunction): Array<CriteriaFilter> {
|
|
84
|
+
expect(onChange.mock.calls.length).toBeGreaterThan(0);
|
|
85
|
+
|
|
86
|
+
return onChange.mock.calls[
|
|
87
|
+
onChange.mock.calls.length - 1
|
|
88
|
+
]![0] as Array<CriteriaFilter>;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
async function pickFromDropdown(input: {
|
|
92
|
+
labelledBy: string;
|
|
93
|
+
optionLabel: string;
|
|
94
|
+
}): Promise<void> {
|
|
95
|
+
const user: ReturnType<typeof userEvent.setup> = userEvent.setup();
|
|
96
|
+
|
|
97
|
+
/*
|
|
98
|
+
* react-select renders its own text input per dropdown. Ordering follows
|
|
99
|
+
* the form: "Filter Type" first, then "Filter Condition".
|
|
100
|
+
*/
|
|
101
|
+
const comboboxes: Array<HTMLElement> = screen.getAllByRole("combobox");
|
|
102
|
+
const index: number = input.labelledBy === "Filter Type" ? 0 : 1;
|
|
103
|
+
|
|
104
|
+
await user.click(comboboxes[index]!);
|
|
105
|
+
await user.click(await screen.findByText(input.optionLabel));
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
describe("The criteria Filter Condition dropdown", () => {
|
|
109
|
+
describe("the row reported in #3412", () => {
|
|
110
|
+
test("the seeded active-incidents filter shows its condition, not an empty Select...", () => {
|
|
111
|
+
const criteriaFilter: CriteriaFilter =
|
|
112
|
+
seededExternalStatusPageFilters()[1]!;
|
|
113
|
+
|
|
114
|
+
expect(criteriaFilter.checkOn).toBe(
|
|
115
|
+
CheckOn.ExternalStatusPageActiveIncidents,
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
renderFilter(criteriaFilter, MonitorType.ExternalStatusPage);
|
|
119
|
+
|
|
120
|
+
// The Filter Type dropdown, which was never the problem.
|
|
121
|
+
expect(
|
|
122
|
+
screen.getAllByText(CheckOn.ExternalStatusPageActiveIncidents).length,
|
|
123
|
+
).toBeGreaterThan(0);
|
|
124
|
+
|
|
125
|
+
// The Filter Condition dropdown. This is what showed "Select...".
|
|
126
|
+
expect(screen.getAllByText(FilterType.EqualTo).length).toBeGreaterThan(0);
|
|
127
|
+
expect(screen.queryByText(UNSET)).toBeNull();
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
test("the sibling is-online filter still shows True", () => {
|
|
131
|
+
const criteriaFilter: CriteriaFilter =
|
|
132
|
+
seededExternalStatusPageFilters()[0]!;
|
|
133
|
+
|
|
134
|
+
expect(criteriaFilter.checkOn).toBe(CheckOn.ExternalStatusPageIsOnline);
|
|
135
|
+
|
|
136
|
+
renderFilter(criteriaFilter, MonitorType.ExternalStatusPage);
|
|
137
|
+
|
|
138
|
+
expect(screen.getAllByText(FilterType.True).length).toBeGreaterThan(0);
|
|
139
|
+
expect(screen.queryByText(UNSET)).toBeNull();
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
test("both seeded filters render with nothing left unset", () => {
|
|
143
|
+
for (const criteriaFilter of seededExternalStatusPageFilters()) {
|
|
144
|
+
const { unmount }: { unmount: () => void } = render(
|
|
145
|
+
<CriteriaFilterElement
|
|
146
|
+
monitorType={MonitorType.ExternalStatusPage}
|
|
147
|
+
monitorStep={monitorStep()}
|
|
148
|
+
value={criteriaFilter}
|
|
149
|
+
onChange={() => {}}
|
|
150
|
+
/>,
|
|
151
|
+
);
|
|
152
|
+
|
|
153
|
+
expect(screen.queryByText(UNSET)).toBeNull();
|
|
154
|
+
unmount();
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe("changing the Filter Type", () => {
|
|
160
|
+
test("moves the condition to the new check's default instead of clearing it", async () => {
|
|
161
|
+
const { onChange }: { onChange: MockFunction } = renderFilter(
|
|
162
|
+
seededExternalStatusPageFilters()[0]!,
|
|
163
|
+
MonitorType.ExternalStatusPage,
|
|
164
|
+
);
|
|
165
|
+
|
|
166
|
+
await pickFromDropdown({
|
|
167
|
+
labelledBy: "Filter Type",
|
|
168
|
+
optionLabel: CheckOn.ExternalStatusPageActiveIncidents,
|
|
169
|
+
});
|
|
170
|
+
|
|
171
|
+
const emitted: CriteriaFilter = lastFilterFrom(onChange);
|
|
172
|
+
|
|
173
|
+
expect(emitted.checkOn).toBe(CheckOn.ExternalStatusPageActiveIncidents);
|
|
174
|
+
// Was undefined, which is what left the next dropdown blank.
|
|
175
|
+
expect(emitted.filterType).toBe(FilterType.EqualTo);
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
test("a check with exactly one condition no longer asks the user to re-pick it", async () => {
|
|
179
|
+
const { onChange }: { onChange: MockFunction } = renderFilter(
|
|
180
|
+
{
|
|
181
|
+
checkOn: CheckOn.ResponseBody,
|
|
182
|
+
filterType: FilterType.Contains,
|
|
183
|
+
value: "ok",
|
|
184
|
+
},
|
|
185
|
+
MonitorType.API,
|
|
186
|
+
);
|
|
187
|
+
|
|
188
|
+
await pickFromDropdown({
|
|
189
|
+
labelledBy: "Filter Type",
|
|
190
|
+
optionLabel: CheckOn.JavaScriptExpression,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
const emitted: CriteriaFilter = lastFilterFrom(onChange);
|
|
194
|
+
|
|
195
|
+
expect(emitted.checkOn).toBe(CheckOn.JavaScriptExpression);
|
|
196
|
+
expect(emitted.filterType).toBe(FilterType.EvaluatesToTrue);
|
|
197
|
+
});
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
describe("adding a filter", () => {
|
|
201
|
+
test("an External Status Page monitor gets a filter it can actually render", () => {
|
|
202
|
+
const onChange: MockFunction = getJestMockFunction();
|
|
203
|
+
|
|
204
|
+
render(
|
|
205
|
+
<CriteriaFiltersElement
|
|
206
|
+
monitorType={MonitorType.ExternalStatusPage}
|
|
207
|
+
monitorStep={monitorStep()}
|
|
208
|
+
value={[seededExternalStatusPageFilters()[0]!]}
|
|
209
|
+
onChange={
|
|
210
|
+
onChange as unknown as (value: Array<CriteriaFilter>) => void
|
|
211
|
+
}
|
|
212
|
+
/>,
|
|
213
|
+
);
|
|
214
|
+
|
|
215
|
+
screen.getByText("Add Filter").click();
|
|
216
|
+
|
|
217
|
+
const added: CriteriaFilter = lastFilterListFrom(onChange)[1]!;
|
|
218
|
+
|
|
219
|
+
/*
|
|
220
|
+
* Was { checkOn: IsOnline, filterType: EqualTo } — a check this monitor
|
|
221
|
+
* type does not offer, paired with a condition that check has never
|
|
222
|
+
* accepted, so both dropdowns opened blank.
|
|
223
|
+
*/
|
|
224
|
+
expect(added.checkOn).toBe(CheckOn.ExternalStatusPageIsOnline);
|
|
225
|
+
expect(added.filterType).toBe(FilterType.True);
|
|
226
|
+
});
|
|
227
|
+
|
|
228
|
+
test("a website monitor keeps its up/down check and gains a condition", () => {
|
|
229
|
+
const onChange: MockFunction = getJestMockFunction();
|
|
230
|
+
|
|
231
|
+
render(
|
|
232
|
+
<CriteriaFiltersElement
|
|
233
|
+
monitorType={MonitorType.Website}
|
|
234
|
+
monitorStep={monitorStep()}
|
|
235
|
+
value={[
|
|
236
|
+
{
|
|
237
|
+
checkOn: CheckOn.IsOnline,
|
|
238
|
+
filterType: FilterType.True,
|
|
239
|
+
value: undefined,
|
|
240
|
+
},
|
|
241
|
+
]}
|
|
242
|
+
onChange={
|
|
243
|
+
onChange as unknown as (value: Array<CriteriaFilter>) => void
|
|
244
|
+
}
|
|
245
|
+
/>,
|
|
246
|
+
);
|
|
247
|
+
|
|
248
|
+
screen.getByText("Add Filter").click();
|
|
249
|
+
|
|
250
|
+
const added: CriteriaFilter = lastFilterListFrom(onChange)[1]!;
|
|
251
|
+
|
|
252
|
+
expect(added.checkOn).toBe(CheckOn.IsOnline);
|
|
253
|
+
expect(added.filterType).toBe(FilterType.True);
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
});
|