@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,743 @@
|
|
|
1
|
+
import ObjectID from "../../ObjectID";
|
|
2
|
+
import MetricsViewConfig from "../../Metrics/MetricsViewConfig";
|
|
3
|
+
import MetricQueryConfigData from "../../Metrics/MetricQueryConfigData";
|
|
4
|
+
import MetricFormulaConfigData from "../../Metrics/MetricFormulaConfigData";
|
|
5
|
+
import MonitorStep from "../MonitorStep";
|
|
6
|
+
import MonitorSteps from "../MonitorSteps";
|
|
7
|
+
import MonitorCriteriaInstance from "../MonitorCriteriaInstance";
|
|
8
|
+
import { CriteriaIncident } from "../CriteriaIncident";
|
|
9
|
+
import { CriteriaAlert } from "../CriteriaAlert";
|
|
10
|
+
import MonitorRecommendationSeverityMapper from "./MonitorRecommendationSeverityMapper";
|
|
11
|
+
import {
|
|
12
|
+
MonitorRecommendation,
|
|
13
|
+
MonitorRecommendationArgs,
|
|
14
|
+
MonitorRecommendationNotificationMode,
|
|
15
|
+
MonitorRecommendationNotificationSettings,
|
|
16
|
+
MonitorRecommendationSeverity,
|
|
17
|
+
} from "./MonitorRecommendationTypes";
|
|
18
|
+
|
|
19
|
+
/*
|
|
20
|
+
* The structural slice of every `MonitorStep<X>Monitor` this module reads.
|
|
21
|
+
*
|
|
22
|
+
* All eight infrastructure step configs carry a `metricViewConfig` plus
|
|
23
|
+
* exactly one identifier field, whose name varies by resource type
|
|
24
|
+
* (see `MonitorRecommendationTypes.MonitorRecommendationArgs`). Reading all
|
|
25
|
+
* three optional names keeps the fingerprint logic resource-type-agnostic
|
|
26
|
+
* without needing a per-type switch.
|
|
27
|
+
*/
|
|
28
|
+
interface InfrastructureMonitorStepConfig {
|
|
29
|
+
clusterIdentifier?: string | undefined;
|
|
30
|
+
hostIdentifier?: string | undefined;
|
|
31
|
+
fleetIdentifier?: string | undefined;
|
|
32
|
+
metricViewConfig?: MetricsViewConfig | undefined;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/*
|
|
36
|
+
* What uniquely identifies "this alert is already being watched", derived
|
|
37
|
+
* from a monitor step rather than from the monitor's name.
|
|
38
|
+
*
|
|
39
|
+
* Name matching was the obvious alternative and is wrong: renaming a monitor
|
|
40
|
+
* would make an already-created recommendation reappear as un-created, and
|
|
41
|
+
* two different templates on the same resource can be renamed into collision.
|
|
42
|
+
* What a step actually WATCHES is the right identity, and every component
|
|
43
|
+
* below is deterministic per template.
|
|
44
|
+
*
|
|
45
|
+
* The first version of this used only (resourceIdentifier, metricNames,
|
|
46
|
+
* formulas), which turned out to be far too coarse: thirteen groups of
|
|
47
|
+
* genuinely different templates fingerprinted identically. Eight of the Ceph
|
|
48
|
+
* templates all query the single metric `ceph_health_detail` and differ only
|
|
49
|
+
* by the health-check name they filter on, so creating "Ceph Daemon Crash"
|
|
50
|
+
* marked four unrelated recommendations — clock skew, OSD nearfull,
|
|
51
|
+
* backfillfull, full — as "already created" and silently hid four real
|
|
52
|
+
* monitoring gaps. "Cluster Near Full" hid "Cluster Full" the same way. The
|
|
53
|
+
* failure is invisible by construction: the cards render as handled.
|
|
54
|
+
*
|
|
55
|
+
* So the fingerprint now carries everything that distinguishes one shipped
|
|
56
|
+
* template from another:
|
|
57
|
+
*
|
|
58
|
+
* configKind which infrastructure config the step carries. Docker,
|
|
59
|
+
* Docker Swarm and Podman ship byte-identical metric sets
|
|
60
|
+
* ("container.cpu.utilization"), and while the page never
|
|
61
|
+
* mixes resource types today, nothing in this function
|
|
62
|
+
* enforced that.
|
|
63
|
+
* metricAliases the per-query variable name. Author-chosen per template
|
|
64
|
+
* ("recent_crash", "mon_clock_skew"), and the thing the
|
|
65
|
+
* criteria filters actually reference.
|
|
66
|
+
* queryAttributes the attribute filter on each query, e.g.
|
|
67
|
+
* { name: "PG_DAMAGED" } — the only difference between
|
|
68
|
+
* several Ceph health-detail templates.
|
|
69
|
+
* criteriaFilters the thresholds. The last resort, and the only thing that
|
|
70
|
+
* separates "near full" (85%) from "full" (95%).
|
|
71
|
+
*
|
|
72
|
+
* Including thresholds has a cost worth stating: retuning a created monitor
|
|
73
|
+
* from 90% to 85% makes its recommendation resurface as not-yet-created. That
|
|
74
|
+
* is the correct reading — the monitor no longer watches what the
|
|
75
|
+
* recommendation describes — it is visible, and it is dismissable. Silently
|
|
76
|
+
* hiding a Critical recommendation is neither.
|
|
77
|
+
*
|
|
78
|
+
* Existing monitors keep matching: none of these components is touched by the
|
|
79
|
+
* notification settings the create flow writes, so a monitor created before
|
|
80
|
+
* this change still fingerprints to its own template.
|
|
81
|
+
*/
|
|
82
|
+
export interface MonitorRecommendationFingerprint {
|
|
83
|
+
resourceIdentifier: string;
|
|
84
|
+
configKind: string;
|
|
85
|
+
metricNames: Array<string>;
|
|
86
|
+
formulas: Array<string>;
|
|
87
|
+
metricAliases: Array<string>;
|
|
88
|
+
queryAttributes: Array<string>;
|
|
89
|
+
criteriaFilters: Array<string>;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export default class MonitorRecommendationUtil {
|
|
93
|
+
/*
|
|
94
|
+
* Deterministic monitor name for a recommendation applied to a resource.
|
|
95
|
+
*
|
|
96
|
+
* Used both as the created monitor's name and as the `monitorName` arg the
|
|
97
|
+
* template modules interpolate into their incident/alert titles.
|
|
98
|
+
*/
|
|
99
|
+
public static getMonitorName(data: {
|
|
100
|
+
recommendation: MonitorRecommendation;
|
|
101
|
+
resourceDisplayName: string;
|
|
102
|
+
}): string {
|
|
103
|
+
const resourceName: string = data.resourceDisplayName.trim();
|
|
104
|
+
|
|
105
|
+
if (!resourceName) {
|
|
106
|
+
return data.recommendation.name;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return `${resourceName} - ${data.recommendation.name}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/*
|
|
113
|
+
* Push the user's chosen on-call policies, owners, labels, severities and
|
|
114
|
+
* incident-vs-alert choice into every criteria instance in a step.
|
|
115
|
+
*
|
|
116
|
+
* This is the whole reason the create form asks for on-call policies: every
|
|
117
|
+
* shipped template hardcodes `onCallPolicyIds: []`, so a template-created
|
|
118
|
+
* monitor would open incidents that page nobody. Templates are the source
|
|
119
|
+
* of the thresholds; this is the source of "who hears about it, how loudly,
|
|
120
|
+
* and through which record".
|
|
121
|
+
*
|
|
122
|
+
* `severity` is the RECOMMENDATION's severity (`Critical` / `Warning`), not
|
|
123
|
+
* a project severity. It selects which project severity to write, via the
|
|
124
|
+
* caller's mapping — see `MonitorRecommendationSeverityMapper`.
|
|
125
|
+
*
|
|
126
|
+
* Mutates and returns the step (the step was just built by the template and
|
|
127
|
+
* is not shared with anything else).
|
|
128
|
+
*/
|
|
129
|
+
public static applyNotificationSettingsToMonitorStep(data: {
|
|
130
|
+
monitorStep: MonitorStep;
|
|
131
|
+
notificationSettings: MonitorRecommendationNotificationSettings;
|
|
132
|
+
severity: MonitorRecommendationSeverity;
|
|
133
|
+
}): MonitorStep {
|
|
134
|
+
const settings: MonitorRecommendationNotificationSettings =
|
|
135
|
+
data.notificationSettings;
|
|
136
|
+
|
|
137
|
+
const incidentSeverityId: ObjectID | undefined =
|
|
138
|
+
MonitorRecommendationSeverityMapper.resolveSeverityId({
|
|
139
|
+
severity: data.severity,
|
|
140
|
+
severityMap: settings.incidentSeverityIdBySeverity,
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
const alertSeverityId: ObjectID | undefined =
|
|
144
|
+
MonitorRecommendationSeverityMapper.resolveSeverityId({
|
|
145
|
+
severity: data.severity,
|
|
146
|
+
severityMap: settings.alertSeverityIdBySeverity,
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
const criteriaInstances: Array<MonitorCriteriaInstance> =
|
|
150
|
+
data.monitorStep.data?.monitorCriteria?.data
|
|
151
|
+
?.monitorCriteriaInstanceArray || [];
|
|
152
|
+
|
|
153
|
+
for (const criteriaInstance of criteriaInstances) {
|
|
154
|
+
if (!criteriaInstance.data) {
|
|
155
|
+
continue;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
for (const incident of criteriaInstance.data.incidents || []) {
|
|
159
|
+
this.applyToCriteriaIncident(incident, settings, incidentSeverityId);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
for (const alert of criteriaInstance.data.alerts || []) {
|
|
163
|
+
this.applyToCriteriaAlert(alert, settings, alertSeverityId);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
this.applyNotificationModeToCriteriaInstance(
|
|
167
|
+
criteriaInstance,
|
|
168
|
+
settings.notificationMode,
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return data.monitorStep;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/*
|
|
176
|
+
* Turn the user's Alert / Incident / Both choice into the two flags the
|
|
177
|
+
* monitor evaluator actually reads (`MonitorIncident.ts` and
|
|
178
|
+
* `MonitorAlert.ts` both early-return unless the flag is true).
|
|
179
|
+
*
|
|
180
|
+
* Two things this deliberately does NOT do:
|
|
181
|
+
*
|
|
182
|
+
* 1. It never flips a flag on a criteria instance whose corresponding
|
|
183
|
+
* array is empty. Every template ships a "Healthy" recovery criteria
|
|
184
|
+
* with `incidents: []` and `createIncidents: false`; setting the flag
|
|
185
|
+
* there would mark it as incident-creating, which the auto-resolve path
|
|
186
|
+
* reads as "this criteria contributes breaches" and would stop
|
|
187
|
+
* recovered monitors from resolving their own incidents.
|
|
188
|
+
*
|
|
189
|
+
* 2. It never empties the arrays of the mode that was NOT chosen. The
|
|
190
|
+
* config stays on the monitor, inert, exactly like the monitor criteria
|
|
191
|
+
* form's own toggles behave — so a user who later decides they do want
|
|
192
|
+
* incidents flips one switch on the monitor instead of re-authoring the
|
|
193
|
+
* incident title, description and severity.
|
|
194
|
+
*/
|
|
195
|
+
private static applyNotificationModeToCriteriaInstance(
|
|
196
|
+
criteriaInstance: MonitorCriteriaInstance,
|
|
197
|
+
notificationMode: MonitorRecommendationNotificationMode | undefined,
|
|
198
|
+
): void {
|
|
199
|
+
if (!notificationMode || !criteriaInstance.data) {
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
const shouldCreateIncidents: boolean =
|
|
204
|
+
notificationMode === MonitorRecommendationNotificationMode.Incident ||
|
|
205
|
+
notificationMode === MonitorRecommendationNotificationMode.Both;
|
|
206
|
+
|
|
207
|
+
const shouldCreateAlerts: boolean =
|
|
208
|
+
notificationMode === MonitorRecommendationNotificationMode.Alert ||
|
|
209
|
+
notificationMode === MonitorRecommendationNotificationMode.Both;
|
|
210
|
+
|
|
211
|
+
if ((criteriaInstance.data.incidents || []).length > 0) {
|
|
212
|
+
criteriaInstance.data.createIncidents = shouldCreateIncidents;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
if ((criteriaInstance.data.alerts || []).length > 0) {
|
|
216
|
+
criteriaInstance.data.createAlerts = shouldCreateAlerts;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
private static applyToCriteriaIncident(
|
|
221
|
+
incident: CriteriaIncident,
|
|
222
|
+
settings: MonitorRecommendationNotificationSettings,
|
|
223
|
+
incidentSeverityId: ObjectID | undefined,
|
|
224
|
+
): void {
|
|
225
|
+
if (settings.onCallPolicyIds && settings.onCallPolicyIds.length > 0) {
|
|
226
|
+
incident.onCallPolicyIds = [...settings.onCallPolicyIds];
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (settings.labelIds && settings.labelIds.length > 0) {
|
|
230
|
+
incident.labelIds = [...settings.labelIds];
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
if (settings.ownerTeamIds && settings.ownerTeamIds.length > 0) {
|
|
234
|
+
incident.ownerTeamIds = [...settings.ownerTeamIds];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
if (settings.ownerUserIds && settings.ownerUserIds.length > 0) {
|
|
238
|
+
incident.ownerUserIds = [...settings.ownerUserIds];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
if (incidentSeverityId) {
|
|
242
|
+
incident.incidentSeverityId = incidentSeverityId;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
private static applyToCriteriaAlert(
|
|
247
|
+
alert: CriteriaAlert,
|
|
248
|
+
settings: MonitorRecommendationNotificationSettings,
|
|
249
|
+
alertSeverityId: ObjectID | undefined,
|
|
250
|
+
): void {
|
|
251
|
+
if (settings.onCallPolicyIds && settings.onCallPolicyIds.length > 0) {
|
|
252
|
+
alert.onCallPolicyIds = [...settings.onCallPolicyIds];
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
if (settings.labelIds && settings.labelIds.length > 0) {
|
|
256
|
+
alert.labelIds = [...settings.labelIds];
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
if (settings.ownerTeamIds && settings.ownerTeamIds.length > 0) {
|
|
260
|
+
alert.ownerTeamIds = [...settings.ownerTeamIds];
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
if (settings.ownerUserIds && settings.ownerUserIds.length > 0) {
|
|
264
|
+
alert.ownerUserIds = [...settings.ownerUserIds];
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (alertSeverityId) {
|
|
268
|
+
alert.alertSeverityId = alertSeverityId;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/*
|
|
273
|
+
* Build the complete `MonitorSteps` for one recommendation, ready to hang
|
|
274
|
+
* off a `Monitor` and POST.
|
|
275
|
+
*
|
|
276
|
+
* `defaultMonitorStatusId` is separate from the online/offline ids in
|
|
277
|
+
* `args` because `MonitorSteps.getValidationError` requires it and the
|
|
278
|
+
* template modules never set it — they only produce the inner step.
|
|
279
|
+
*/
|
|
280
|
+
public static buildMonitorSteps(data: {
|
|
281
|
+
recommendation: MonitorRecommendation;
|
|
282
|
+
args: MonitorRecommendationArgs;
|
|
283
|
+
defaultMonitorStatusId: ObjectID;
|
|
284
|
+
notificationSettings?:
|
|
285
|
+
| MonitorRecommendationNotificationSettings
|
|
286
|
+
| undefined;
|
|
287
|
+
}): MonitorSteps {
|
|
288
|
+
const monitorStep: MonitorStep = data.recommendation.getMonitorStep(
|
|
289
|
+
data.args,
|
|
290
|
+
);
|
|
291
|
+
|
|
292
|
+
if (data.notificationSettings) {
|
|
293
|
+
this.applyNotificationSettingsToMonitorStep({
|
|
294
|
+
monitorStep: monitorStep,
|
|
295
|
+
notificationSettings: data.notificationSettings,
|
|
296
|
+
severity: data.recommendation.severity,
|
|
297
|
+
});
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const monitorSteps: MonitorSteps = new MonitorSteps();
|
|
301
|
+
|
|
302
|
+
monitorSteps.data = {
|
|
303
|
+
monitorStepsInstanceArray: [monitorStep],
|
|
304
|
+
defaultMonitorStatusId: data.defaultMonitorStatusId,
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
return monitorSteps;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
/*
|
|
311
|
+
* Reduce a monitor step to what it watches. Returns undefined for steps that
|
|
312
|
+
* carry no infrastructure config (an HTTP monitor step, say) — those can
|
|
313
|
+
* never match a recommendation.
|
|
314
|
+
*
|
|
315
|
+
* See `MonitorRecommendationFingerprint` for why each component is here.
|
|
316
|
+
*/
|
|
317
|
+
public static getFingerprintFromMonitorStep(
|
|
318
|
+
monitorStep: MonitorStep,
|
|
319
|
+
): MonitorRecommendationFingerprint | undefined {
|
|
320
|
+
const configKind: string | undefined =
|
|
321
|
+
this.getInfrastructureConfigKind(monitorStep);
|
|
322
|
+
|
|
323
|
+
if (!configKind) {
|
|
324
|
+
return undefined;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const config: InfrastructureMonitorStepConfig = (
|
|
328
|
+
monitorStep.data as unknown as Record<
|
|
329
|
+
string,
|
|
330
|
+
InfrastructureMonitorStepConfig
|
|
331
|
+
>
|
|
332
|
+
)[configKind]!;
|
|
333
|
+
|
|
334
|
+
const resourceIdentifier: string =
|
|
335
|
+
config.clusterIdentifier ||
|
|
336
|
+
config.hostIdentifier ||
|
|
337
|
+
config.fleetIdentifier ||
|
|
338
|
+
"";
|
|
339
|
+
|
|
340
|
+
const metricNames: Array<string> = [];
|
|
341
|
+
const metricAliases: Array<string> = [];
|
|
342
|
+
const queryAttributes: Array<string> = [];
|
|
343
|
+
const formulas: Array<string> = [];
|
|
344
|
+
|
|
345
|
+
for (const queryConfig of config.metricViewConfig?.queryConfigs || []) {
|
|
346
|
+
const metricName: string | undefined =
|
|
347
|
+
this.getMetricNameFromQueryConfig(queryConfig);
|
|
348
|
+
|
|
349
|
+
if (metricName) {
|
|
350
|
+
metricNames.push(metricName);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
const metricAlias: string | undefined =
|
|
354
|
+
queryConfig.metricAliasData?.metricVariable;
|
|
355
|
+
|
|
356
|
+
if (metricAlias) {
|
|
357
|
+
metricAliases.push(metricAlias);
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
queryAttributes.push(this.getQueryAttributeKey(queryConfig));
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
for (const formulaConfig of config.metricViewConfig?.formulaConfigs || []) {
|
|
364
|
+
const formula: string | undefined =
|
|
365
|
+
this.getFormulaFromFormulaConfig(formulaConfig);
|
|
366
|
+
|
|
367
|
+
if (formula) {
|
|
368
|
+
formulas.push(formula);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
return {
|
|
373
|
+
resourceIdentifier: resourceIdentifier,
|
|
374
|
+
configKind: configKind,
|
|
375
|
+
/*
|
|
376
|
+
* Every list is sorted so that declaration order inside a template is
|
|
377
|
+
* not part of the identity — a template author reordering two queries
|
|
378
|
+
* must not orphan every monitor already created from it.
|
|
379
|
+
*/
|
|
380
|
+
metricNames: metricNames.sort(),
|
|
381
|
+
formulas: formulas.sort(),
|
|
382
|
+
metricAliases: metricAliases.sort(),
|
|
383
|
+
queryAttributes: queryAttributes.sort(),
|
|
384
|
+
criteriaFilters: this.getCriteriaFilterKeys(monitorStep).sort(),
|
|
385
|
+
};
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
/*
|
|
389
|
+
* Which infrastructure config the step carries, as the property name.
|
|
390
|
+
*
|
|
391
|
+
* Returned instead of the config object itself because the NAME is part of
|
|
392
|
+
* the fingerprint: Docker, Docker Swarm and Podman ship structurally
|
|
393
|
+
* identical metric configs, so a Docker monitor and a Podman monitor
|
|
394
|
+
* watching container CPU are indistinguishable by contents alone.
|
|
395
|
+
*/
|
|
396
|
+
private static getInfrastructureConfigKind(
|
|
397
|
+
monitorStep: MonitorStep,
|
|
398
|
+
): string | undefined {
|
|
399
|
+
if (!monitorStep.data) {
|
|
400
|
+
return undefined;
|
|
401
|
+
}
|
|
402
|
+
|
|
403
|
+
const kinds: Array<string> = [
|
|
404
|
+
"kubernetesMonitor",
|
|
405
|
+
"hostMonitor",
|
|
406
|
+
"dockerMonitor",
|
|
407
|
+
"dockerSwarmMonitor",
|
|
408
|
+
"podmanMonitor",
|
|
409
|
+
"proxmoxMonitor",
|
|
410
|
+
"cephMonitor",
|
|
411
|
+
"iotMonitor",
|
|
412
|
+
];
|
|
413
|
+
|
|
414
|
+
const data: Record<string, unknown> = monitorStep.data as unknown as Record<
|
|
415
|
+
string,
|
|
416
|
+
unknown
|
|
417
|
+
>;
|
|
418
|
+
|
|
419
|
+
return kinds.find((kind: string) => {
|
|
420
|
+
return Boolean(data[kind]);
|
|
421
|
+
});
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
/*
|
|
425
|
+
* The attribute filter on one query, as a stable string.
|
|
426
|
+
*
|
|
427
|
+
* Keys are sorted before serializing: `{ name: "X", severity: "Y" }` and
|
|
428
|
+
* `{ severity: "Y", name: "X" }` describe the same query, and JSON.stringify
|
|
429
|
+
* alone would call them different.
|
|
430
|
+
*/
|
|
431
|
+
private static getQueryAttributeKey(
|
|
432
|
+
queryConfig: MetricQueryConfigData,
|
|
433
|
+
): string {
|
|
434
|
+
const attributes: unknown =
|
|
435
|
+
queryConfig.metricQueryData?.filterData?.["attributes"];
|
|
436
|
+
|
|
437
|
+
if (!attributes || typeof attributes !== "object") {
|
|
438
|
+
return "";
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
const record: Record<string, unknown> = attributes as Record<
|
|
442
|
+
string,
|
|
443
|
+
unknown
|
|
444
|
+
>;
|
|
445
|
+
|
|
446
|
+
return Object.keys(record)
|
|
447
|
+
.sort()
|
|
448
|
+
.map((key: string) => {
|
|
449
|
+
return `${key}=${String(record[key])}`;
|
|
450
|
+
})
|
|
451
|
+
.join(",");
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
/*
|
|
455
|
+
* Every threshold the step evaluates, as stable strings.
|
|
456
|
+
*
|
|
457
|
+
* Covers all criteria instances, healthy and unhealthy alike: the recovery
|
|
458
|
+
* criteria's own threshold is equally part of what the step watches, and two
|
|
459
|
+
* templates that differ only in their recovery bound are still two different
|
|
460
|
+
* monitors.
|
|
461
|
+
*
|
|
462
|
+
* `metricAlias` is included because a multi-query step's filters are only
|
|
463
|
+
* meaningful against the series they name.
|
|
464
|
+
*/
|
|
465
|
+
private static getCriteriaFilterKeys(
|
|
466
|
+
monitorStep: MonitorStep,
|
|
467
|
+
): Array<string> {
|
|
468
|
+
const keys: Array<string> = [];
|
|
469
|
+
|
|
470
|
+
const criteriaInstances: Array<MonitorCriteriaInstance> =
|
|
471
|
+
monitorStep.data?.monitorCriteria?.data?.monitorCriteriaInstanceArray ||
|
|
472
|
+
[];
|
|
473
|
+
|
|
474
|
+
for (const criteriaInstance of criteriaInstances) {
|
|
475
|
+
for (const filter of criteriaInstance.data?.filters || []) {
|
|
476
|
+
keys.push(
|
|
477
|
+
[
|
|
478
|
+
String(filter.checkOn ?? ""),
|
|
479
|
+
String(filter.filterType ?? ""),
|
|
480
|
+
String(filter.value ?? ""),
|
|
481
|
+
String(filter.metricMonitorOptions?.metricAlias ?? ""),
|
|
482
|
+
].join("|"),
|
|
483
|
+
);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
return keys;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
private static getMetricNameFromQueryConfig(
|
|
491
|
+
queryConfig: MetricQueryConfigData,
|
|
492
|
+
): string | undefined {
|
|
493
|
+
const metricName: unknown = queryConfig.metricQueryData?.filterData?.[
|
|
494
|
+
"metricName"
|
|
495
|
+
] as unknown;
|
|
496
|
+
|
|
497
|
+
if (typeof metricName === "string" && metricName) {
|
|
498
|
+
return metricName;
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return undefined;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
private static getFormulaFromFormulaConfig(
|
|
505
|
+
formulaConfig: MetricFormulaConfigData,
|
|
506
|
+
): string | undefined {
|
|
507
|
+
const formula: string | undefined =
|
|
508
|
+
formulaConfig.metricFormulaData?.metricFormula;
|
|
509
|
+
|
|
510
|
+
if (typeof formula === "string" && formula) {
|
|
511
|
+
return formula;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
return undefined;
|
|
515
|
+
}
|
|
516
|
+
|
|
517
|
+
/*
|
|
518
|
+
* A positional array rather than the object, so the serialization is stable
|
|
519
|
+
* regardless of the order the fields were written in. Every component of
|
|
520
|
+
* `MonitorRecommendationFingerprint` must appear here — a field added to the
|
|
521
|
+
* interface but forgotten below is invisible: the type checker is satisfied
|
|
522
|
+
* and the fingerprint silently keeps ignoring it, which is exactly the class
|
|
523
|
+
* of bug the interface's own comment describes.
|
|
524
|
+
*/
|
|
525
|
+
public static serializeFingerprint(
|
|
526
|
+
fingerprint: MonitorRecommendationFingerprint,
|
|
527
|
+
): string {
|
|
528
|
+
return JSON.stringify([
|
|
529
|
+
fingerprint.resourceIdentifier,
|
|
530
|
+
fingerprint.configKind,
|
|
531
|
+
fingerprint.metricNames,
|
|
532
|
+
fingerprint.formulas,
|
|
533
|
+
fingerprint.metricAliases,
|
|
534
|
+
fingerprint.queryAttributes,
|
|
535
|
+
fingerprint.criteriaFilters,
|
|
536
|
+
]);
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
public static areFingerprintsEqual(
|
|
540
|
+
a: MonitorRecommendationFingerprint | undefined,
|
|
541
|
+
b: MonitorRecommendationFingerprint | undefined,
|
|
542
|
+
): boolean {
|
|
543
|
+
if (!a || !b) {
|
|
544
|
+
return false;
|
|
545
|
+
}
|
|
546
|
+
|
|
547
|
+
return this.serializeFingerprint(a) === this.serializeFingerprint(b);
|
|
548
|
+
}
|
|
549
|
+
|
|
550
|
+
/*
|
|
551
|
+
* Which recommendations are already covered by monitors that exist.
|
|
552
|
+
*
|
|
553
|
+
* `existingMonitorSteps` is every step of every monitor already attached to
|
|
554
|
+
* this resource. A recommendation counts as covered when its freshly-built
|
|
555
|
+
* step fingerprints identically to one of them — i.e. same resource, same
|
|
556
|
+
* metrics, same formulas.
|
|
557
|
+
*
|
|
558
|
+
* `args` must use the same resource identifier the existing monitors were
|
|
559
|
+
* created with, otherwise nothing will ever match.
|
|
560
|
+
*/
|
|
561
|
+
public static getCoveredRecommendationIds(data: {
|
|
562
|
+
recommendations: Array<MonitorRecommendation>;
|
|
563
|
+
existingMonitorSteps: Array<MonitorStep>;
|
|
564
|
+
args: MonitorRecommendationArgs;
|
|
565
|
+
}): Set<string> {
|
|
566
|
+
const existingFingerprints: Set<string> = new Set<string>();
|
|
567
|
+
|
|
568
|
+
for (const monitorStep of data.existingMonitorSteps) {
|
|
569
|
+
const fingerprint: MonitorRecommendationFingerprint | undefined =
|
|
570
|
+
this.getFingerprintFromMonitorStep(monitorStep);
|
|
571
|
+
|
|
572
|
+
if (fingerprint) {
|
|
573
|
+
existingFingerprints.add(this.serializeFingerprint(fingerprint));
|
|
574
|
+
}
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
const covered: Set<string> = new Set<string>();
|
|
578
|
+
|
|
579
|
+
if (existingFingerprints.size === 0) {
|
|
580
|
+
return covered;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
for (const recommendation of data.recommendations) {
|
|
584
|
+
const fingerprint: string | undefined =
|
|
585
|
+
this.getSerializedFingerprintForRecommendation({
|
|
586
|
+
recommendation: recommendation,
|
|
587
|
+
args: data.args,
|
|
588
|
+
});
|
|
589
|
+
|
|
590
|
+
if (fingerprint && existingFingerprints.has(fingerprint)) {
|
|
591
|
+
covered.add(recommendation.recommendationId);
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
return covered;
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/*
|
|
599
|
+
* Same diff as `getCoveredRecommendationIds`, but keeps WHICH monitor
|
|
600
|
+
* covers each recommendation instead of only that one does.
|
|
601
|
+
*
|
|
602
|
+
* The set-only version is enough to grey a card out, and that is all the
|
|
603
|
+
* page could do with it: a user looking at "Already created" had no way to
|
|
604
|
+
* get from the card to the monitor to check its thresholds, its on-call
|
|
605
|
+
* policy, or whether it is even enabled. Returning the id lets the card link
|
|
606
|
+
* there.
|
|
607
|
+
*
|
|
608
|
+
* When several monitors fingerprint identically — which happens when someone
|
|
609
|
+
* creates the same recommendation twice by hand — the FIRST is kept, matching
|
|
610
|
+
* the order the caller passed them in (the API sorts monitors, so this is
|
|
611
|
+
* stable across loads rather than whichever row Postgres returned first).
|
|
612
|
+
*/
|
|
613
|
+
public static getCoveredRecommendationMonitorIds(data: {
|
|
614
|
+
recommendations: Array<MonitorRecommendation>;
|
|
615
|
+
existingMonitors: Array<{
|
|
616
|
+
monitorId: ObjectID;
|
|
617
|
+
monitorSteps: Array<MonitorStep>;
|
|
618
|
+
}>;
|
|
619
|
+
args: MonitorRecommendationArgs;
|
|
620
|
+
}): Map<string, ObjectID> {
|
|
621
|
+
const monitorIdByFingerprint: Map<string, ObjectID> = new Map<
|
|
622
|
+
string,
|
|
623
|
+
ObjectID
|
|
624
|
+
>();
|
|
625
|
+
|
|
626
|
+
for (const existingMonitor of data.existingMonitors) {
|
|
627
|
+
for (const monitorStep of existingMonitor.monitorSteps) {
|
|
628
|
+
const fingerprint: MonitorRecommendationFingerprint | undefined =
|
|
629
|
+
this.getFingerprintFromMonitorStep(monitorStep);
|
|
630
|
+
|
|
631
|
+
if (!fingerprint) {
|
|
632
|
+
continue;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
const serialized: string = this.serializeFingerprint(fingerprint);
|
|
636
|
+
|
|
637
|
+
if (!monitorIdByFingerprint.has(serialized)) {
|
|
638
|
+
monitorIdByFingerprint.set(serialized, existingMonitor.monitorId);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
|
|
643
|
+
const covered: Map<string, ObjectID> = new Map<string, ObjectID>();
|
|
644
|
+
|
|
645
|
+
if (monitorIdByFingerprint.size === 0) {
|
|
646
|
+
return covered;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
for (const recommendation of data.recommendations) {
|
|
650
|
+
const fingerprint: string | undefined =
|
|
651
|
+
this.getSerializedFingerprintForRecommendation({
|
|
652
|
+
recommendation: recommendation,
|
|
653
|
+
args: data.args,
|
|
654
|
+
});
|
|
655
|
+
|
|
656
|
+
if (!fingerprint) {
|
|
657
|
+
continue;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
const monitorId: ObjectID | undefined =
|
|
661
|
+
monitorIdByFingerprint.get(fingerprint);
|
|
662
|
+
|
|
663
|
+
if (monitorId) {
|
|
664
|
+
covered.set(recommendation.recommendationId, monitorId);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
return covered;
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
/*
|
|
672
|
+
* Build a recommendation's step exactly as the create flow would, and reduce
|
|
673
|
+
* it to its serialized fingerprint.
|
|
674
|
+
*
|
|
675
|
+
* The `monitorName` recomputation matters: templates interpolate the monitor
|
|
676
|
+
* name into incident titles, and while the name is not part of the
|
|
677
|
+
* fingerprint, building the step with a different name than the create flow
|
|
678
|
+
* uses would be an easy way for the two paths to drift apart later.
|
|
679
|
+
*/
|
|
680
|
+
private static getSerializedFingerprintForRecommendation(data: {
|
|
681
|
+
recommendation: MonitorRecommendation;
|
|
682
|
+
args: MonitorRecommendationArgs;
|
|
683
|
+
}): string | undefined {
|
|
684
|
+
const fingerprint: MonitorRecommendationFingerprint | undefined =
|
|
685
|
+
this.getFingerprintFromMonitorStep(
|
|
686
|
+
data.recommendation.getMonitorStep({
|
|
687
|
+
...data.args,
|
|
688
|
+
monitorName: this.getMonitorName({
|
|
689
|
+
recommendation: data.recommendation,
|
|
690
|
+
resourceDisplayName: data.args.monitorName,
|
|
691
|
+
}),
|
|
692
|
+
}),
|
|
693
|
+
);
|
|
694
|
+
|
|
695
|
+
if (!fingerprint) {
|
|
696
|
+
return undefined;
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
return this.serializeFingerprint(fingerprint);
|
|
700
|
+
}
|
|
701
|
+
|
|
702
|
+
/*
|
|
703
|
+
* The recommendations worth showing first: not yet covered, Critical before
|
|
704
|
+
* Warning, otherwise the module's own declaration order (which puts the
|
|
705
|
+
* most important templates first).
|
|
706
|
+
*/
|
|
707
|
+
public static sortRecommendations(data: {
|
|
708
|
+
recommendations: Array<MonitorRecommendation>;
|
|
709
|
+
coveredRecommendationIds: Set<string>;
|
|
710
|
+
}): Array<MonitorRecommendation> {
|
|
711
|
+
const indexOf: Map<string, number> = new Map<string, number>();
|
|
712
|
+
|
|
713
|
+
data.recommendations.forEach(
|
|
714
|
+
(recommendation: MonitorRecommendation, index: number) => {
|
|
715
|
+
indexOf.set(recommendation.recommendationId, index);
|
|
716
|
+
},
|
|
717
|
+
);
|
|
718
|
+
|
|
719
|
+
return [...data.recommendations].sort(
|
|
720
|
+
(a: MonitorRecommendation, b: MonitorRecommendation) => {
|
|
721
|
+
const aCovered: boolean = data.coveredRecommendationIds.has(
|
|
722
|
+
a.recommendationId,
|
|
723
|
+
);
|
|
724
|
+
const bCovered: boolean = data.coveredRecommendationIds.has(
|
|
725
|
+
b.recommendationId,
|
|
726
|
+
);
|
|
727
|
+
|
|
728
|
+
if (aCovered !== bCovered) {
|
|
729
|
+
return aCovered ? 1 : -1;
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
if (a.severity !== b.severity) {
|
|
733
|
+
return a.severity === "Critical" ? -1 : 1;
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
return (
|
|
737
|
+
(indexOf.get(a.recommendationId) ?? 0) -
|
|
738
|
+
(indexOf.get(b.recommendationId) ?? 0)
|
|
739
|
+
);
|
|
740
|
+
},
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
}
|