@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,901 @@
|
|
|
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
|
+
MonitorRecommendationNotificationMode,
|
|
11
|
+
MonitorRecommendationNotificationSettings,
|
|
12
|
+
MonitorRecommendationResourceType,
|
|
13
|
+
} from "../../../../Types/Monitor/Recommendation/MonitorRecommendationTypes";
|
|
14
|
+
import MonitorStep from "../../../../Types/Monitor/MonitorStep";
|
|
15
|
+
import MonitorCriteriaInstance from "../../../../Types/Monitor/MonitorCriteriaInstance";
|
|
16
|
+
import MonitorType from "../../../../Types/Monitor/MonitorType";
|
|
17
|
+
import ObjectID from "../../../../Types/ObjectID";
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* `getCoveredRecommendationMonitorIds` is the richer sibling of
|
|
21
|
+
* `getCoveredRecommendationIds`: same diff, but it remembers WHICH monitor
|
|
22
|
+
* already watches each recommendation so the card can link to it.
|
|
23
|
+
*
|
|
24
|
+
* Two classes of failure motivate this file:
|
|
25
|
+
*
|
|
26
|
+
* 1. Drift between the two methods. They compute the same diff twice, and
|
|
27
|
+
* the two answers are rendered in two different places — the side-menu
|
|
28
|
+
* badge counts what is NOT covered from one of them, the page greys out
|
|
29
|
+
* cards from the other. If they ever disagree, the badge says "3 to
|
|
30
|
+
* create" while the page shows a different number of live cards, and
|
|
31
|
+
* nothing crashes to tell anyone. The agreement tests below run both over
|
|
32
|
+
* the same inputs for all eight resource types.
|
|
33
|
+
*
|
|
34
|
+
* 2. A wrong link target. The Map's VALUE is a monitor id that becomes a
|
|
35
|
+
* navigable link. Pointing it at the wrong monitor is worse than not
|
|
36
|
+
* linking at all: the user opens a monitor, sees thresholds that do not
|
|
37
|
+
* match the card they clicked, and concludes the feature lies. The
|
|
38
|
+
* duplicate-monitor and multi-step cases below pin exactly which id wins.
|
|
39
|
+
*
|
|
40
|
+
* The fixture style (buildArgs factory, generated ObjectID constants) mirrors
|
|
41
|
+
* MonitorRecommendationUtil.test.ts so both files describe the same world.
|
|
42
|
+
*/
|
|
43
|
+
|
|
44
|
+
const ONLINE_STATUS_ID: ObjectID = ObjectID.generate();
|
|
45
|
+
const OFFLINE_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
|
+
/*
|
|
52
|
+
* Structurally identical to the inline element type
|
|
53
|
+
* `getCoveredRecommendationMonitorIds` accepts. Named here only so the
|
|
54
|
+
* helpers below can be typed.
|
|
55
|
+
*/
|
|
56
|
+
interface ExistingRecommendationMonitor {
|
|
57
|
+
monitorId: ObjectID;
|
|
58
|
+
monitorSteps: Array<MonitorStep>;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function buildArgs(
|
|
62
|
+
overrides?: Partial<MonitorRecommendationArgs>,
|
|
63
|
+
): MonitorRecommendationArgs {
|
|
64
|
+
return {
|
|
65
|
+
resourceIdentifier: RESOURCE_IDENTIFIER,
|
|
66
|
+
onlineMonitorStatusId: ONLINE_STATUS_ID,
|
|
67
|
+
offlineMonitorStatusId: OFFLINE_STATUS_ID,
|
|
68
|
+
defaultIncidentSeverityId: INCIDENT_SEVERITY_ID,
|
|
69
|
+
defaultAlertSeverityId: ALERT_SEVERITY_ID,
|
|
70
|
+
monitorName: "Test Monitor",
|
|
71
|
+
...overrides,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/*
|
|
76
|
+
* Build the step a monitor created from this recommendation would actually
|
|
77
|
+
* carry — including the name prefixing the create flow applies. Building it
|
|
78
|
+
* with the raw args instead would still pass today (the name is not part of
|
|
79
|
+
* the fingerprint) but would stop these tests from noticing if the name ever
|
|
80
|
+
* leaked into the fingerprint.
|
|
81
|
+
*/
|
|
82
|
+
function buildCreatedStep(
|
|
83
|
+
recommendation: MonitorRecommendation,
|
|
84
|
+
args: MonitorRecommendationArgs,
|
|
85
|
+
): MonitorStep {
|
|
86
|
+
return recommendation.getMonitorStep({
|
|
87
|
+
...args,
|
|
88
|
+
monitorName: MonitorRecommendationUtil.getMonitorName({
|
|
89
|
+
recommendation: recommendation,
|
|
90
|
+
resourceDisplayName: args.monitorName,
|
|
91
|
+
}),
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/*
|
|
96
|
+
* What the diff actually compares for a recommendation, as a string. Used only
|
|
97
|
+
* to pick recommendations the diff can tell apart.
|
|
98
|
+
*/
|
|
99
|
+
function getSerializedFingerprint(
|
|
100
|
+
recommendation: MonitorRecommendation,
|
|
101
|
+
args: MonitorRecommendationArgs,
|
|
102
|
+
): string {
|
|
103
|
+
const fingerprint: MonitorRecommendationFingerprint | undefined =
|
|
104
|
+
MonitorRecommendationUtil.getFingerprintFromMonitorStep(
|
|
105
|
+
buildCreatedStep(recommendation, args),
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
if (!fingerprint) {
|
|
109
|
+
throw new Error(
|
|
110
|
+
`${recommendation.recommendationId} built a step with no infrastructure config.`,
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return MonitorRecommendationUtil.serializeFingerprint(fingerprint);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function buildMonitorFor(
|
|
118
|
+
recommendations: Array<MonitorRecommendation>,
|
|
119
|
+
args: MonitorRecommendationArgs,
|
|
120
|
+
): ExistingRecommendationMonitor {
|
|
121
|
+
return {
|
|
122
|
+
monitorId: ObjectID.generate(),
|
|
123
|
+
monitorSteps: recommendations.map(
|
|
124
|
+
(recommendation: MonitorRecommendation) => {
|
|
125
|
+
return buildCreatedStep(recommendation, args);
|
|
126
|
+
},
|
|
127
|
+
),
|
|
128
|
+
};
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/*
|
|
132
|
+
* A step that has `data` but no infrastructure sub-config at all — what an
|
|
133
|
+
* HTTP/Ping monitor on the same project looks like. Distinct from
|
|
134
|
+
* `new MonitorStep()` (no `data` whatsoever), and the more realistic of the
|
|
135
|
+
* two: a bare step could be short-circuited by a null check that a populated
|
|
136
|
+
* one would sail past and then read `undefined.clusterIdentifier` from.
|
|
137
|
+
*/
|
|
138
|
+
function buildNonInfrastructureStep(): MonitorStep {
|
|
139
|
+
return MonitorStep.getDefaultMonitorStep({
|
|
140
|
+
monitorName: "Ping Monitor",
|
|
141
|
+
monitorType: MonitorType.Ping,
|
|
142
|
+
onlineMonitorStatusId: ONLINE_STATUS_ID,
|
|
143
|
+
offlineMonitorStatusId: OFFLINE_STATUS_ID,
|
|
144
|
+
defaultIncidentSeverityId: INCIDENT_SEVERITY_ID,
|
|
145
|
+
defaultAlertSeverityId: ALERT_SEVERITY_ID,
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function getCriteriaInstances(
|
|
150
|
+
monitorStep: MonitorStep,
|
|
151
|
+
): Array<MonitorCriteriaInstance> {
|
|
152
|
+
return (
|
|
153
|
+
monitorStep.data?.monitorCriteria?.data?.monitorCriteriaInstanceArray || []
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function flattenSteps(
|
|
158
|
+
monitors: Array<ExistingRecommendationMonitor>,
|
|
159
|
+
): Array<MonitorStep> {
|
|
160
|
+
return monitors.flatMap((monitor: ExistingRecommendationMonitor) => {
|
|
161
|
+
return monitor.monitorSteps;
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const RESOURCE_TYPE_DEFINITIONS: Array<MonitorRecommendationResourceTypeDefinition> =
|
|
166
|
+
MonitorRecommendationCatalog.getResourceTypeDefinitions();
|
|
167
|
+
|
|
168
|
+
const KUBERNETES_RECOMMENDATIONS: Array<MonitorRecommendation> =
|
|
169
|
+
MonitorRecommendationCatalog.getRecommendations(
|
|
170
|
+
MonitorRecommendationResourceType.Kubernetes,
|
|
171
|
+
);
|
|
172
|
+
|
|
173
|
+
describe("MonitorRecommendationUtil.getCoveredRecommendationMonitorIds", () => {
|
|
174
|
+
it("maps a covered recommendation to the id of the monitor that watches it", () => {
|
|
175
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
176
|
+
const created: MonitorRecommendation = KUBERNETES_RECOMMENDATIONS[0]!;
|
|
177
|
+
const existingMonitor: ExistingRecommendationMonitor = buildMonitorFor(
|
|
178
|
+
[created],
|
|
179
|
+
args,
|
|
180
|
+
);
|
|
181
|
+
|
|
182
|
+
const covered: Map<string, ObjectID> =
|
|
183
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
184
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
185
|
+
existingMonitors: [existingMonitor],
|
|
186
|
+
args: args,
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
expect(covered.size).toBe(1);
|
|
190
|
+
/*
|
|
191
|
+
* `toBe`, not `toEqual`: the caller hands the id straight to a link, so
|
|
192
|
+
* the method must pass the caller's own ObjectID through rather than
|
|
193
|
+
* reconstructing one from a string somewhere in the middle.
|
|
194
|
+
*/
|
|
195
|
+
expect(covered.get(created.recommendationId)).toBe(
|
|
196
|
+
existingMonitor.monitorId,
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("leaves recommendations that no monitor watches out of the map", () => {
|
|
201
|
+
/*
|
|
202
|
+
* Absence, not a `undefined` value under the key: the page renders a
|
|
203
|
+
* "create" card for anything missing from the map, and a present key with
|
|
204
|
+
* no id would render an "already created" card whose link goes nowhere.
|
|
205
|
+
*/
|
|
206
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
207
|
+
const created: MonitorRecommendation = KUBERNETES_RECOMMENDATIONS[0]!;
|
|
208
|
+
const notCreated: MonitorRecommendation = KUBERNETES_RECOMMENDATIONS[1]!;
|
|
209
|
+
|
|
210
|
+
const covered: Map<string, ObjectID> =
|
|
211
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
212
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
213
|
+
existingMonitors: [buildMonitorFor([created], args)],
|
|
214
|
+
args: args,
|
|
215
|
+
});
|
|
216
|
+
|
|
217
|
+
expect(covered.has(notCreated.recommendationId)).toBe(false);
|
|
218
|
+
expect(KUBERNETES_RECOMMENDATIONS.length).toBeGreaterThan(1);
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it("is empty when the resource has no monitors at all", () => {
|
|
222
|
+
expect(
|
|
223
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
224
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
225
|
+
existingMonitors: [],
|
|
226
|
+
args: buildArgs(),
|
|
227
|
+
}).size,
|
|
228
|
+
).toBe(0);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it("is empty when every existing monitor carries no infrastructure config", () => {
|
|
232
|
+
/*
|
|
233
|
+
* A project's non-infrastructure monitors (HTTP, Ping, ...) are handed to
|
|
234
|
+
* this method too. A fingerprint of `("", [], [])` for all of them would
|
|
235
|
+
* collide with any recommendation that also produced an empty
|
|
236
|
+
* fingerprint, marking real work as already done.
|
|
237
|
+
*/
|
|
238
|
+
const covered: Map<string, ObjectID> =
|
|
239
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
240
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
241
|
+
existingMonitors: [
|
|
242
|
+
{ monitorId: ObjectID.generate(), monitorSteps: [new MonitorStep()] },
|
|
243
|
+
{
|
|
244
|
+
monitorId: ObjectID.generate(),
|
|
245
|
+
monitorSteps: [buildNonInfrastructureStep()],
|
|
246
|
+
},
|
|
247
|
+
{ monitorId: ObjectID.generate(), monitorSteps: [] },
|
|
248
|
+
],
|
|
249
|
+
args: buildArgs(),
|
|
250
|
+
});
|
|
251
|
+
|
|
252
|
+
expect(covered.size).toBe(0);
|
|
253
|
+
});
|
|
254
|
+
|
|
255
|
+
it("keeps the first monitor in the array when two monitors watch the same thing", () => {
|
|
256
|
+
/*
|
|
257
|
+
* Reachable by hand: create a recommendation, then create it again from a
|
|
258
|
+
* second browser tab that still shows the stale "not created" card. Both
|
|
259
|
+
* monitors exist and fingerprint identically. The API returns them sorted,
|
|
260
|
+
* so honouring the array order makes the link target stable across page
|
|
261
|
+
* loads instead of flipping with whatever row order Postgres felt like.
|
|
262
|
+
*/
|
|
263
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
264
|
+
const created: MonitorRecommendation = KUBERNETES_RECOMMENDATIONS[0]!;
|
|
265
|
+
|
|
266
|
+
const firstMonitor: ExistingRecommendationMonitor = buildMonitorFor(
|
|
267
|
+
[created],
|
|
268
|
+
args,
|
|
269
|
+
);
|
|
270
|
+
const secondMonitor: ExistingRecommendationMonitor = buildMonitorFor(
|
|
271
|
+
[created],
|
|
272
|
+
args,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
expect(
|
|
276
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
277
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
278
|
+
existingMonitors: [firstMonitor, secondMonitor],
|
|
279
|
+
args: args,
|
|
280
|
+
}).get(created.recommendationId),
|
|
281
|
+
).toBe(firstMonitor.monitorId);
|
|
282
|
+
|
|
283
|
+
/*
|
|
284
|
+
* Reversing the input flips the answer. Without this the assertion above
|
|
285
|
+
* would also pass an implementation that picked the LAST monitor and
|
|
286
|
+
* happened to be handed them in a lucky order.
|
|
287
|
+
*/
|
|
288
|
+
expect(
|
|
289
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
290
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
291
|
+
existingMonitors: [secondMonitor, firstMonitor],
|
|
292
|
+
args: args,
|
|
293
|
+
}).get(created.recommendationId),
|
|
294
|
+
).toBe(secondMonitor.monitorId);
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
it("credits every step of a monitor that has several", () => {
|
|
298
|
+
/*
|
|
299
|
+
* A monitor is not limited to one step, and nothing stops a user from
|
|
300
|
+
* hand-adding a second infrastructure step to a monitor the feature
|
|
301
|
+
* created. Only looking at step 0 would resurface an already-watched
|
|
302
|
+
* recommendation as "not created" and invite a duplicate monitor.
|
|
303
|
+
*/
|
|
304
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
305
|
+
const createdRecommendations: Array<MonitorRecommendation> =
|
|
306
|
+
KUBERNETES_RECOMMENDATIONS.slice(0, 3);
|
|
307
|
+
|
|
308
|
+
const multiStepMonitor: ExistingRecommendationMonitor = buildMonitorFor(
|
|
309
|
+
createdRecommendations,
|
|
310
|
+
args,
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
const covered: Map<string, ObjectID> =
|
|
314
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
315
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
316
|
+
existingMonitors: [multiStepMonitor],
|
|
317
|
+
args: args,
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
expect(createdRecommendations.length).toBe(3);
|
|
321
|
+
for (const recommendation of createdRecommendations) {
|
|
322
|
+
expect(covered.get(recommendation.recommendationId)).toBe(
|
|
323
|
+
multiStepMonitor.monitorId,
|
|
324
|
+
);
|
|
325
|
+
}
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it("still credits a real step sitting next to a non-infrastructure one", () => {
|
|
329
|
+
/*
|
|
330
|
+
* The undefined fingerprint of the unrelated step must be skipped, not
|
|
331
|
+
* treated as the monitor's answer and used to abandon the rest.
|
|
332
|
+
*/
|
|
333
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
334
|
+
const created: MonitorRecommendation = KUBERNETES_RECOMMENDATIONS[0]!;
|
|
335
|
+
const monitorId: ObjectID = ObjectID.generate();
|
|
336
|
+
|
|
337
|
+
const covered: Map<string, ObjectID> =
|
|
338
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
339
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
340
|
+
existingMonitors: [
|
|
341
|
+
{
|
|
342
|
+
monitorId: monitorId,
|
|
343
|
+
monitorSteps: [
|
|
344
|
+
buildNonInfrastructureStep(),
|
|
345
|
+
buildCreatedStep(created, args),
|
|
346
|
+
],
|
|
347
|
+
},
|
|
348
|
+
],
|
|
349
|
+
args: args,
|
|
350
|
+
});
|
|
351
|
+
|
|
352
|
+
expect(covered.get(created.recommendationId)).toBe(monitorId);
|
|
353
|
+
});
|
|
354
|
+
|
|
355
|
+
it("covers nothing when the existing monitors are scoped to another resource", () => {
|
|
356
|
+
/*
|
|
357
|
+
* Same recommendation, different cluster. Coverage is per resource: the
|
|
358
|
+
* cluster being viewed genuinely is not watched, and reporting otherwise
|
|
359
|
+
* would hide a real gap on every cluster after the first.
|
|
360
|
+
*/
|
|
361
|
+
const otherResourceMonitor: ExistingRecommendationMonitor = buildMonitorFor(
|
|
362
|
+
KUBERNETES_RECOMMENDATIONS,
|
|
363
|
+
buildArgs({ resourceIdentifier: "some-other-cluster" }),
|
|
364
|
+
);
|
|
365
|
+
|
|
366
|
+
const covered: Map<string, ObjectID> =
|
|
367
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
368
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
369
|
+
existingMonitors: [otherResourceMonitor],
|
|
370
|
+
args: buildArgs(),
|
|
371
|
+
});
|
|
372
|
+
|
|
373
|
+
expect(covered.size).toBe(0);
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
it("is unaffected by the on-call policies, labels and severities applied at create time", () => {
|
|
377
|
+
/*
|
|
378
|
+
* Monitors created through this feature have had notification settings
|
|
379
|
+
* written into their criteria; the recommendation the diff rebuilds has
|
|
380
|
+
* not. If any of that reached the fingerprint, every monitor created with
|
|
381
|
+
* an on-call policy would immediately show up as "not created" again.
|
|
382
|
+
*/
|
|
383
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
384
|
+
const created: MonitorRecommendation = KUBERNETES_RECOMMENDATIONS[0]!;
|
|
385
|
+
const monitorId: ObjectID = ObjectID.generate();
|
|
386
|
+
|
|
387
|
+
const existingStep: MonitorStep =
|
|
388
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
389
|
+
monitorStep: buildCreatedStep(created, args),
|
|
390
|
+
notificationSettings: {
|
|
391
|
+
onCallPolicyIds: [ObjectID.generate(), ObjectID.generate()],
|
|
392
|
+
labelIds: [ObjectID.generate()],
|
|
393
|
+
ownerTeamIds: [ObjectID.generate()],
|
|
394
|
+
ownerUserIds: [ObjectID.generate()],
|
|
395
|
+
incidentSeverityIdBySeverity: {
|
|
396
|
+
Critical: ObjectID.generate(),
|
|
397
|
+
Warning: ObjectID.generate(),
|
|
398
|
+
},
|
|
399
|
+
alertSeverityIdBySeverity: {
|
|
400
|
+
Critical: ObjectID.generate(),
|
|
401
|
+
Warning: ObjectID.generate(),
|
|
402
|
+
},
|
|
403
|
+
},
|
|
404
|
+
severity: created.severity,
|
|
405
|
+
});
|
|
406
|
+
|
|
407
|
+
const covered: Map<string, ObjectID> =
|
|
408
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
409
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
410
|
+
existingMonitors: [
|
|
411
|
+
{ monitorId: monitorId, monitorSteps: [existingStep] },
|
|
412
|
+
],
|
|
413
|
+
args: args,
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
expect(covered.get(created.recommendationId)).toBe(monitorId);
|
|
417
|
+
});
|
|
418
|
+
|
|
419
|
+
it("is unaffected by the notification mode the monitor was created with", () => {
|
|
420
|
+
/*
|
|
421
|
+
* The Alert / Incident / Both choice flips `createIncidents` and
|
|
422
|
+
* `createAlerts`, which decides what a breach OPENS — not what the
|
|
423
|
+
* monitor WATCHES. A monitor created as alert-only watches exactly the
|
|
424
|
+
* same metrics as one created as incident-only, so both must still cover
|
|
425
|
+
* their recommendation. Were the flags in the fingerprint, a user who
|
|
426
|
+
* picked "Alert" would be offered the same monitor again forever.
|
|
427
|
+
*/
|
|
428
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
429
|
+
const created: MonitorRecommendation = KUBERNETES_RECOMMENDATIONS[0]!;
|
|
430
|
+
|
|
431
|
+
const modes: Array<MonitorRecommendationNotificationMode> = [
|
|
432
|
+
MonitorRecommendationNotificationMode.Alert,
|
|
433
|
+
MonitorRecommendationNotificationMode.Incident,
|
|
434
|
+
MonitorRecommendationNotificationMode.Both,
|
|
435
|
+
];
|
|
436
|
+
|
|
437
|
+
for (const mode of modes) {
|
|
438
|
+
const notificationSettings: MonitorRecommendationNotificationSettings = {
|
|
439
|
+
notificationMode: mode,
|
|
440
|
+
};
|
|
441
|
+
|
|
442
|
+
const existingStep: MonitorStep =
|
|
443
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
444
|
+
monitorStep: buildCreatedStep(created, args),
|
|
445
|
+
notificationSettings: notificationSettings,
|
|
446
|
+
severity: created.severity,
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
const monitorId: ObjectID = ObjectID.generate();
|
|
450
|
+
|
|
451
|
+
const covered: Map<string, ObjectID> =
|
|
452
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
453
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
454
|
+
existingMonitors: [
|
|
455
|
+
{ monitorId: monitorId, monitorSteps: [existingStep] },
|
|
456
|
+
],
|
|
457
|
+
args: args,
|
|
458
|
+
});
|
|
459
|
+
|
|
460
|
+
expect(covered.get(created.recommendationId)).toBe(monitorId);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/*
|
|
464
|
+
* Guard against a vacuous version of the loop above: if the mode stopped
|
|
465
|
+
* changing anything on the step, every iteration would trivially pass
|
|
466
|
+
* while proving nothing. Alert mode must actually turn incident creation
|
|
467
|
+
* off somewhere on the step.
|
|
468
|
+
*/
|
|
469
|
+
const alertOnlyStep: MonitorStep =
|
|
470
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
471
|
+
monitorStep: buildCreatedStep(created, args),
|
|
472
|
+
notificationSettings: {
|
|
473
|
+
notificationMode: MonitorRecommendationNotificationMode.Alert,
|
|
474
|
+
},
|
|
475
|
+
severity: created.severity,
|
|
476
|
+
});
|
|
477
|
+
|
|
478
|
+
const bothStep: MonitorStep =
|
|
479
|
+
MonitorRecommendationUtil.applyNotificationSettingsToMonitorStep({
|
|
480
|
+
monitorStep: buildCreatedStep(created, args),
|
|
481
|
+
notificationSettings: {
|
|
482
|
+
notificationMode: MonitorRecommendationNotificationMode.Both,
|
|
483
|
+
},
|
|
484
|
+
severity: created.severity,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
const alertOnlyIncidentFlags: Array<boolean | undefined> =
|
|
488
|
+
getCriteriaInstances(alertOnlyStep).map(
|
|
489
|
+
(instance: MonitorCriteriaInstance) => {
|
|
490
|
+
return instance.data?.createIncidents;
|
|
491
|
+
},
|
|
492
|
+
);
|
|
493
|
+
|
|
494
|
+
const bothIncidentFlags: Array<boolean | undefined> = getCriteriaInstances(
|
|
495
|
+
bothStep,
|
|
496
|
+
).map((instance: MonitorCriteriaInstance) => {
|
|
497
|
+
return instance.data?.createIncidents;
|
|
498
|
+
});
|
|
499
|
+
|
|
500
|
+
expect(alertOnlyIncidentFlags).not.toEqual(bothIncidentFlags);
|
|
501
|
+
});
|
|
502
|
+
|
|
503
|
+
it("agrees exactly with getCoveredRecommendationIds for every resource type", () => {
|
|
504
|
+
/*
|
|
505
|
+
* The divergence this rules out: the side-menu badge counts the
|
|
506
|
+
* uncovered recommendations from one method and the page greys cards out
|
|
507
|
+
* using the other. Any difference in how the two walk the existing
|
|
508
|
+
* monitors — a `continue` in one and not the other, a different empty
|
|
509
|
+
* check — shows up as a badge that contradicts the page, with no error
|
|
510
|
+
* anywhere.
|
|
511
|
+
*
|
|
512
|
+
* Every other recommendation is "created", each on its own monitor, so
|
|
513
|
+
* both a covered and an uncovered answer are exercised for all eight
|
|
514
|
+
* resource types.
|
|
515
|
+
*/
|
|
516
|
+
expect(RESOURCE_TYPE_DEFINITIONS.length).toBe(8);
|
|
517
|
+
|
|
518
|
+
for (const definition of RESOURCE_TYPE_DEFINITIONS) {
|
|
519
|
+
const recommendations: Array<MonitorRecommendation> =
|
|
520
|
+
definition.getRecommendations();
|
|
521
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
522
|
+
|
|
523
|
+
const createdRecommendations: Array<MonitorRecommendation> =
|
|
524
|
+
recommendations.filter(
|
|
525
|
+
(_recommendation: MonitorRecommendation, index: number) => {
|
|
526
|
+
return index % 2 === 0;
|
|
527
|
+
},
|
|
528
|
+
);
|
|
529
|
+
|
|
530
|
+
const existingMonitors: Array<ExistingRecommendationMonitor> =
|
|
531
|
+
createdRecommendations.map((recommendation: MonitorRecommendation) => {
|
|
532
|
+
return buildMonitorFor([recommendation], args);
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
const coveredMap: Map<string, ObjectID> =
|
|
536
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
537
|
+
recommendations: recommendations,
|
|
538
|
+
existingMonitors: existingMonitors,
|
|
539
|
+
args: args,
|
|
540
|
+
});
|
|
541
|
+
|
|
542
|
+
const coveredSet: Set<string> =
|
|
543
|
+
MonitorRecommendationUtil.getCoveredRecommendationIds({
|
|
544
|
+
recommendations: recommendations,
|
|
545
|
+
existingMonitorSteps: flattenSteps(existingMonitors),
|
|
546
|
+
args: args,
|
|
547
|
+
});
|
|
548
|
+
|
|
549
|
+
expect(new Set<string>(coveredMap.keys())).toEqual(coveredSet);
|
|
550
|
+
|
|
551
|
+
// Non-vacuity: both answers must actually contain the created ones.
|
|
552
|
+
expect(createdRecommendations.length).toBeGreaterThan(0);
|
|
553
|
+
for (const recommendation of createdRecommendations) {
|
|
554
|
+
expect(coveredMap.has(recommendation.recommendationId)).toBe(true);
|
|
555
|
+
expect(coveredSet.has(recommendation.recommendationId)).toBe(true);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// ...and neither may claim everything, or agreement would be trivial.
|
|
559
|
+
expect(coveredMap.size).toBeLessThan(recommendations.length);
|
|
560
|
+
}
|
|
561
|
+
});
|
|
562
|
+
|
|
563
|
+
it("agrees with getCoveredRecommendationIds on the empty and unmatched cases", () => {
|
|
564
|
+
/*
|
|
565
|
+
* The two methods take their early-out on an empty fingerprint index
|
|
566
|
+
* separately. A mismatch here would mean a resource with only HTTP
|
|
567
|
+
* monitors renders one way in the badge and another on the page.
|
|
568
|
+
*/
|
|
569
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
570
|
+
|
|
571
|
+
const noiseMonitors: Array<ExistingRecommendationMonitor> = [
|
|
572
|
+
{ monitorId: ObjectID.generate(), monitorSteps: [] },
|
|
573
|
+
{
|
|
574
|
+
monitorId: ObjectID.generate(),
|
|
575
|
+
monitorSteps: [buildNonInfrastructureStep(), new MonitorStep()],
|
|
576
|
+
},
|
|
577
|
+
buildMonitorFor(
|
|
578
|
+
[KUBERNETES_RECOMMENDATIONS[0]!],
|
|
579
|
+
buildArgs({ resourceIdentifier: "unrelated-cluster" }),
|
|
580
|
+
),
|
|
581
|
+
];
|
|
582
|
+
|
|
583
|
+
for (const existingMonitors of [
|
|
584
|
+
[] as Array<ExistingRecommendationMonitor>,
|
|
585
|
+
noiseMonitors,
|
|
586
|
+
]) {
|
|
587
|
+
const coveredMap: Map<string, ObjectID> =
|
|
588
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
589
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
590
|
+
existingMonitors: existingMonitors,
|
|
591
|
+
args: args,
|
|
592
|
+
});
|
|
593
|
+
|
|
594
|
+
const coveredSet: Set<string> =
|
|
595
|
+
MonitorRecommendationUtil.getCoveredRecommendationIds({
|
|
596
|
+
recommendations: KUBERNETES_RECOMMENDATIONS,
|
|
597
|
+
existingMonitorSteps: flattenSteps(existingMonitors),
|
|
598
|
+
args: args,
|
|
599
|
+
});
|
|
600
|
+
|
|
601
|
+
expect(new Set<string>(coveredMap.keys())).toEqual(coveredSet);
|
|
602
|
+
expect(coveredMap.size).toBe(0);
|
|
603
|
+
}
|
|
604
|
+
});
|
|
605
|
+
|
|
606
|
+
it("resolves the right monitor id for every resource type in the catalog", () => {
|
|
607
|
+
/*
|
|
608
|
+
* Each resource type renames the identifier field on its way into the
|
|
609
|
+
* template module (clusterIdentifier / hostIdentifier / fleetIdentifier).
|
|
610
|
+
* A type whose rename is wrong produces a step scoped to "" on both sides
|
|
611
|
+
* of the diff, which still matches — so this asserts the id of the RIGHT
|
|
612
|
+
* monitor among two, which an all-empty fingerprint could not do: with
|
|
613
|
+
* every fingerprint collapsed to `("", [], [])` both recommendations would
|
|
614
|
+
* resolve to the first monitor and this fails.
|
|
615
|
+
*
|
|
616
|
+
* The pair is chosen by fingerprint rather than by taking index 0 and 1,
|
|
617
|
+
* because Proxmox and Ceph ship several templates that watch the same
|
|
618
|
+
* metric with different thresholds and therefore fingerprint identically
|
|
619
|
+
* — see `serializeFingerprint`, which reads metric names and formulas
|
|
620
|
+
* only. Those are genuinely indistinguishable to the diff (reported
|
|
621
|
+
* separately); this test is about the identifier plumbing, so it uses a
|
|
622
|
+
* pair the diff can tell apart.
|
|
623
|
+
*/
|
|
624
|
+
for (const definition of RESOURCE_TYPE_DEFINITIONS) {
|
|
625
|
+
const recommendations: Array<MonitorRecommendation> =
|
|
626
|
+
definition.getRecommendations();
|
|
627
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
628
|
+
|
|
629
|
+
const first: MonitorRecommendation = recommendations[0]!;
|
|
630
|
+
const firstFingerprint: string = getSerializedFingerprint(first, args);
|
|
631
|
+
|
|
632
|
+
const second: MonitorRecommendation | undefined = recommendations.find(
|
|
633
|
+
(recommendation: MonitorRecommendation) => {
|
|
634
|
+
return (
|
|
635
|
+
getSerializedFingerprint(recommendation, args) !== firstFingerprint
|
|
636
|
+
);
|
|
637
|
+
},
|
|
638
|
+
);
|
|
639
|
+
|
|
640
|
+
expect(second).toBeDefined();
|
|
641
|
+
|
|
642
|
+
const firstMonitor: ExistingRecommendationMonitor = buildMonitorFor(
|
|
643
|
+
[first],
|
|
644
|
+
args,
|
|
645
|
+
);
|
|
646
|
+
const secondMonitor: ExistingRecommendationMonitor = buildMonitorFor(
|
|
647
|
+
[second!],
|
|
648
|
+
args,
|
|
649
|
+
);
|
|
650
|
+
|
|
651
|
+
const covered: Map<string, ObjectID> =
|
|
652
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
653
|
+
recommendations: recommendations,
|
|
654
|
+
existingMonitors: [firstMonitor, secondMonitor],
|
|
655
|
+
args: args,
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
expect(covered.get(first.recommendationId)).toBe(firstMonitor.monitorId);
|
|
659
|
+
expect(covered.get(second!.recommendationId)).toBe(
|
|
660
|
+
secondMonitor.monitorId,
|
|
661
|
+
);
|
|
662
|
+
}
|
|
663
|
+
});
|
|
664
|
+
|
|
665
|
+
/*
|
|
666
|
+
* The side-menu badge (RecommendationsSideMenuItem) computes this same diff,
|
|
667
|
+
* and it does so WITHOUT fetching the project's monitor statuses or its
|
|
668
|
+
* incident and alert severities — it passes ObjectID.getZeroObjectID() for
|
|
669
|
+
* all four, because those ids are written into the criteria a template
|
|
670
|
+
* builds but never reach the fingerprint the diff compares. That saves four
|
|
671
|
+
* project-wide list requests on every page of every resource.
|
|
672
|
+
*
|
|
673
|
+
* It also means a change on the template side that DID let one of those ids
|
|
674
|
+
* into the metric config would make the badge disagree with the page it
|
|
675
|
+
* links to: the badge would find nothing covered and nag about
|
|
676
|
+
* recommendations the page shows as already created. Nothing about that
|
|
677
|
+
* failure is visible from either file, so the invariant is pinned here
|
|
678
|
+
* rather than described in a comment over there.
|
|
679
|
+
*/
|
|
680
|
+
it("ignores the monitor-status and severity args entirely", () => {
|
|
681
|
+
for (const definition of RESOURCE_TYPE_DEFINITIONS) {
|
|
682
|
+
const recommendations: Array<MonitorRecommendation> =
|
|
683
|
+
definition.getRecommendations();
|
|
684
|
+
|
|
685
|
+
const realArgs: MonitorRecommendationArgs = buildArgs();
|
|
686
|
+
|
|
687
|
+
const placeholderArgs: MonitorRecommendationArgs = buildArgs({
|
|
688
|
+
onlineMonitorStatusId: ObjectID.getZeroObjectID(),
|
|
689
|
+
offlineMonitorStatusId: ObjectID.getZeroObjectID(),
|
|
690
|
+
defaultIncidentSeverityId: ObjectID.getZeroObjectID(),
|
|
691
|
+
defaultAlertSeverityId: ObjectID.getZeroObjectID(),
|
|
692
|
+
});
|
|
693
|
+
|
|
694
|
+
/*
|
|
695
|
+
* The monitor was created with the project's real ids; the diff is run
|
|
696
|
+
* with placeholders. This is exactly the asymmetry the badge relies on.
|
|
697
|
+
*/
|
|
698
|
+
const existingMonitor: ExistingRecommendationMonitor = buildMonitorFor(
|
|
699
|
+
recommendations,
|
|
700
|
+
realArgs,
|
|
701
|
+
);
|
|
702
|
+
|
|
703
|
+
const coveredWithPlaceholders: Map<string, ObjectID> =
|
|
704
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
705
|
+
recommendations: recommendations,
|
|
706
|
+
existingMonitors: [existingMonitor],
|
|
707
|
+
args: placeholderArgs,
|
|
708
|
+
});
|
|
709
|
+
|
|
710
|
+
const coveredWithRealIds: Map<string, ObjectID> =
|
|
711
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
712
|
+
recommendations: recommendations,
|
|
713
|
+
existingMonitors: [existingMonitor],
|
|
714
|
+
args: realArgs,
|
|
715
|
+
});
|
|
716
|
+
|
|
717
|
+
expect(coveredWithPlaceholders.size).toBe(recommendations.length);
|
|
718
|
+
expect([...coveredWithPlaceholders.keys()].sort()).toEqual(
|
|
719
|
+
[...coveredWithRealIds.keys()].sort(),
|
|
720
|
+
);
|
|
721
|
+
}
|
|
722
|
+
});
|
|
723
|
+
/*
|
|
724
|
+
* Every shipped template must fingerprint differently from every other
|
|
725
|
+
* template FOR THE SAME RESOURCE TYPE. This is the property the whole
|
|
726
|
+
* already-created diff rests on, and it is not obvious: the fingerprint is
|
|
727
|
+
* derived, not declared, so two templates can collide without either author
|
|
728
|
+
* doing anything wrong.
|
|
729
|
+
*
|
|
730
|
+
* They did. Before the fingerprint was widened past (identifier, metric
|
|
731
|
+
* names, formulas), thirteen groups collided — eight Ceph templates share
|
|
732
|
+
* the single metric `ceph_health_detail` and differ only in the health-check
|
|
733
|
+
* name they filter on, and "Cluster Near Full" differs from "Cluster Full"
|
|
734
|
+
* only in its threshold. Creating "Ceph Daemon Crash" therefore marked
|
|
735
|
+
* clock-skew, OSD-nearfull, OSD-backfillfull and OSD-full as already
|
|
736
|
+
* created, hiding four real monitoring gaps behind cards that read as
|
|
737
|
+
* handled, and pointed each of their "View monitor" links at a monitor with
|
|
738
|
+
* unrelated thresholds.
|
|
739
|
+
*
|
|
740
|
+
* Nothing about that is visible from the page, from the templates, or from
|
|
741
|
+
* any other test. It is caught here or not at all.
|
|
742
|
+
*/
|
|
743
|
+
it("gives every template a distinct fingerprint within its resource type", () => {
|
|
744
|
+
for (const definition of RESOURCE_TYPE_DEFINITIONS) {
|
|
745
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
746
|
+
|
|
747
|
+
const idsByFingerprint: Map<string, Array<string>> = new Map<
|
|
748
|
+
string,
|
|
749
|
+
Array<string>
|
|
750
|
+
>();
|
|
751
|
+
|
|
752
|
+
for (const recommendation of definition.getRecommendations()) {
|
|
753
|
+
const fingerprint: string = getSerializedFingerprint(
|
|
754
|
+
recommendation,
|
|
755
|
+
args,
|
|
756
|
+
);
|
|
757
|
+
|
|
758
|
+
const ids: Array<string> = idsByFingerprint.get(fingerprint) || [];
|
|
759
|
+
ids.push(recommendation.recommendationId);
|
|
760
|
+
idsByFingerprint.set(fingerprint, ids);
|
|
761
|
+
}
|
|
762
|
+
|
|
763
|
+
const collisions: Array<Array<string>> = [
|
|
764
|
+
...idsByFingerprint.values(),
|
|
765
|
+
].filter((ids: Array<string>) => {
|
|
766
|
+
return ids.length > 1;
|
|
767
|
+
});
|
|
768
|
+
|
|
769
|
+
/*
|
|
770
|
+
* Asserted as the colliding ids rather than as a count, so a failure
|
|
771
|
+
* names the two templates that need separating instead of saying "3".
|
|
772
|
+
*/
|
|
773
|
+
expect(collisions).toEqual([]);
|
|
774
|
+
}
|
|
775
|
+
});
|
|
776
|
+
|
|
777
|
+
/*
|
|
778
|
+
* The same property across resource types. The page never mixes them — it
|
|
779
|
+
* queries monitors by monitorType — but nothing in the fingerprint enforced
|
|
780
|
+
* that, and Docker, Docker Swarm and Podman ship byte-identical metric
|
|
781
|
+
* configs ("container.cpu.utilization" and friends). A future caller that
|
|
782
|
+
* diffs across types, or a resource type that starts accepting another's
|
|
783
|
+
* monitor type, would silently mark half its recommendations as handled.
|
|
784
|
+
*/
|
|
785
|
+
it("gives every template in the whole catalog a distinct fingerprint", () => {
|
|
786
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
787
|
+
|
|
788
|
+
const idsByFingerprint: Map<string, Array<string>> = new Map<
|
|
789
|
+
string,
|
|
790
|
+
Array<string>
|
|
791
|
+
>();
|
|
792
|
+
|
|
793
|
+
for (const recommendation of MonitorRecommendationCatalog.getAllRecommendations()) {
|
|
794
|
+
const fingerprint: string = getSerializedFingerprint(
|
|
795
|
+
recommendation,
|
|
796
|
+
args,
|
|
797
|
+
);
|
|
798
|
+
|
|
799
|
+
const ids: Array<string> = idsByFingerprint.get(fingerprint) || [];
|
|
800
|
+
ids.push(recommendation.recommendationId);
|
|
801
|
+
idsByFingerprint.set(fingerprint, ids);
|
|
802
|
+
}
|
|
803
|
+
|
|
804
|
+
const collisions: Array<Array<string>> = [
|
|
805
|
+
...idsByFingerprint.values(),
|
|
806
|
+
].filter((ids: Array<string>) => {
|
|
807
|
+
return ids.length > 1;
|
|
808
|
+
});
|
|
809
|
+
|
|
810
|
+
expect(collisions).toEqual([]);
|
|
811
|
+
});
|
|
812
|
+
|
|
813
|
+
/*
|
|
814
|
+
* The concrete regressions, named. The tests above would catch these too,
|
|
815
|
+
* but they would report them as an anonymous collision list; these say what
|
|
816
|
+
* broke and what the user loses when it does.
|
|
817
|
+
*/
|
|
818
|
+
it("does not let one Ceph health-detail template cover the others", () => {
|
|
819
|
+
const cephRecommendations: Array<MonitorRecommendation> =
|
|
820
|
+
MonitorRecommendationCatalog.getRecommendations(
|
|
821
|
+
MonitorRecommendationResourceType.Ceph,
|
|
822
|
+
);
|
|
823
|
+
|
|
824
|
+
const daemonCrash: MonitorRecommendation | undefined =
|
|
825
|
+
cephRecommendations.find((recommendation: MonitorRecommendation) => {
|
|
826
|
+
return recommendation.templateId === "ceph-daemon-crash";
|
|
827
|
+
});
|
|
828
|
+
|
|
829
|
+
expect(daemonCrash).toBeDefined();
|
|
830
|
+
|
|
831
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
832
|
+
|
|
833
|
+
const covered: Map<string, ObjectID> =
|
|
834
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
835
|
+
recommendations: cephRecommendations,
|
|
836
|
+
existingMonitors: [buildMonitorFor([daemonCrash!], args)],
|
|
837
|
+
args: args,
|
|
838
|
+
});
|
|
839
|
+
|
|
840
|
+
expect([...covered.keys()]).toEqual([daemonCrash!.recommendationId]);
|
|
841
|
+
});
|
|
842
|
+
|
|
843
|
+
it("does not let a near-full threshold cover the full threshold", () => {
|
|
844
|
+
const cephRecommendations: Array<MonitorRecommendation> =
|
|
845
|
+
MonitorRecommendationCatalog.getRecommendations(
|
|
846
|
+
MonitorRecommendationResourceType.Ceph,
|
|
847
|
+
);
|
|
848
|
+
|
|
849
|
+
const nearFull: MonitorRecommendation | undefined =
|
|
850
|
+
cephRecommendations.find((recommendation: MonitorRecommendation) => {
|
|
851
|
+
return recommendation.templateId === "ceph-cluster-near-full";
|
|
852
|
+
});
|
|
853
|
+
const full: MonitorRecommendation | undefined = cephRecommendations.find(
|
|
854
|
+
(recommendation: MonitorRecommendation) => {
|
|
855
|
+
return recommendation.templateId === "ceph-cluster-full";
|
|
856
|
+
},
|
|
857
|
+
);
|
|
858
|
+
|
|
859
|
+
expect(nearFull).toBeDefined();
|
|
860
|
+
expect(full).toBeDefined();
|
|
861
|
+
|
|
862
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
863
|
+
|
|
864
|
+
const covered: Map<string, ObjectID> =
|
|
865
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
866
|
+
recommendations: cephRecommendations,
|
|
867
|
+
existingMonitors: [buildMonitorFor([nearFull!], args)],
|
|
868
|
+
args: args,
|
|
869
|
+
});
|
|
870
|
+
|
|
871
|
+
expect(covered.has(nearFull!.recommendationId)).toBe(true);
|
|
872
|
+
expect(covered.has(full!.recommendationId)).toBe(false);
|
|
873
|
+
});
|
|
874
|
+
|
|
875
|
+
it("does not let a Docker monitor cover the identical Podman recommendation", () => {
|
|
876
|
+
/*
|
|
877
|
+
* Docker and Podman ship the same metric names, the same aliases and the
|
|
878
|
+
* same thresholds for container CPU. Only the step's own config kind
|
|
879
|
+
* (dockerMonitor vs podmanMonitor) tells them apart.
|
|
880
|
+
*/
|
|
881
|
+
const args: MonitorRecommendationArgs = buildArgs();
|
|
882
|
+
|
|
883
|
+
const dockerRecommendations: Array<MonitorRecommendation> =
|
|
884
|
+
MonitorRecommendationCatalog.getRecommendations(
|
|
885
|
+
MonitorRecommendationResourceType.Docker,
|
|
886
|
+
);
|
|
887
|
+
const podmanRecommendations: Array<MonitorRecommendation> =
|
|
888
|
+
MonitorRecommendationCatalog.getRecommendations(
|
|
889
|
+
MonitorRecommendationResourceType.Podman,
|
|
890
|
+
);
|
|
891
|
+
|
|
892
|
+
const covered: Map<string, ObjectID> =
|
|
893
|
+
MonitorRecommendationUtil.getCoveredRecommendationMonitorIds({
|
|
894
|
+
recommendations: podmanRecommendations,
|
|
895
|
+
existingMonitors: [buildMonitorFor(dockerRecommendations, args)],
|
|
896
|
+
args: args,
|
|
897
|
+
});
|
|
898
|
+
|
|
899
|
+
expect(covered.size).toBe(0);
|
|
900
|
+
});
|
|
901
|
+
});
|