@oneuptime/common 11.7.2 → 11.7.4
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/AnalyticsModels/KubernetesCostAllocation.ts +103 -0
- package/Models/DatabaseModels/AlertEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/AlertStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentEpisodeStateTimeline.ts +1 -0
- package/Models/DatabaseModels/IncidentStateTimeline.ts +1 -0
- package/Models/DatabaseModels/Index.ts +5 -0
- package/Models/DatabaseModels/LogDropFilter.ts +89 -0
- package/Models/DatabaseModels/NetworkDeviceDiscoveryScan.ts +13 -3
- package/Models/DatabaseModels/RecommendationDismissal.ts +463 -0
- package/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.ts +1 -0
- package/Models/DatabaseModels/StatusPageGroup.ts +95 -0
- package/Models/DatabaseModels/TableView.ts +40 -0
- package/Models/DatabaseModels/TraceDropFilter.ts +89 -0
- package/Models/DatabaseModels/WorkflowVariable.ts +5 -1
- package/Server/API/AlertAPI.ts +32 -17
- package/Server/API/BaseAPI.ts +31 -2
- package/Server/API/BaseAnalyticsAPI.ts +36 -2
- package/Server/API/CommonAPI.ts +57 -0
- package/Server/API/IncidentAPI.ts +60 -33
- package/Server/API/IncidentEpisodeAPI.ts +31 -16
- package/Server/API/MonitorAPI.ts +30 -0
- package/Server/API/MonitorTemplateAPI.ts +8 -0
- package/Server/API/ProbeAPI.ts +196 -15
- package/Server/API/ProjectAPI.ts +79 -1
- package/Server/API/ScheduledMaintenanceAPI.ts +33 -18
- package/Server/API/StatusPageAPI.ts +74 -37
- package/Server/API/WorkspaceNotificationRuleAPI.ts +34 -4
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.ts +13 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.ts +527 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.ts +31 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.ts +55 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.ts +53 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +10 -0
- package/Server/Services/AlertEpisodeService.ts +47 -0
- package/Server/Services/AlertService.ts +125 -0
- package/Server/Services/BillingService.ts +98 -0
- package/Server/Services/DatabaseService.ts +184 -20
- package/Server/Services/IncidentEpisodeService.ts +47 -0
- package/Server/Services/IncidentService.ts +123 -0
- package/Server/Services/IncidentTemplateService.ts +123 -1
- package/Server/Services/Index.ts +2 -0
- package/Server/Services/LogDropFilterService.ts +133 -0
- package/Server/Services/MonitorProbeService.ts +94 -4
- package/Server/Services/MonitorService.ts +48 -43
- package/Server/Services/NetworkDeviceDiscoveryScanService.ts +70 -0
- package/Server/Services/ProbeService.ts +66 -0
- package/Server/Services/ProjectService.ts +131 -7
- package/Server/Services/RecommendationDismissalService.ts +52 -0
- package/Server/Services/RunbookAgentJobService.ts +68 -7
- package/Server/Services/ScheduledMaintenanceService.ts +103 -0
- package/Server/Services/ScheduledMaintenanceTemplateService.ts +90 -0
- package/Server/Services/StatusPageGroupService.ts +186 -0
- package/Server/Services/TraceDropFilterService.ts +133 -0
- package/Server/Types/Workflow/Components/API/Delete.ts +12 -0
- package/Server/Types/Workflow/Components/API/Get.ts +12 -0
- package/Server/Types/Workflow/Components/API/Patch.ts +12 -0
- package/Server/Types/Workflow/Components/API/Post.ts +12 -0
- package/Server/Types/Workflow/Components/API/Put.ts +13 -0
- package/Server/Utils/Database/ProjectScopedReferenceValidator.ts +212 -0
- package/Server/Utils/DropFilterValidation.ts +127 -0
- package/Server/Utils/Errors.ts +5 -0
- package/Server/Utils/Monitor/MonitorAlert.ts +28 -1
- package/Server/Utils/Monitor/MonitorIncident.ts +31 -2
- package/Server/Utils/Telemetry/AppMetrics.ts +31 -0
- package/Tests/Models/AnalyticsModels/KubernetesCostAllocation.test.ts +16 -0
- package/Tests/Models/DatabaseModels/MonitorProbeColumnAccessControl.test.ts +196 -0
- package/Tests/Models/RecommendationDismissalModel.test.ts +269 -0
- package/Tests/Server/API/BaseAPIUpdatePayloadValidation.test.ts +320 -0
- package/Tests/Server/API/BaseAnalyticsAPIUpdatePayloadValidation.test.ts +293 -0
- package/Tests/Server/API/CommonAPIAuthGuard.test.ts +164 -0
- package/Tests/Server/API/ProbeAPI.test.ts +422 -53
- package/Tests/Server/API/ProjectAPI.test.ts +180 -0
- package/Tests/Server/API/ProjectAPIExtendTrialBillingDisabled.test.ts +138 -0
- package/Tests/Server/API/ProjectScopedCustomRouteAuth.test.ts +458 -0
- package/Tests/Server/Infrastructure/ContainerBootConfiguration.test.ts +359 -0
- package/Tests/Server/Infrastructure/ProcessSignalDelivery.test.ts +427 -0
- package/Tests/Server/Services/BillingServiceExtendTrial.test.ts +439 -0
- package/Tests/Server/Services/CrossProjectReferenceWriteGuard.test.ts +810 -0
- package/Tests/Server/Services/DatabaseServiceAtomicAddToColumns.test.ts +269 -0
- package/Tests/Server/Services/DatabaseServiceEmptyUpdate.test.ts +277 -0
- package/Tests/Server/Services/DatabaseServiceSortColumnSelect.test.ts +201 -0
- package/Tests/Server/Services/DiscoveryScanClaimHookFreeSafety.test.ts +81 -23
- package/Tests/Server/Services/DropFilterSaveValidation.test.ts +398 -0
- package/Tests/Server/Services/MonitorProbeDeleteHooks.test.ts +280 -0
- package/Tests/Server/Services/MonitorProbeServiceProbeTenancy.test.ts +304 -0
- package/Tests/Server/Services/NetworkDeviceDiscoveryScanService.test.ts +262 -0
- package/Tests/Server/Services/ProjectServiceExtendTrial.test.ts +614 -0
- package/Tests/Server/Services/RecommendationDismissalService.test.ts +306 -0
- package/Tests/Server/Services/RepairCrossProjectIncidentReferencesMigration.test.ts +528 -0
- package/Tests/Server/Services/StatusPageGroupNesting.test.ts +395 -0
- package/Tests/Server/TestingUtils/Services/BillingServiceHelper.ts +26 -21
- package/Tests/Server/Types/Database/Permissions/WorkflowVariableColumnPermission.test.ts +227 -0
- package/Tests/Server/Types/Workflow/Components/ApiComponentErrorPort.test.ts +285 -0
- package/Tests/Server/Utils/Database/ProjectScopedReferenceValidator.test.ts +463 -0
- package/Tests/Server/Utils/DropFilterValidation.test.ts +269 -0
- package/Tests/Server/Utils/Telemetry/SpanUtil.test.ts +94 -0
- package/Tests/Types/DateUserTimezone.test.ts +52 -0
- package/Tests/Types/JSONFunctions.test.ts +103 -1
- package/Tests/Types/Monitor/KubernetesAlertTemplates.test.ts +177 -9
- package/Tests/Types/Monitor/MonitorStepDnsMonitor.test.ts +105 -0
- package/Tests/Types/Monitor/MonitorStepDnssecMonitor.test.ts +131 -0
- package/Tests/Types/Monitor/MonitorStepDomainMonitor.test.ts +60 -0
- package/Tests/Types/Monitor/MonitorStepExternalStatusPageMonitor.test.ts +82 -0
- package/Tests/Types/Monitor/MonitorStepInfrastructureMonitors.test.ts +268 -0
- package/Tests/Types/Monitor/MonitorStepMetricMonitor.test.ts +386 -0
- package/Tests/Types/Monitor/MonitorStepProfileMonitor.test.ts +185 -0
- package/Tests/Types/Monitor/MonitorStepSnmpMonitor.test.ts +156 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCatalog.test.ts +371 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationCoverage.test.ts +901 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationNotificationMode.test.ts +607 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.test.ts +754 -0
- package/Tests/Types/Monitor/Recommendation/MonitorRecommendationUtil.test.ts +960 -0
- package/Tests/Types/RollingTime/RollingTimeUtil.test.ts +186 -0
- package/Tests/Types/Runbook/RunbookStepTimeout.test.ts +410 -0
- package/Tests/Types/Telemetry/DropFilterSampling.test.ts +241 -0
- package/Tests/Types/Workspace/NotificationRuleConditionUtil.test.ts +353 -0
- package/Tests/Types/Workspace/NotificationRuleUtil.test.ts +566 -0
- package/Tests/UI/Components/CodeEditor.test.tsx +174 -0
- package/Tests/UI/Components/ErrorBoundary.test.tsx +555 -24
- package/Tests/UI/Components/InfoCard.test.tsx +358 -0
- package/Tests/UI/Components/LogTimeRangePicker.test.tsx +138 -0
- package/Tests/UI/Components/Modal.test.tsx +43 -5
- package/Tests/UI/Components/ModelTable/BaseModelTableColumnCustomization.test.tsx +679 -0
- package/Tests/UI/Components/ModelTable/BaseModelTableSearchLabels.test.tsx +397 -0
- package/Tests/UI/Components/ModelTable/ColumnCustomizationModal.test.tsx +999 -0
- package/Tests/UI/Components/ModelTable/ColumnPreference.test.ts +1678 -0
- package/Tests/UI/Components/ModelTable/CustomFieldColumns.test.tsx +1094 -0
- package/Tests/UI/Components/Runbook/StepTimeoutInput.test.tsx +386 -0
- package/Tests/UI/Components/StatusPage/ResourceGroupSection.test.tsx +671 -0
- package/Tests/UI/Components/TelemetryTimeRangePicker.test.tsx +404 -0
- package/Tests/UI/Utils/DropdownAlignment.test.ts +306 -0
- package/Tests/UI/Utils/TableFilterUrlState.test.ts +292 -0
- package/Tests/Utils/Monitor/MonitorSummaryProbeUtil.test.ts +263 -0
- package/Tests/Utils/NetworkDiscovery/ScanTargetUtil.test.ts +583 -0
- package/Tests/Utils/StatusPage/GroupNestingLayout.test.ts +1072 -0
- package/Tests/Utils/StatusPage/GroupTree.test.ts +583 -0
- package/Tests/Utils/StatusPage/ResourceUptime.test.ts +411 -0
- package/Tests/Utils/UserPreferences.test.ts +563 -0
- package/Types/Date.ts +15 -6
- package/Types/JSONFunctions.ts +7 -2
- package/Types/Kubernetes/KubernetesCostIngest.ts +37 -1
- package/Types/Monitor/KubernetesAlertTemplates.ts +205 -0
- package/Types/Monitor/MonitorStep.ts +10 -1
- package/Types/Monitor/MonitorStepMetricMonitor.ts +46 -1
- package/Types/Monitor/Recommendation/MonitorRecommendationCatalog.ts +413 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.ts +168 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationTypes.ts +175 -0
- package/Types/Monitor/Recommendation/MonitorRecommendationUtil.ts +743 -0
- package/Types/Permission.ts +45 -0
- package/Types/Recommendation/RecommendationType.ts +20 -0
- package/Types/Runbook/RunbookExecutionDeadline.ts +129 -0
- package/Types/Runbook/RunbookStepTimeout.ts +202 -0
- package/Types/Telemetry/DropFilterSampling.ts +92 -0
- package/UI/Components/AI/GenerateFromAIModal.tsx +0 -2
- package/UI/Components/Accordion/Accordion.tsx +10 -2
- package/UI/Components/BulkUpdate/BulkLabelActions.tsx +0 -1
- package/UI/Components/CodeEditor/CodeEditor.tsx +34 -20
- package/UI/Components/Date/RangeStartAndEndDateView.tsx +0 -1
- package/UI/Components/EditionLabel/EditionLabel.tsx +1 -27
- package/UI/Components/Error/PageError.tsx +0 -4
- package/UI/Components/ErrorBoundary.tsx +252 -15
- package/UI/Components/InfoCard/InfoCard.tsx +58 -1
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +34 -1
- package/UI/Components/MasterPage/MasterPage.tsx +65 -0
- package/UI/Components/Modal/Modal.tsx +16 -60
- package/UI/Components/ModelFormModal/ModelFormModal.tsx +0 -8
- package/UI/Components/ModelTable/BaseModelTable.tsx +503 -71
- package/UI/Components/ModelTable/Column.ts +17 -0
- package/UI/Components/ModelTable/ColumnCustomizationModal.tsx +523 -0
- package/UI/Components/ModelTable/ColumnPreference.ts +482 -0
- package/UI/Components/ModelTable/CustomFieldColumns.tsx +326 -0
- package/UI/Components/ModelTable/TableView.tsx +24 -2
- package/UI/Components/ModelTable/useCustomFieldColumns.ts +150 -0
- package/UI/Components/Runbook/StepTimeoutInput.tsx +173 -0
- package/UI/Components/SideMenu/SideMenu.tsx +24 -4
- package/UI/Components/StatusPage/ResourceGroupSection.tsx +231 -0
- package/UI/Components/Table/Table.tsx +14 -1
- package/UI/Components/Table/TableRow.tsx +180 -175
- package/UI/Components/Table/Types/Column.ts +2 -0
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +34 -1
- package/UI/Utils/DropdownAlignment.ts +162 -0
- package/UI/Utils/TableFilterUrlState.ts +59 -20
- package/Utils/API.ts +106 -2
- package/Utils/Monitor/MonitorSummaryProbeUtil.ts +130 -0
- package/Utils/NetworkDiscovery/ScanTargetUtil.ts +434 -0
- package/Utils/StatusPage/GroupNestingLayout.ts +343 -0
- package/Utils/StatusPage/GroupTree.ts +307 -0
- package/Utils/StatusPage/ResourceUptime.ts +90 -17
- package/Utils/UserPreferences.ts +53 -0
- package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js +86 -0
- package/build/dist/Models/AnalyticsModels/KubernetesCostAllocation.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/AlertEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/AlertStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentEpisodeStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/IncidentStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/LogDropFilter.js +91 -0
- package/build/dist/Models/DatabaseModels/LogDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js +14 -4
- package/build/dist/Models/DatabaseModels/NetworkDeviceDiscoveryScan.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RecommendationDismissal.js +484 -0
- package/build/dist/Models/DatabaseModels/RecommendationDismissal.js.map +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js +1 -0
- package/build/dist/Models/DatabaseModels/ScheduledMaintenanceStateTimeline.js.map +1 -1
- package/build/dist/Models/DatabaseModels/StatusPageGroup.js +94 -0
- package/build/dist/Models/DatabaseModels/StatusPageGroup.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TableView.js +40 -0
- package/build/dist/Models/DatabaseModels/TableView.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js +91 -0
- package/build/dist/Models/DatabaseModels/TraceDropFilter.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js +5 -1
- package/build/dist/Models/DatabaseModels/WorkflowVariable.js.map +1 -1
- package/build/dist/Server/API/AlertAPI.js +22 -13
- package/build/dist/Server/API/AlertAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAPI.js +22 -1
- package/build/dist/Server/API/BaseAPI.js.map +1 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js +24 -1
- package/build/dist/Server/API/BaseAnalyticsAPI.js.map +1 -1
- package/build/dist/Server/API/CommonAPI.js +42 -0
- package/build/dist/Server/API/CommonAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentAPI.js +43 -26
- package/build/dist/Server/API/IncidentAPI.js.map +1 -1
- package/build/dist/Server/API/IncidentEpisodeAPI.js +21 -12
- package/build/dist/Server/API/IncidentEpisodeAPI.js.map +1 -1
- package/build/dist/Server/API/MonitorAPI.js +23 -0
- package/build/dist/Server/API/MonitorAPI.js.map +1 -1
- package/build/dist/Server/API/MonitorTemplateAPI.js +4 -0
- package/build/dist/Server/API/MonitorTemplateAPI.js.map +1 -1
- package/build/dist/Server/API/ProbeAPI.js +131 -15
- package/build/dist/Server/API/ProbeAPI.js.map +1 -1
- package/build/dist/Server/API/ProjectAPI.js +62 -3
- package/build/dist/Server/API/ProjectAPI.js.map +1 -1
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js +23 -14
- package/build/dist/Server/API/ScheduledMaintenanceAPI.js.map +1 -1
- package/build/dist/Server/API/StatusPageAPI.js +61 -33
- package/build/dist/Server/API/StatusPageAPI.js.map +1 -1
- package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js +26 -2
- package/build/dist/Server/API/WorkspaceNotificationRuleAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js +12 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785241000000-AddColumnsToTableView.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js +397 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785320000000-RepairCrossProjectIncidentReferences.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js +16 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785329453269-AddParentGroupToStatusPageGroup.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js +30 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785397674289-AddRecommendationDismissalTable.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js +34 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1785405581596-AddDroppedCountersToDropFilters.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +10 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Services/AlertEpisodeService.js +39 -0
- package/build/dist/Server/Services/AlertEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +95 -0
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/BillingService.js +71 -0
- package/build/dist/Server/Services/BillingService.js.map +1 -1
- package/build/dist/Server/Services/DatabaseService.js +140 -17
- package/build/dist/Server/Services/DatabaseService.js.map +1 -1
- package/build/dist/Server/Services/IncidentEpisodeService.js +39 -0
- package/build/dist/Server/Services/IncidentEpisodeService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +100 -0
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentTemplateService.js +97 -0
- package/build/dist/Server/Services/IncidentTemplateService.js.map +1 -1
- package/build/dist/Server/Services/Index.js +2 -0
- package/build/dist/Server/Services/Index.js.map +1 -1
- package/build/dist/Server/Services/LogDropFilterService.js +93 -0
- package/build/dist/Server/Services/LogDropFilterService.js.map +1 -1
- package/build/dist/Server/Services/MonitorProbeService.js +73 -5
- package/build/dist/Server/Services/MonitorProbeService.js.map +1 -1
- package/build/dist/Server/Services/MonitorService.js +34 -39
- package/build/dist/Server/Services/MonitorService.js.map +1 -1
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js +71 -0
- package/build/dist/Server/Services/NetworkDeviceDiscoveryScanService.js.map +1 -1
- package/build/dist/Server/Services/ProbeService.js +64 -0
- package/build/dist/Server/Services/ProbeService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +103 -7
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/RecommendationDismissalService.js +50 -0
- package/build/dist/Server/Services/RecommendationDismissalService.js.map +1 -0
- package/build/dist/Server/Services/RunbookAgentJobService.js +63 -5
- package/build/dist/Server/Services/RunbookAgentJobService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +83 -0
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js +65 -0
- package/build/dist/Server/Services/ScheduledMaintenanceTemplateService.js.map +1 -1
- package/build/dist/Server/Services/StatusPageGroupService.js +136 -0
- package/build/dist/Server/Services/StatusPageGroupService.js.map +1 -1
- package/build/dist/Server/Services/TraceDropFilterService.js +93 -0
- package/build/dist/Server/Services/TraceDropFilterService.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Delete.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Get.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Get.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Patch.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Post.js +11 -0
- package/build/dist/Server/Types/Workflow/Components/API/Post.js.map +1 -1
- package/build/dist/Server/Types/Workflow/Components/API/Put.js +12 -0
- package/build/dist/Server/Types/Workflow/Components/API/Put.js.map +1 -1
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js +132 -0
- package/build/dist/Server/Utils/Database/ProjectScopedReferenceValidator.js.map +1 -0
- package/build/dist/Server/Utils/DropFilterValidation.js +80 -0
- package/build/dist/Server/Utils/DropFilterValidation.js.map +1 -0
- package/build/dist/Server/Utils/Errors.js +3 -0
- package/build/dist/Server/Utils/Errors.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js +30 -12
- package/build/dist/Server/Utils/Monitor/MonitorAlert.js.map +1 -1
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js +38 -18
- package/build/dist/Server/Utils/Monitor/MonitorIncident.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js +26 -0
- package/build/dist/Server/Utils/Telemetry/AppMetrics.js.map +1 -1
- package/build/dist/Types/Date.js +14 -4
- package/build/dist/Types/Date.js.map +1 -1
- package/build/dist/Types/JSONFunctions.js +8 -3
- package/build/dist/Types/JSONFunctions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesCostIngest.js.map +1 -1
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js +192 -0
- package/build/dist/Types/Monitor/KubernetesAlertTemplates.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStep.js +6 -1
- package/build/dist/Types/Monitor/MonitorStep.js.map +1 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js +32 -1
- package/build/dist/Types/Monitor/MonitorStepMetricMonitor.js.map +1 -1
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js +294 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationCatalog.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js +110 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationSeverityMapper.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js +58 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationTypes.js.map +1 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js +451 -0
- package/build/dist/Types/Monitor/Recommendation/MonitorRecommendationUtil.js.map +1 -0
- package/build/dist/Types/Permission.js +40 -0
- package/build/dist/Types/Permission.js.map +1 -1
- package/build/dist/Types/Recommendation/RecommendationType.js +21 -0
- package/build/dist/Types/Recommendation/RecommendationType.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookExecutionDeadline.js +93 -0
- package/build/dist/Types/Runbook/RunbookExecutionDeadline.js.map +1 -0
- package/build/dist/Types/Runbook/RunbookStepTimeout.js +142 -0
- package/build/dist/Types/Runbook/RunbookStepTimeout.js.map +1 -0
- package/build/dist/Types/Telemetry/DropFilterSampling.js +75 -0
- package/build/dist/Types/Telemetry/DropFilterSampling.js.map +1 -0
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js +1 -2
- package/build/dist/UI/Components/AI/GenerateFromAIModal.js.map +1 -1
- package/build/dist/UI/Components/Accordion/Accordion.js +9 -2
- package/build/dist/UI/Components/Accordion/Accordion.js.map +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js +1 -1
- package/build/dist/UI/Components/BulkUpdate/BulkLabelActions.js.map +1 -1
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js +27 -14
- package/build/dist/UI/Components/CodeEditor/CodeEditor.js.map +1 -1
- package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js +1 -1
- package/build/dist/UI/Components/Date/RangeStartAndEndDateView.js.map +1 -1
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js +2 -20
- package/build/dist/UI/Components/EditionLabel/EditionLabel.js.map +1 -1
- package/build/dist/UI/Components/Error/PageError.js +1 -3
- package/build/dist/UI/Components/Error/PageError.js.map +1 -1
- package/build/dist/UI/Components/ErrorBoundary.js +145 -11
- package/build/dist/UI/Components/ErrorBoundary.js.map +1 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js +28 -1
- package/build/dist/UI/Components/InfoCard/InfoCard.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +13 -2
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js +50 -1
- package/build/dist/UI/Components/MasterPage/MasterPage.js.map +1 -1
- package/build/dist/UI/Components/Modal/Modal.js +4 -32
- package/build/dist/UI/Components/Modal/Modal.js.map +1 -1
- package/build/dist/UI/Components/ModelFormModal/ModelFormModal.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +308 -40
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js +230 -0
- package/build/dist/UI/Components/ModelTable/ColumnCustomizationModal.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js +258 -0
- package/build/dist/UI/Components/ModelTable/ColumnPreference.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js +168 -0
- package/build/dist/UI/Components/ModelTable/CustomFieldColumns.js.map +1 -0
- package/build/dist/UI/Components/ModelTable/TableView.js +13 -2
- package/build/dist/UI/Components/ModelTable/TableView.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js +84 -0
- package/build/dist/UI/Components/ModelTable/useCustomFieldColumns.js.map +1 -0
- package/build/dist/UI/Components/Runbook/StepTimeoutInput.js +101 -0
- package/build/dist/UI/Components/Runbook/StepTimeoutInput.js.map +1 -0
- package/build/dist/UI/Components/SideMenu/SideMenu.js +15 -5
- package/build/dist/UI/Components/SideMenu/SideMenu.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js +73 -0
- package/build/dist/UI/Components/StatusPage/ResourceGroupSection.js.map +1 -0
- package/build/dist/UI/Components/Table/Table.js +15 -2
- package/build/dist/UI/Components/Table/Table.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +11 -6
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +13 -2
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Utils/DropdownAlignment.js +98 -0
- package/build/dist/UI/Utils/DropdownAlignment.js.map +1 -0
- package/build/dist/UI/Utils/TableFilterUrlState.js +48 -15
- package/build/dist/UI/Utils/TableFilterUrlState.js.map +1 -1
- package/build/dist/Utils/API.js +39 -2
- package/build/dist/Utils/API.js.map +1 -1
- package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js +97 -0
- package/build/dist/Utils/Monitor/MonitorSummaryProbeUtil.js.map +1 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js +330 -0
- package/build/dist/Utils/NetworkDiscovery/ScanTargetUtil.js.map +1 -0
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js +280 -0
- package/build/dist/Utils/StatusPage/GroupNestingLayout.js.map +1 -0
- package/build/dist/Utils/StatusPage/GroupTree.js +214 -0
- package/build/dist/Utils/StatusPage/GroupTree.js.map +1 -0
- package/build/dist/Utils/StatusPage/ResourceUptime.js +60 -16
- package/build/dist/Utils/StatusPage/ResourceUptime.js.map +1 -1
- package/build/dist/Utils/UserPreferences.js +33 -0
- package/build/dist/Utils/UserPreferences.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,960 @@
|
|
|
1
|
+
import MonitorRecommendationCatalog, {
|
|
2
|
+
MonitorRecommendationResourceTypeDefinition,
|
|
3
|
+
} from "../../../../Types/Monitor/Recommendation/MonitorRecommendationCatalog";
|
|
4
|
+
import MonitorRecommendationUtil, {
|
|
5
|
+
MonitorRecommendationFingerprint,
|
|
6
|
+
} from "../../../../Types/Monitor/Recommendation/MonitorRecommendationUtil";
|
|
7
|
+
import {
|
|
8
|
+
MonitorRecommendation,
|
|
9
|
+
MonitorRecommendationArgs,
|
|
10
|
+
MonitorRecommendationNotificationSettings,
|
|
11
|
+
MonitorRecommendationResourceType,
|
|
12
|
+
MonitorRecommendationSeverity,
|
|
13
|
+
} from "../../../../Types/Monitor/Recommendation/MonitorRecommendationTypes";
|
|
14
|
+
import MonitorStep from "../../../../Types/Monitor/MonitorStep";
|
|
15
|
+
import MonitorSteps from "../../../../Types/Monitor/MonitorSteps";
|
|
16
|
+
import MonitorCriteriaInstance from "../../../../Types/Monitor/MonitorCriteriaInstance";
|
|
17
|
+
import { CriteriaIncident } from "../../../../Types/Monitor/CriteriaIncident";
|
|
18
|
+
import { CriteriaAlert } from "../../../../Types/Monitor/CriteriaAlert";
|
|
19
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
20
|
+
|
|
21
|
+
/*
|
|
22
|
+
* These tests cover the three things that stand between "the user clicked
|
|
23
|
+
* Create" and "an alert reaches a human":
|
|
24
|
+
*
|
|
25
|
+
* 1. The resource identifier survives the args rename. Each template module
|
|
26
|
+
* names it differently (clusterIdentifier / hostIdentifier /
|
|
27
|
+
* fleetIdentifier) and the catalog adapters rename it. A wrong rename
|
|
28
|
+
* compiles, renders, and produces a monitor scoped to "" that matches no
|
|
29
|
+
* telemetry and never fires. Nothing else would catch it.
|
|
30
|
+
*
|
|
31
|
+
* 2. On-call policies actually land on the generated criteria. Every shipped
|
|
32
|
+
* template hardcodes `onCallPolicyIds: []`; the `no template ships an
|
|
33
|
+
* on-call policy` test below pins that fact, and the rest prove the
|
|
34
|
+
* create form's selection overrides it. Without this, a "Critical"
|
|
35
|
+
* recommendation creates incidents that page nobody — which is the exact
|
|
36
|
+
* failure this whole feature exists to fix.
|
|
37
|
+
*
|
|
38
|
+
* 3. The already-created diff is computed from what a monitor WATCHES, not
|
|
39
|
+
* from its name. Name matching would resurface a recommendation the
|
|
40
|
+
* moment someone renamed a monitor.
|
|
41
|
+
*/
|
|
42
|
+
|
|
43
|
+
const ONLINE_STATUS_ID: ObjectID = ObjectID.generate();
|
|
44
|
+
const OFFLINE_STATUS_ID: ObjectID = ObjectID.generate();
|
|
45
|
+
const DEFAULT_STATUS_ID: ObjectID = ObjectID.generate();
|
|
46
|
+
const INCIDENT_SEVERITY_ID: ObjectID = ObjectID.generate();
|
|
47
|
+
const ALERT_SEVERITY_ID: ObjectID = ObjectID.generate();
|
|
48
|
+
|
|
49
|
+
const RESOURCE_IDENTIFIER: string = "prod-cluster-01";
|
|
50
|
+
|
|
51
|
+
function buildArgs(
|
|
52
|
+
overrides?: Partial<MonitorRecommendationArgs>,
|
|
53
|
+
): MonitorRecommendationArgs {
|
|
54
|
+
return {
|
|
55
|
+
resourceIdentifier: RESOURCE_IDENTIFIER,
|
|
56
|
+
onlineMonitorStatusId: ONLINE_STATUS_ID,
|
|
57
|
+
offlineMonitorStatusId: OFFLINE_STATUS_ID,
|
|
58
|
+
defaultIncidentSeverityId: INCIDENT_SEVERITY_ID,
|
|
59
|
+
defaultAlertSeverityId: ALERT_SEVERITY_ID,
|
|
60
|
+
monitorName: "Test Monitor",
|
|
61
|
+
...overrides,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function getCriteriaInstances(
|
|
66
|
+
monitorStep: MonitorStep,
|
|
67
|
+
): Array<MonitorCriteriaInstance> {
|
|
68
|
+
return (
|
|
69
|
+
monitorStep.data?.monitorCriteria?.data?.monitorCriteriaInstanceArray || []
|
|
70
|
+
);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function getAllCriteriaIncidents(
|
|
74
|
+
monitorStep: MonitorStep,
|
|
75
|
+
): Array<CriteriaIncident> {
|
|
76
|
+
return getCriteriaInstances(monitorStep).flatMap(
|
|
77
|
+
(instance: MonitorCriteriaInstance) => {
|
|
78
|
+
return instance.data?.incidents || [];
|
|
79
|
+
},
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function getAllCriteriaAlerts(monitorStep: MonitorStep): Array<CriteriaAlert> {
|
|
84
|
+
return getCriteriaInstances(monitorStep).flatMap(
|
|
85
|
+
(instance: MonitorCriteriaInstance) => {
|
|
86
|
+
return instance.data?.alerts || [];
|
|
87
|
+
},
|
|
88
|
+
);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const ALL_RECOMMENDATIONS: Array<MonitorRecommendation> =
|
|
92
|
+
MonitorRecommendationCatalog.getAllRecommendations();
|
|
93
|
+
|
|
94
|
+
// One representative recommendation per resource type, for the focused tests.
|
|
95
|
+
const ONE_PER_RESOURCE_TYPE: Array<MonitorRecommendation> =
|
|
96
|
+
MonitorRecommendationCatalog.getResourceTypeDefinitions().map(
|
|
97
|
+
(definition: MonitorRecommendationResourceTypeDefinition) => {
|
|
98
|
+
return definition.getRecommendations()[0]!;
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
|
|
102
|
+
describe("MonitorRecommendationUtil", () => {
|
|
103
|
+
describe("getMonitorName", () => {
|
|
104
|
+
it("prefixes the recommendation name with the resource name", () => {
|
|
105
|
+
expect(
|
|
106
|
+
MonitorRecommendationUtil.getMonitorName({
|
|
107
|
+
recommendation: ALL_RECOMMENDATIONS[0]!,
|
|
108
|
+
resourceDisplayName: "prod-cluster",
|
|
109
|
+
}),
|
|
110
|
+
).toBe(`prod-cluster - ${ALL_RECOMMENDATIONS[0]!.name}`);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
it("trims surrounding whitespace on the resource name", () => {
|
|
114
|
+
expect(
|
|
115
|
+
MonitorRecommendationUtil.getMonitorName({
|
|
116
|
+
recommendation: ALL_RECOMMENDATIONS[0]!,
|
|
117
|
+
resourceDisplayName: " prod-cluster ",
|
|
118
|
+
}),
|
|
119
|
+
).toBe(`prod-cluster - ${ALL_RECOMMENDATIONS[0]!.name}`);
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it("falls back to the bare recommendation name when the resource has none", () => {
|
|
123
|
+
for (const resourceDisplayName of ["", " "]) {
|
|
124
|
+
expect(
|
|
125
|
+
MonitorRecommendationUtil.getMonitorName({
|
|
126
|
+
recommendation: ALL_RECOMMENDATIONS[0]!,
|
|
127
|
+
resourceDisplayName: resourceDisplayName,
|
|
128
|
+
}),
|
|
129
|
+
).toBe(ALL_RECOMMENDATIONS[0]!.name);
|
|
130
|
+
}
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
it("produces distinct names within a resource type", () => {
|
|
134
|
+
/*
|
|
135
|
+
* Scoped per resource type on purpose: template NAMES do collide across
|
|
136
|
+
* modules (Docker and Podman both ship "Container Restart Loop", and
|
|
137
|
+
* five more besides), unlike template ids. That is harmless — a given
|
|
138
|
+
* resource only ever renders one resource type's recommendations, so
|
|
139
|
+
* two same-named monitors can never be created against one resource.
|
|
140
|
+
*
|
|
141
|
+
* Names are a display concern only; the created/not-created diff uses
|
|
142
|
+
* fingerprints, not names.
|
|
143
|
+
*/
|
|
144
|
+
for (const definition of MonitorRecommendationCatalog.getResourceTypeDefinitions()) {
|
|
145
|
+
const names: Array<string> = definition
|
|
146
|
+
.getRecommendations()
|
|
147
|
+
.map((recommendation: MonitorRecommendation) => {
|
|
148
|
+
return MonitorRecommendationUtil.getMonitorName({
|
|
149
|
+
recommendation: recommendation,
|
|
150
|
+
resourceDisplayName: "prod",
|
|
151
|
+
});
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
expect(new Set(names).size).toBe(names.length);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe("getMonitorStep — every recommendation, every resource type", () => {
|
|
160
|
+
it("builds a step with at least one criteria instance for all recommendations", () => {
|
|
161
|
+
expect(ALL_RECOMMENDATIONS.length).toBeGreaterThan(0);
|
|
162
|
+
|
|
163
|
+
for (const recommendation of ALL_RECOMMENDATIONS) {
|
|
164
|
+
const monitorStep: MonitorStep =
|
|
165
|
+
recommendation.getMonitorStep(buildArgs());
|
|
166
|
+
|
|
167
|
+
expect(monitorStep.data).toBeTruthy();
|
|
168
|
+
expect(getCriteriaInstances(monitorStep).length).toBeGreaterThan(0);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
it("threads the resource identifier through every module's args rename", () => {
|
|
173
|
+
for (const recommendation of ALL_RECOMMENDATIONS) {
|
|
174
|
+
const fingerprint: MonitorRecommendationFingerprint | undefined =
|
|
175
|
+
MonitorRecommendationUtil.getFingerprintFromMonitorStep(
|
|
176
|
+
recommendation.getMonitorStep(buildArgs()),
|
|
177
|
+
);
|
|
178
|
+
|
|
179
|
+
expect(fingerprint).toBeDefined();
|
|
180
|
+
expect(fingerprint?.resourceIdentifier).toBe(RESOURCE_IDENTIFIER);
|
|
181
|
+
}
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
it("references at least one metric or formula per recommendation", () => {
|
|
185
|
+
for (const recommendation of ALL_RECOMMENDATIONS) {
|
|
186
|
+
const fingerprint: MonitorRecommendationFingerprint | undefined =
|
|
187
|
+
MonitorRecommendationUtil.getFingerprintFromMonitorStep(
|
|
188
|
+
recommendation.getMonitorStep(buildArgs()),
|
|
189
|
+
);
|
|
190
|
+
|
|
191
|
+
expect(
|
|
192
|
+
(fingerprint?.metricNames.length || 0) +
|
|
193
|
+
(fingerprint?.formulas.length || 0),
|
|
194
|
+
).toBeGreaterThan(0);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
it("gives every criteria instance a name and description", () => {
|
|
199
|
+
// MonitorCriteriaInstance.getValidationError rejects a blank one.
|
|
200
|
+
for (const recommendation of ALL_RECOMMENDATIONS) {
|
|
201
|
+
for (const instance of getCriteriaInstances(
|
|
202
|
+
recommendation.getMonitorStep(buildArgs()),
|
|
203
|
+
)) {
|
|
204
|
+
expect(instance.data?.name?.trim().length).toBeGreaterThan(0);
|
|
205
|
+
expect(instance.data?.description?.trim().length).toBeGreaterThan(0);
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it("gives every criteria incident and alert a title, description and severity", () => {
|
|
211
|
+
for (const recommendation of ALL_RECOMMENDATIONS) {
|
|
212
|
+
const monitorStep: MonitorStep =
|
|
213
|
+
recommendation.getMonitorStep(buildArgs());
|
|
214
|
+
|
|
215
|
+
for (const incident of getAllCriteriaIncidents(monitorStep)) {
|
|
216
|
+
expect(incident.title.trim().length).toBeGreaterThan(0);
|
|
217
|
+
expect(incident.description.trim().length).toBeGreaterThan(0);
|
|
218
|
+
expect(incident.incidentSeverityId).toBeDefined();
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
for (const alert of getAllCriteriaAlerts(monitorStep)) {
|
|
222
|
+
expect(alert.title.trim().length).toBeGreaterThan(0);
|
|
223
|
+
expect(alert.description.trim().length).toBeGreaterThan(0);
|
|
224
|
+
expect(alert.alertSeverityId).toBeDefined();
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
describe("applyNotificationSettingsToMonitorStep", () => {
|
|
231
|
+
it("no shipped template pages anyone on its own — the gap this feature closes", () => {
|
|
232
|
+
/*
|
|
233
|
+
* Documents the pre-existing behaviour every template has: an empty
|
|
234
|
+
* onCallPolicyIds on every generated incident and alert. If a template
|
|
235
|
+
* module ever starts shipping a real policy id, this test fails and the
|
|
236
|
+
* override semantics below need re-examining.
|
|
237
|
+
*/
|
|
238
|
+
for (const recommendation of ALL_RECOMMENDATIONS) {
|
|
239
|
+
const monitorStep: MonitorStep =
|
|
240
|
+
recommendation.getMonitorStep(buildArgs());
|
|
241
|
+
|
|
242
|
+
for (const incident of getAllCriteriaIncidents(monitorStep)) {
|
|
243
|
+
expect(incident.onCallPolicyIds || []).toEqual([]);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
for (const alert of getAllCriteriaAlerts(monitorStep)) {
|
|
247
|
+
expect(alert.onCallPolicyIds || []).toEqual([]);
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
it("applies on-call policies to every incident and alert of every criteria instance", () => {
|
|
253
|
+
const policyA: ObjectID = ObjectID.generate();
|
|
254
|
+
const policyB: ObjectID = ObjectID.generate();
|
|
255
|
+
|
|
256
|
+
for (const recommendation of ALL_RECOMMENDATIONS) {
|
|
257
|
+
const monitorStep: MonitorStep =
|
|
258
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
259
|
+
monitorStep: recommendation.getMonitorStep(buildArgs()),
|
|
260
|
+
notificationSettings: { onCallPolicyIds: [policyA, policyB] },
|
|
261
|
+
severity: recommendation.severity,
|
|
262
|
+
});
|
|
263
|
+
|
|
264
|
+
const incidents: Array<CriteriaIncident> =
|
|
265
|
+
getAllCriteriaIncidents(monitorStep);
|
|
266
|
+
const alerts: Array<CriteriaAlert> = getAllCriteriaAlerts(monitorStep);
|
|
267
|
+
|
|
268
|
+
expect(incidents.length).toBeGreaterThan(0);
|
|
269
|
+
expect(alerts.length).toBeGreaterThan(0);
|
|
270
|
+
|
|
271
|
+
for (const incident of incidents) {
|
|
272
|
+
expect(incident.onCallPolicyIds).toEqual([policyA, policyB]);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
for (const alert of alerts) {
|
|
276
|
+
expect(alert.onCallPolicyIds).toEqual([policyA, policyB]);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
it("copies the policy array rather than sharing it across criteria", () => {
|
|
282
|
+
/*
|
|
283
|
+
* A shared array reference would mean editing one incident's policies in
|
|
284
|
+
* a later flow silently edits every other incident on the monitor.
|
|
285
|
+
*/
|
|
286
|
+
const policy: ObjectID = ObjectID.generate();
|
|
287
|
+
const settingsArray: Array<ObjectID> = [policy];
|
|
288
|
+
|
|
289
|
+
const monitorStep: MonitorStep =
|
|
290
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
291
|
+
monitorStep: ONE_PER_RESOURCE_TYPE[0]!.getMonitorStep(buildArgs()),
|
|
292
|
+
notificationSettings: { onCallPolicyIds: settingsArray },
|
|
293
|
+
severity: ONE_PER_RESOURCE_TYPE[0]!.severity,
|
|
294
|
+
});
|
|
295
|
+
|
|
296
|
+
const incidents: Array<CriteriaIncident> =
|
|
297
|
+
getAllCriteriaIncidents(monitorStep);
|
|
298
|
+
|
|
299
|
+
for (const incident of incidents) {
|
|
300
|
+
expect(incident.onCallPolicyIds).not.toBe(settingsArray);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
if (incidents.length > 1) {
|
|
304
|
+
expect(incidents[0]!.onCallPolicyIds).not.toBe(
|
|
305
|
+
incidents[1]!.onCallPolicyIds,
|
|
306
|
+
);
|
|
307
|
+
}
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
it("applies labels and owners to incidents and alerts", () => {
|
|
311
|
+
const labelId: ObjectID = ObjectID.generate();
|
|
312
|
+
const teamId: ObjectID = ObjectID.generate();
|
|
313
|
+
const userId: ObjectID = ObjectID.generate();
|
|
314
|
+
|
|
315
|
+
const monitorStep: MonitorStep =
|
|
316
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
317
|
+
monitorStep: ONE_PER_RESOURCE_TYPE[0]!.getMonitorStep(buildArgs()),
|
|
318
|
+
notificationSettings: {
|
|
319
|
+
labelIds: [labelId],
|
|
320
|
+
ownerTeamIds: [teamId],
|
|
321
|
+
ownerUserIds: [userId],
|
|
322
|
+
},
|
|
323
|
+
severity: ONE_PER_RESOURCE_TYPE[0]!.severity,
|
|
324
|
+
});
|
|
325
|
+
|
|
326
|
+
for (const incident of getAllCriteriaIncidents(monitorStep)) {
|
|
327
|
+
expect(incident.labelIds).toEqual([labelId]);
|
|
328
|
+
expect(incident.ownerTeamIds).toEqual([teamId]);
|
|
329
|
+
expect(incident.ownerUserIds).toEqual([userId]);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
for (const alert of getAllCriteriaAlerts(monitorStep)) {
|
|
333
|
+
expect(alert.labelIds).toEqual([labelId]);
|
|
334
|
+
expect(alert.ownerTeamIds).toEqual([teamId]);
|
|
335
|
+
expect(alert.ownerUserIds).toEqual([userId]);
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
339
|
+
it("writes the severity mapped from the RECOMMENDATION's own severity", () => {
|
|
340
|
+
/*
|
|
341
|
+
* The mapping is per recommendation severity, not one id for the whole
|
|
342
|
+
* batch. A Critical and a Warning recommendation created in the same
|
|
343
|
+
* batch must come out with different severities, which is the entire
|
|
344
|
+
* reason the map exists — before it, both took
|
|
345
|
+
* `args.defaultIncidentSeverityId` and every recommendation paged
|
|
346
|
+
* identically.
|
|
347
|
+
*/
|
|
348
|
+
const criticalIncidentSeverityId: ObjectID = ObjectID.generate();
|
|
349
|
+
const warningIncidentSeverityId: ObjectID = ObjectID.generate();
|
|
350
|
+
const criticalAlertSeverityId: ObjectID = ObjectID.generate();
|
|
351
|
+
const warningAlertSeverityId: ObjectID = ObjectID.generate();
|
|
352
|
+
|
|
353
|
+
const notificationSettings: MonitorRecommendationNotificationSettings = {
|
|
354
|
+
incidentSeverityIdBySeverity: {
|
|
355
|
+
Critical: criticalIncidentSeverityId,
|
|
356
|
+
Warning: warningIncidentSeverityId,
|
|
357
|
+
},
|
|
358
|
+
alertSeverityIdBySeverity: {
|
|
359
|
+
Critical: criticalAlertSeverityId,
|
|
360
|
+
Warning: warningAlertSeverityId,
|
|
361
|
+
},
|
|
362
|
+
};
|
|
363
|
+
|
|
364
|
+
for (const severity of [
|
|
365
|
+
"Critical",
|
|
366
|
+
"Warning",
|
|
367
|
+
] as Array<MonitorRecommendationSeverity>) {
|
|
368
|
+
const monitorStep: MonitorStep =
|
|
369
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
370
|
+
monitorStep: ONE_PER_RESOURCE_TYPE[0]!.getMonitorStep(buildArgs()),
|
|
371
|
+
notificationSettings: notificationSettings,
|
|
372
|
+
severity: severity,
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
const expectedIncidentSeverityId: ObjectID =
|
|
376
|
+
severity === "Critical"
|
|
377
|
+
? criticalIncidentSeverityId
|
|
378
|
+
: warningIncidentSeverityId;
|
|
379
|
+
const expectedAlertSeverityId: ObjectID =
|
|
380
|
+
severity === "Critical"
|
|
381
|
+
? criticalAlertSeverityId
|
|
382
|
+
: warningAlertSeverityId;
|
|
383
|
+
|
|
384
|
+
for (const incident of getAllCriteriaIncidents(monitorStep)) {
|
|
385
|
+
expect(incident.incidentSeverityId).toEqual(
|
|
386
|
+
expectedIncidentSeverityId,
|
|
387
|
+
);
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
for (const alert of getAllCriteriaAlerts(monitorStep)) {
|
|
391
|
+
expect(alert.alertSeverityId).toEqual(expectedAlertSeverityId);
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
it("leaves the template's severity alone when the map has no entry for that severity", () => {
|
|
397
|
+
/*
|
|
398
|
+
* A partial map must not blank the severity out. A criteria instance
|
|
399
|
+
* with a populated incident and no severity id fails
|
|
400
|
+
* MonitorCriteriaInstance.getValidationError, so the whole create would
|
|
401
|
+
* fail at submit with a message about a field the user never saw.
|
|
402
|
+
*/
|
|
403
|
+
const monitorStep: MonitorStep =
|
|
404
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
405
|
+
monitorStep: ONE_PER_RESOURCE_TYPE[0]!.getMonitorStep(buildArgs()),
|
|
406
|
+
notificationSettings: {
|
|
407
|
+
incidentSeverityIdBySeverity: { Critical: ObjectID.generate() },
|
|
408
|
+
},
|
|
409
|
+
severity: "Warning",
|
|
410
|
+
});
|
|
411
|
+
|
|
412
|
+
for (const incident of getAllCriteriaIncidents(monitorStep)) {
|
|
413
|
+
expect(incident.incidentSeverityId).toEqual(INCIDENT_SEVERITY_ID);
|
|
414
|
+
}
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
it("leaves the template's own severity in place when none is chosen", () => {
|
|
418
|
+
const monitorStep: MonitorStep =
|
|
419
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
420
|
+
monitorStep: ONE_PER_RESOURCE_TYPE[0]!.getMonitorStep(buildArgs()),
|
|
421
|
+
notificationSettings: { onCallPolicyIds: [ObjectID.generate()] },
|
|
422
|
+
severity: ONE_PER_RESOURCE_TYPE[0]!.severity,
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
for (const incident of getAllCriteriaIncidents(monitorStep)) {
|
|
426
|
+
expect(incident.incidentSeverityId).toEqual(INCIDENT_SEVERITY_ID);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
for (const alert of getAllCriteriaAlerts(monitorStep)) {
|
|
430
|
+
expect(alert.alertSeverityId).toEqual(ALERT_SEVERITY_ID);
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it("is a no-op for empty settings and empty arrays", () => {
|
|
435
|
+
for (const notificationSettings of [
|
|
436
|
+
{},
|
|
437
|
+
{
|
|
438
|
+
onCallPolicyIds: [],
|
|
439
|
+
labelIds: [],
|
|
440
|
+
ownerTeamIds: [],
|
|
441
|
+
ownerUserIds: [],
|
|
442
|
+
},
|
|
443
|
+
]) {
|
|
444
|
+
const monitorStep: MonitorStep =
|
|
445
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
446
|
+
monitorStep: ONE_PER_RESOURCE_TYPE[0]!.getMonitorStep(buildArgs()),
|
|
447
|
+
notificationSettings: notificationSettings,
|
|
448
|
+
severity: ONE_PER_RESOURCE_TYPE[0]!.severity,
|
|
449
|
+
});
|
|
450
|
+
|
|
451
|
+
for (const incident of getAllCriteriaIncidents(monitorStep)) {
|
|
452
|
+
expect(incident.onCallPolicyIds || []).toEqual([]);
|
|
453
|
+
expect(incident.incidentSeverityId).toEqual(INCIDENT_SEVERITY_ID);
|
|
454
|
+
}
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
it("tolerates a step with no criteria instances", () => {
|
|
459
|
+
const emptyStep: MonitorStep = new MonitorStep();
|
|
460
|
+
|
|
461
|
+
expect(() => {
|
|
462
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
463
|
+
monitorStep: emptyStep,
|
|
464
|
+
notificationSettings: { onCallPolicyIds: [ObjectID.generate()] },
|
|
465
|
+
severity: "Critical",
|
|
466
|
+
});
|
|
467
|
+
}).not.toThrow();
|
|
468
|
+
});
|
|
469
|
+
});
|
|
470
|
+
|
|
471
|
+
describe("buildMonitorSteps", () => {
|
|
472
|
+
it("produces MonitorSteps that pass validation for every recommendation", () => {
|
|
473
|
+
/*
|
|
474
|
+
* MonitorSteps.getValidationError is the same client-side gate the
|
|
475
|
+
* monitor create form runs before POSTing. If a recommendation cannot
|
|
476
|
+
* pass it, the create flow would fail at submit time with a message the
|
|
477
|
+
* user cannot act on.
|
|
478
|
+
*/
|
|
479
|
+
for (const recommendation of ALL_RECOMMENDATIONS) {
|
|
480
|
+
const monitorSteps: MonitorSteps =
|
|
481
|
+
MonitorRecommendationUtil.buildMonitorSteps({
|
|
482
|
+
recommendation: recommendation,
|
|
483
|
+
args: buildArgs(),
|
|
484
|
+
defaultMonitorStatusId: DEFAULT_STATUS_ID,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
expect(
|
|
488
|
+
MonitorSteps.getValidationError(
|
|
489
|
+
monitorSteps,
|
|
490
|
+
recommendation.monitorType,
|
|
491
|
+
),
|
|
492
|
+
).toBeNull();
|
|
493
|
+
}
|
|
494
|
+
});
|
|
495
|
+
|
|
496
|
+
it("sets the default monitor status and exactly one step", () => {
|
|
497
|
+
const monitorSteps: MonitorSteps =
|
|
498
|
+
MonitorRecommendationUtil.buildMonitorSteps({
|
|
499
|
+
recommendation: ONE_PER_RESOURCE_TYPE[0]!,
|
|
500
|
+
args: buildArgs(),
|
|
501
|
+
defaultMonitorStatusId: DEFAULT_STATUS_ID,
|
|
502
|
+
});
|
|
503
|
+
|
|
504
|
+
expect(monitorSteps.data?.defaultMonitorStatusId).toEqual(
|
|
505
|
+
DEFAULT_STATUS_ID,
|
|
506
|
+
);
|
|
507
|
+
expect(monitorSteps.data?.monitorStepsInstanceArray.length).toBe(1);
|
|
508
|
+
});
|
|
509
|
+
|
|
510
|
+
it("applies notification settings when given, and still validates", () => {
|
|
511
|
+
const policy: ObjectID = ObjectID.generate();
|
|
512
|
+
|
|
513
|
+
for (const recommendation of ONE_PER_RESOURCE_TYPE) {
|
|
514
|
+
const monitorSteps: MonitorSteps =
|
|
515
|
+
MonitorRecommendationUtil.buildMonitorSteps({
|
|
516
|
+
recommendation: recommendation,
|
|
517
|
+
args: buildArgs(),
|
|
518
|
+
defaultMonitorStatusId: DEFAULT_STATUS_ID,
|
|
519
|
+
notificationSettings: { onCallPolicyIds: [policy] },
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
expect(
|
|
523
|
+
MonitorSteps.getValidationError(
|
|
524
|
+
monitorSteps,
|
|
525
|
+
recommendation.monitorType,
|
|
526
|
+
),
|
|
527
|
+
).toBeNull();
|
|
528
|
+
|
|
529
|
+
const monitorStep: MonitorStep =
|
|
530
|
+
monitorSteps.data!.monitorStepsInstanceArray[0]!;
|
|
531
|
+
|
|
532
|
+
for (const incident of getAllCriteriaIncidents(monitorStep)) {
|
|
533
|
+
expect(incident.onCallPolicyIds).toEqual([policy]);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
});
|
|
537
|
+
|
|
538
|
+
it("does not mutate a previously built step when building again", () => {
|
|
539
|
+
const recommendation: MonitorRecommendation = ONE_PER_RESOURCE_TYPE[0]!;
|
|
540
|
+
|
|
541
|
+
const withoutPolicy: MonitorSteps =
|
|
542
|
+
MonitorRecommendationUtil.buildMonitorSteps({
|
|
543
|
+
recommendation: recommendation,
|
|
544
|
+
args: buildArgs(),
|
|
545
|
+
defaultMonitorStatusId: DEFAULT_STATUS_ID,
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
MonitorRecommendationUtil.buildMonitorSteps({
|
|
549
|
+
recommendation: recommendation,
|
|
550
|
+
args: buildArgs(),
|
|
551
|
+
defaultMonitorStatusId: DEFAULT_STATUS_ID,
|
|
552
|
+
notificationSettings: { onCallPolicyIds: [ObjectID.generate()] },
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
for (const incident of getAllCriteriaIncidents(
|
|
556
|
+
withoutPolicy.data!.monitorStepsInstanceArray[0]!,
|
|
557
|
+
)) {
|
|
558
|
+
expect(incident.onCallPolicyIds || []).toEqual([]);
|
|
559
|
+
}
|
|
560
|
+
});
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
describe("getFingerprintFromMonitorStep", () => {
|
|
564
|
+
it("is stable across repeated builds of the same recommendation", () => {
|
|
565
|
+
for (const recommendation of ONE_PER_RESOURCE_TYPE) {
|
|
566
|
+
const a: MonitorRecommendationFingerprint | undefined =
|
|
567
|
+
MonitorRecommendationUtil.getFingerprintFromMonitorStep(
|
|
568
|
+
recommendation.getMonitorStep(buildArgs()),
|
|
569
|
+
);
|
|
570
|
+
const b: MonitorRecommendationFingerprint | undefined =
|
|
571
|
+
MonitorRecommendationUtil.getFingerprintFromMonitorStep(
|
|
572
|
+
recommendation.getMonitorStep(buildArgs()),
|
|
573
|
+
);
|
|
574
|
+
|
|
575
|
+
expect(MonitorRecommendationUtil.areFingerprintsEqual(a, b)).toBe(true);
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
it("ignores the monitor name — renaming must not resurface a recommendation", () => {
|
|
580
|
+
const recommendation: MonitorRecommendation = ONE_PER_RESOURCE_TYPE[0]!;
|
|
581
|
+
|
|
582
|
+
const a: MonitorRecommendationFingerprint | undefined =
|
|
583
|
+
MonitorRecommendationUtil.getFingerprintFromMonitorStep(
|
|
584
|
+
recommendation.getMonitorStep(buildArgs({ monitorName: "one" })),
|
|
585
|
+
);
|
|
586
|
+
const b: MonitorRecommendationFingerprint | undefined =
|
|
587
|
+
MonitorRecommendationUtil.getFingerprintFromMonitorStep(
|
|
588
|
+
recommendation.getMonitorStep(
|
|
589
|
+
buildArgs({ monitorName: "something else entirely" }),
|
|
590
|
+
),
|
|
591
|
+
);
|
|
592
|
+
|
|
593
|
+
expect(MonitorRecommendationUtil.areFingerprintsEqual(a, b)).toBe(true);
|
|
594
|
+
});
|
|
595
|
+
|
|
596
|
+
it("differs when the resource identifier differs", () => {
|
|
597
|
+
const recommendation: MonitorRecommendation = ONE_PER_RESOURCE_TYPE[0]!;
|
|
598
|
+
|
|
599
|
+
const a: MonitorRecommendationFingerprint | undefined =
|
|
600
|
+
MonitorRecommendationUtil.getFingerprintFromMonitorStep(
|
|
601
|
+
recommendation.getMonitorStep(
|
|
602
|
+
buildArgs({ resourceIdentifier: "cluster-a" }),
|
|
603
|
+
),
|
|
604
|
+
);
|
|
605
|
+
const b: MonitorRecommendationFingerprint | undefined =
|
|
606
|
+
MonitorRecommendationUtil.getFingerprintFromMonitorStep(
|
|
607
|
+
recommendation.getMonitorStep(
|
|
608
|
+
buildArgs({ resourceIdentifier: "cluster-b" }),
|
|
609
|
+
),
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
expect(MonitorRecommendationUtil.areFingerprintsEqual(a, b)).toBe(false);
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
it("returns undefined for a step with no infrastructure config", () => {
|
|
616
|
+
expect(
|
|
617
|
+
MonitorRecommendationUtil.getFingerprintFromMonitorStep(
|
|
618
|
+
new MonitorStep(),
|
|
619
|
+
),
|
|
620
|
+
).toBeUndefined();
|
|
621
|
+
});
|
|
622
|
+
|
|
623
|
+
it("treats two undefined fingerprints as unequal", () => {
|
|
624
|
+
// Otherwise every non-infrastructure monitor would "cover" everything.
|
|
625
|
+
expect(
|
|
626
|
+
MonitorRecommendationUtil.areFingerprintsEqual(undefined, undefined),
|
|
627
|
+
).toBe(false);
|
|
628
|
+
});
|
|
629
|
+
|
|
630
|
+
it("sorts metric names so query order is not part of the identity", () => {
|
|
631
|
+
const fingerprint: MonitorRecommendationFingerprint = {
|
|
632
|
+
resourceIdentifier: "x",
|
|
633
|
+
configKind: "kubernetesMonitor",
|
|
634
|
+
metricNames: ["b", "a"],
|
|
635
|
+
formulas: [],
|
|
636
|
+
metricAliases: [],
|
|
637
|
+
queryAttributes: [],
|
|
638
|
+
criteriaFilters: [],
|
|
639
|
+
};
|
|
640
|
+
|
|
641
|
+
const sorted: MonitorRecommendationFingerprint = {
|
|
642
|
+
resourceIdentifier: "x",
|
|
643
|
+
configKind: "kubernetesMonitor",
|
|
644
|
+
metricNames: ["a", "b"],
|
|
645
|
+
formulas: [],
|
|
646
|
+
metricAliases: [],
|
|
647
|
+
queryAttributes: [],
|
|
648
|
+
criteriaFilters: [],
|
|
649
|
+
};
|
|
650
|
+
|
|
651
|
+
expect(
|
|
652
|
+
MonitorRecommendationUtil.serializeFingerprint(sorted),
|
|
653
|
+
).not.toEqual(
|
|
654
|
+
MonitorRecommendationUtil.serializeFingerprint(fingerprint),
|
|
655
|
+
);
|
|
656
|
+
|
|
657
|
+
/*
|
|
658
|
+
* The util sorts on the way OUT of a step, so two steps built with
|
|
659
|
+
* queries in different orders converge — verified via real steps in the
|
|
660
|
+
* stability test above. This case only pins that serialize() itself is
|
|
661
|
+
* order-sensitive, i.e. sorting is genuinely load-bearing.
|
|
662
|
+
*/
|
|
663
|
+
});
|
|
664
|
+
});
|
|
665
|
+
|
|
666
|
+
describe("getCoveredRecommendationIds", () => {
|
|
667
|
+
const kubernetesRecommendations: Array<MonitorRecommendation> =
|
|
668
|
+
MonitorRecommendationCatalog.getRecommendations(
|
|
669
|
+
MonitorRecommendationResourceType.Kubernetes,
|
|
670
|
+
);
|
|
671
|
+
|
|
672
|
+
it("covers nothing when no monitors exist", () => {
|
|
673
|
+
expect(
|
|
674
|
+
MonitorRecommendationUtil.getCoveredRecommendationIds({
|
|
675
|
+
recommendations: kubernetesRecommendations,
|
|
676
|
+
existingMonitorSteps: [],
|
|
677
|
+
args: buildArgs(),
|
|
678
|
+
}).size,
|
|
679
|
+
).toBe(0);
|
|
680
|
+
});
|
|
681
|
+
|
|
682
|
+
it("covers exactly the recommendations an existing monitor was built from", () => {
|
|
683
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
684
|
+
const created: MonitorRecommendation = kubernetesRecommendations[0]!;
|
|
685
|
+
|
|
686
|
+
const existingStep: MonitorStep = created.getMonitorStep({
|
|
687
|
+
...args,
|
|
688
|
+
monitorName: MonitorRecommendationUtil.getMonitorName({
|
|
689
|
+
recommendation: created,
|
|
690
|
+
resourceDisplayName: args.monitorName,
|
|
691
|
+
}),
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
const covered: Set<string> =
|
|
695
|
+
MonitorRecommendationUtil.getCoveredRecommendationIds({
|
|
696
|
+
recommendations: kubernetesRecommendations,
|
|
697
|
+
existingMonitorSteps: [existingStep],
|
|
698
|
+
args: args,
|
|
699
|
+
});
|
|
700
|
+
|
|
701
|
+
expect(covered.has(created.recommendationId)).toBe(true);
|
|
702
|
+
expect(covered.size).toBe(1);
|
|
703
|
+
});
|
|
704
|
+
|
|
705
|
+
it("covers several recommendations when several monitors exist", () => {
|
|
706
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
707
|
+
const created: Array<MonitorRecommendation> =
|
|
708
|
+
kubernetesRecommendations.slice(0, 3);
|
|
709
|
+
|
|
710
|
+
const existingSteps: Array<MonitorStep> = created.map(
|
|
711
|
+
(recommendation: MonitorRecommendation) => {
|
|
712
|
+
return recommendation.getMonitorStep({
|
|
713
|
+
...args,
|
|
714
|
+
monitorName: MonitorRecommendationUtil.getMonitorName({
|
|
715
|
+
recommendation: recommendation,
|
|
716
|
+
resourceDisplayName: args.monitorName,
|
|
717
|
+
}),
|
|
718
|
+
});
|
|
719
|
+
},
|
|
720
|
+
);
|
|
721
|
+
|
|
722
|
+
const covered: Set<string> =
|
|
723
|
+
MonitorRecommendationUtil.getCoveredRecommendationIds({
|
|
724
|
+
recommendations: kubernetesRecommendations,
|
|
725
|
+
existingMonitorSteps: existingSteps,
|
|
726
|
+
args: args,
|
|
727
|
+
});
|
|
728
|
+
|
|
729
|
+
expect(covered.size).toBe(created.length);
|
|
730
|
+
for (const recommendation of created) {
|
|
731
|
+
expect(covered.has(recommendation.recommendationId)).toBe(true);
|
|
732
|
+
}
|
|
733
|
+
});
|
|
734
|
+
|
|
735
|
+
it("does not cover a recommendation created against a different resource", () => {
|
|
736
|
+
const created: MonitorRecommendation = kubernetesRecommendations[0]!;
|
|
737
|
+
|
|
738
|
+
const otherClusterStep: MonitorStep = created.getMonitorStep(
|
|
739
|
+
buildArgs({ resourceIdentifier: "some-other-cluster" }),
|
|
740
|
+
);
|
|
741
|
+
|
|
742
|
+
const covered: Set<string> =
|
|
743
|
+
MonitorRecommendationUtil.getCoveredRecommendationIds({
|
|
744
|
+
recommendations: kubernetesRecommendations,
|
|
745
|
+
existingMonitorSteps: [otherClusterStep],
|
|
746
|
+
args: buildArgs(),
|
|
747
|
+
});
|
|
748
|
+
|
|
749
|
+
expect(covered.size).toBe(0);
|
|
750
|
+
});
|
|
751
|
+
|
|
752
|
+
it("ignores monitors that carry no infrastructure config", () => {
|
|
753
|
+
const covered: Set<string> =
|
|
754
|
+
MonitorRecommendationUtil.getCoveredRecommendationIds({
|
|
755
|
+
recommendations: kubernetesRecommendations,
|
|
756
|
+
existingMonitorSteps: [new MonitorStep()],
|
|
757
|
+
args: buildArgs(),
|
|
758
|
+
});
|
|
759
|
+
|
|
760
|
+
expect(covered.size).toBe(0);
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
it("is unaffected by the notification settings applied at create time", () => {
|
|
764
|
+
/*
|
|
765
|
+
* On-call policies are not part of what a monitor watches, so a monitor
|
|
766
|
+
* created WITH policies must still register as covering its
|
|
767
|
+
* recommendation (which is built without them here).
|
|
768
|
+
*/
|
|
769
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
770
|
+
const created: MonitorRecommendation = kubernetesRecommendations[0]!;
|
|
771
|
+
|
|
772
|
+
const existingStep: MonitorStep =
|
|
773
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
774
|
+
monitorStep: created.getMonitorStep({
|
|
775
|
+
...args,
|
|
776
|
+
monitorName: MonitorRecommendationUtil.getMonitorName({
|
|
777
|
+
recommendation: created,
|
|
778
|
+
resourceDisplayName: args.monitorName,
|
|
779
|
+
}),
|
|
780
|
+
}),
|
|
781
|
+
notificationSettings: {
|
|
782
|
+
onCallPolicyIds: [ObjectID.generate()],
|
|
783
|
+
labelIds: [ObjectID.generate()],
|
|
784
|
+
},
|
|
785
|
+
severity: created.severity,
|
|
786
|
+
});
|
|
787
|
+
|
|
788
|
+
const covered: Set<string> =
|
|
789
|
+
MonitorRecommendationUtil.getCoveredRecommendationIds({
|
|
790
|
+
recommendations: kubernetesRecommendations,
|
|
791
|
+
existingMonitorSteps: [existingStep],
|
|
792
|
+
args: args,
|
|
793
|
+
});
|
|
794
|
+
|
|
795
|
+
expect(covered.has(created.recommendationId)).toBe(true);
|
|
796
|
+
});
|
|
797
|
+
|
|
798
|
+
it("works for every resource type, not just Kubernetes", () => {
|
|
799
|
+
for (const definition of MonitorRecommendationCatalog.getResourceTypeDefinitions()) {
|
|
800
|
+
const recommendations: Array<MonitorRecommendation> =
|
|
801
|
+
definition.getRecommendations();
|
|
802
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
803
|
+
const created: MonitorRecommendation = recommendations[0]!;
|
|
804
|
+
|
|
805
|
+
const existingStep: MonitorStep = created.getMonitorStep({
|
|
806
|
+
...args,
|
|
807
|
+
monitorName: MonitorRecommendationUtil.getMonitorName({
|
|
808
|
+
recommendation: created,
|
|
809
|
+
resourceDisplayName: args.monitorName,
|
|
810
|
+
}),
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
const covered: Set<string> =
|
|
814
|
+
MonitorRecommendationUtil.getCoveredRecommendationIds({
|
|
815
|
+
recommendations: recommendations,
|
|
816
|
+
existingMonitorSteps: [existingStep],
|
|
817
|
+
args: args,
|
|
818
|
+
});
|
|
819
|
+
|
|
820
|
+
expect(covered.has(created.recommendationId)).toBe(true);
|
|
821
|
+
}
|
|
822
|
+
});
|
|
823
|
+
});
|
|
824
|
+
|
|
825
|
+
describe("sortRecommendations", () => {
|
|
826
|
+
const recommendations: Array<MonitorRecommendation> =
|
|
827
|
+
MonitorRecommendationCatalog.getRecommendations(
|
|
828
|
+
MonitorRecommendationResourceType.Kubernetes,
|
|
829
|
+
);
|
|
830
|
+
|
|
831
|
+
it("keeps every recommendation exactly once", () => {
|
|
832
|
+
const sorted: Array<MonitorRecommendation> =
|
|
833
|
+
MonitorRecommendationUtil.sortRecommendations({
|
|
834
|
+
recommendations: recommendations,
|
|
835
|
+
coveredRecommendationIds: new Set<string>(),
|
|
836
|
+
});
|
|
837
|
+
|
|
838
|
+
expect(sorted.length).toBe(recommendations.length);
|
|
839
|
+
expect(
|
|
840
|
+
new Set(
|
|
841
|
+
sorted.map((r: MonitorRecommendation) => {
|
|
842
|
+
return r.recommendationId;
|
|
843
|
+
}),
|
|
844
|
+
).size,
|
|
845
|
+
).toBe(recommendations.length);
|
|
846
|
+
});
|
|
847
|
+
|
|
848
|
+
it("does not mutate the input array", () => {
|
|
849
|
+
const input: Array<MonitorRecommendation> = [...recommendations];
|
|
850
|
+
const before: Array<string> = input.map((r: MonitorRecommendation) => {
|
|
851
|
+
return r.recommendationId;
|
|
852
|
+
});
|
|
853
|
+
|
|
854
|
+
MonitorRecommendationUtil.sortRecommendations({
|
|
855
|
+
recommendations: input,
|
|
856
|
+
coveredRecommendationIds: new Set<string>([
|
|
857
|
+
recommendations[0]!.recommendationId,
|
|
858
|
+
]),
|
|
859
|
+
});
|
|
860
|
+
|
|
861
|
+
expect(
|
|
862
|
+
input.map((r: MonitorRecommendation) => {
|
|
863
|
+
return r.recommendationId;
|
|
864
|
+
}),
|
|
865
|
+
).toEqual(before);
|
|
866
|
+
});
|
|
867
|
+
|
|
868
|
+
it("puts not-yet-created recommendations before created ones", () => {
|
|
869
|
+
const covered: Set<string> = new Set<string>([
|
|
870
|
+
recommendations[0]!.recommendationId,
|
|
871
|
+
]);
|
|
872
|
+
|
|
873
|
+
const sorted: Array<MonitorRecommendation> =
|
|
874
|
+
MonitorRecommendationUtil.sortRecommendations({
|
|
875
|
+
recommendations: recommendations,
|
|
876
|
+
coveredRecommendationIds: covered,
|
|
877
|
+
});
|
|
878
|
+
|
|
879
|
+
expect(sorted[sorted.length - 1]!.recommendationId).toBe(
|
|
880
|
+
recommendations[0]!.recommendationId,
|
|
881
|
+
);
|
|
882
|
+
});
|
|
883
|
+
|
|
884
|
+
it("puts Critical before Warning among not-yet-created recommendations", () => {
|
|
885
|
+
const sorted: Array<MonitorRecommendation> =
|
|
886
|
+
MonitorRecommendationUtil.sortRecommendations({
|
|
887
|
+
recommendations: recommendations,
|
|
888
|
+
coveredRecommendationIds: new Set<string>(),
|
|
889
|
+
});
|
|
890
|
+
|
|
891
|
+
let seenWarning: boolean = false;
|
|
892
|
+
for (const recommendation of sorted) {
|
|
893
|
+
if (recommendation.severity === "Warning") {
|
|
894
|
+
seenWarning = true;
|
|
895
|
+
} else if (seenWarning) {
|
|
896
|
+
throw new Error(
|
|
897
|
+
`Critical recommendation "${recommendation.name}" sorted after a Warning one.`,
|
|
898
|
+
);
|
|
899
|
+
}
|
|
900
|
+
}
|
|
901
|
+
|
|
902
|
+
// Guard against a vacuous pass if the module ever ships one severity only.
|
|
903
|
+
expect(seenWarning).toBe(true);
|
|
904
|
+
});
|
|
905
|
+
|
|
906
|
+
it("preserves module declaration order within a severity", () => {
|
|
907
|
+
const sorted: Array<MonitorRecommendation> =
|
|
908
|
+
MonitorRecommendationUtil.sortRecommendations({
|
|
909
|
+
recommendations: recommendations,
|
|
910
|
+
coveredRecommendationIds: new Set<string>(),
|
|
911
|
+
});
|
|
912
|
+
|
|
913
|
+
const criticalIdsInSortedOrder: Array<string> = sorted
|
|
914
|
+
.filter((r: MonitorRecommendation) => {
|
|
915
|
+
return r.severity === "Critical";
|
|
916
|
+
})
|
|
917
|
+
.map((r: MonitorRecommendation) => {
|
|
918
|
+
return r.recommendationId;
|
|
919
|
+
});
|
|
920
|
+
|
|
921
|
+
const criticalIdsInDeclarationOrder: Array<string> = recommendations
|
|
922
|
+
.filter((r: MonitorRecommendation) => {
|
|
923
|
+
return r.severity === "Critical";
|
|
924
|
+
})
|
|
925
|
+
.map((r: MonitorRecommendation) => {
|
|
926
|
+
return r.recommendationId;
|
|
927
|
+
});
|
|
928
|
+
|
|
929
|
+
expect(criticalIdsInSortedOrder).toEqual(criticalIdsInDeclarationOrder);
|
|
930
|
+
});
|
|
931
|
+
|
|
932
|
+
it("sorts covered recommendations among themselves by severity too", () => {
|
|
933
|
+
const covered: Set<string> = new Set<string>(
|
|
934
|
+
recommendations.map((r: MonitorRecommendation) => {
|
|
935
|
+
return r.recommendationId;
|
|
936
|
+
}),
|
|
937
|
+
);
|
|
938
|
+
|
|
939
|
+
const sorted: Array<MonitorRecommendation> =
|
|
940
|
+
MonitorRecommendationUtil.sortRecommendations({
|
|
941
|
+
recommendations: recommendations,
|
|
942
|
+
coveredRecommendationIds: covered,
|
|
943
|
+
});
|
|
944
|
+
|
|
945
|
+
const firstWarningIndex: number = sorted.findIndex(
|
|
946
|
+
(r: MonitorRecommendation) => {
|
|
947
|
+
return r.severity === "Warning";
|
|
948
|
+
},
|
|
949
|
+
);
|
|
950
|
+
const lastCriticalIndex: number = sorted.reduce(
|
|
951
|
+
(last: number, r: MonitorRecommendation, index: number) => {
|
|
952
|
+
return r.severity === "Critical" ? index : last;
|
|
953
|
+
},
|
|
954
|
+
-1,
|
|
955
|
+
);
|
|
956
|
+
|
|
957
|
+
expect(lastCriticalIndex).toBeLessThan(firstWarningIndex);
|
|
958
|
+
});
|
|
959
|
+
});
|
|
960
|
+
});
|