@oneuptime/common 14.0.8 → 14.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Models/DatabaseModels/Index.ts +2 -0
- package/Models/DatabaseModels/KubernetesAiAgent.ts +364 -0
- package/Models/DatabaseModels/KubernetesCluster.ts +3 -3
- package/Models/DatabaseModels/Project.ts +55 -5
- package/Models/DatabaseModels/ProxmoxResource.ts +49 -1
- package/Models/DatabaseModels/RunnerJob.ts +75 -0
- package/Models/DatabaseModels/TeamComplianceSetting.ts +143 -2
- package/Models/DatabaseModels/WorkspaceProjectAuthToken.ts +17 -0
- package/Server/API/AIChatAPI.ts +6 -3
- package/Server/API/AIInvestigationAPI.ts +17 -10
- package/Server/API/AutoRemediationAPI.ts +207 -25
- package/Server/API/KubernetesClusterAiAccessAPI.ts +623 -21
- package/Server/API/MicrosoftTeamsAPI.ts +93 -14
- package/Server/API/ProxmoxResourceAPI.ts +244 -14
- package/Server/Infrastructure/GlobalCache.ts +32 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.ts +99 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.ts +137 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.ts +42 -0
- package/Server/Infrastructure/Postgres/SchemaMigrations/Index.ts +6 -0
- package/Server/Middleware/TelemetryIngest.ts +41 -18
- package/Server/Services/AIService.ts +70 -1
- package/Server/Services/AlertService.ts +352 -11
- package/Server/Services/AlertSeverityService.ts +65 -15
- package/Server/Services/AutoRemediationRuleService.ts +1 -1
- package/Server/Services/IncidentService.ts +280 -104
- package/Server/Services/IncidentSeverityService.ts +66 -15
- package/Server/Services/KubernetesAiAgentJobService.ts +185 -0
- package/Server/Services/KubernetesAiAgentService.ts +1634 -0
- package/Server/Services/KubernetesClusterAiAccessService.ts +1118 -451
- package/Server/Services/KubernetesClusterService.ts +129 -21
- package/Server/Services/ProjectService.ts +32 -0
- package/Server/Services/ProxmoxResourceService.ts +322 -5
- package/Server/Services/RunnerJobService.ts +241 -104
- package/Server/Services/RunnerService.ts +1 -1
- package/Server/Services/ScheduledMaintenanceService.ts +737 -1
- package/Server/Services/ScheduledMaintenanceStateTimelineService.ts +414 -56
- package/Server/Services/TeamComplianceSettingService.ts +1125 -9
- package/Server/Utils/AI/ClusterAccess/ClusterAccessContext.ts +67 -9
- package/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.ts +39 -10
- package/Server/Utils/AI/ClusterAccess/KubectlJobRunner.ts +14 -3
- package/Server/Utils/AI/Remediation/RemediationCommandTools.ts +37 -15
- package/Server/Utils/AI/Remediation/RemediationExecutionRunner.ts +67 -24
- package/Server/Utils/AI/SRE/AIInvestigationEngine.ts +48 -5
- package/Server/Utils/AI/SRE/IncidentPostmortemRunner.ts +40 -8
- package/Server/Utils/AI/SRE/InvestigationEligibility.ts +13 -2
- package/Server/Utils/AI/SRE/InvestigationGrader.ts +1 -1
- package/Server/Utils/AutoRemediation/CommandPlanExecutor.ts +6 -1
- package/Server/Utils/Telemetry/InventoryEntityRegistry.ts +133 -7
- package/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.ts +58 -4
- package/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.ts +447 -0
- package/Server/Utils/Telemetry/ProxmoxNativePush.ts +903 -0
- package/Server/Utils/Telemetry/TelemetryEntity.ts +87 -27
- package/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.ts +684 -47
- package/Tests/App/AdminDashboard/RowActionPlacementOverrides.test.tsx +278 -0
- package/Tests/App/Dashboard/AIChatChartWidgetZoom.test.tsx +738 -0
- package/Tests/App/Dashboard/AIFeaturesSettingsPointers.test.tsx +147 -0
- package/Tests/App/Dashboard/AIInsightsKubernetesClusterPage.test.tsx +1523 -0
- package/Tests/App/Dashboard/AdminUserNotificationMethodsPage.test.tsx +463 -3
- package/Tests/App/Dashboard/AffectedResourcesPickerDropdownPlacement.test.tsx +958 -0
- package/Tests/App/Dashboard/AffectedResourcesPickerFocusLeave.test.tsx +595 -0
- package/Tests/App/Dashboard/AskAiAvailability.test.ts +20 -3
- package/Tests/App/Dashboard/AskAiDisabled.test.tsx +8 -3
- package/Tests/App/Dashboard/CephRateChartReload.test.tsx +1456 -0
- package/Tests/App/Dashboard/CephTimeRangeZoom.test.tsx +1229 -0
- package/Tests/App/Dashboard/ChartCardTimeRangeZoom.test.tsx +151 -0
- package/Tests/App/Dashboard/ChartCardZoomAffordances.test.tsx +257 -0
- package/Tests/App/Dashboard/ChartRefetchFrame.test.tsx +141 -0
- package/Tests/App/Dashboard/ChartZoomHarness.tsx +249 -0
- package/Tests/App/Dashboard/ChartZoomStandIn.tsx +131 -0
- package/Tests/App/Dashboard/CloudServerlessSlowLoads.test.tsx +628 -0
- package/Tests/App/Dashboard/CloudServerlessTimeRangeZoom.test.tsx +607 -0
- package/Tests/App/Dashboard/ClusterAccessNotice.test.tsx +91 -19
- package/Tests/App/Dashboard/CompanionMetricsTabZoom.test.tsx +632 -0
- package/Tests/App/Dashboard/ContainerChartZoomStandIn.tsx +195 -0
- package/Tests/App/Dashboard/ContainerHostOverviewSlowLoads.test.tsx +542 -0
- package/Tests/App/Dashboard/ContainerHostOverviewTimeRangeZoom.test.tsx +848 -0
- package/Tests/App/Dashboard/ContainerMetricsTimeRangeZoom.test.tsx +561 -0
- package/Tests/App/Dashboard/DashboardBoardWideZoomShell.test.tsx +1147 -0
- package/Tests/App/Dashboard/DashboardEditPermissions.test.tsx +3 -2
- package/Tests/App/Dashboard/DashboardHistogramWidgetDragCursor.test.tsx +399 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoom.test.tsx +985 -0
- package/Tests/App/Dashboard/DashboardHistogramWidgetZoomHint.test.tsx +803 -0
- package/Tests/App/Dashboard/DashboardLazyPages.test.tsx +1 -0
- package/Tests/App/Dashboard/DashboardSettingsPreviewZoom.test.tsx +679 -0
- package/Tests/App/Dashboard/DashboardSloAndDataSourceChartZoom.test.tsx +881 -0
- package/Tests/App/Dashboard/DashboardTraceStatusWidgets.test.tsx +878 -0
- package/Tests/App/Dashboard/DatabaseMetricChartModalTimeRangeZoom.test.tsx +442 -0
- package/Tests/App/Dashboard/DatabaseOverviewTimeRangeZoom.test.tsx +831 -0
- package/Tests/App/Dashboard/DatabaseServerComponents.test.tsx +43 -0
- package/Tests/App/Dashboard/DockerSwarmInsightsTimeRangeZoom.test.tsx +524 -0
- package/Tests/App/Dashboard/DockerSwarmRateChartRefresh.test.tsx +259 -0
- package/Tests/App/Dashboard/EdgeDetailPanelHistory.test.tsx +763 -0
- package/Tests/App/Dashboard/EdgeDetailPanelTimeRangeZoom.test.tsx +547 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsCases.tsx +565 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKathmandu.test.tsx +73 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsKolkata.test.tsx +74 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsNewYork.test.tsx +72 -0
- package/Tests/App/Dashboard/EdgeHistoryBucketsUtc.test.tsx +72 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardRefresh.test.tsx +310 -0
- package/Tests/App/Dashboard/EmbeddedMetricCardTimeRangeZoom.test.tsx +536 -0
- package/Tests/App/Dashboard/EscalationRuleReadiness.test.tsx +3 -1
- package/Tests/App/Dashboard/EscalationRuleRowActions.test.tsx +660 -0
- package/Tests/App/Dashboard/EventNotes.test.tsx +2 -2
- package/Tests/App/Dashboard/EventOverviewPages.test.tsx +587 -13
- package/Tests/App/Dashboard/EventOverviewSnapshotExplorerZoom.test.tsx +1139 -0
- package/Tests/App/Dashboard/EventOverviewSnapshotZoomAcrossTabs.test.tsx +1057 -0
- package/Tests/App/Dashboard/EventOverviewTelemetrySnapshotZoom.test.tsx +757 -0
- package/Tests/App/Dashboard/ExceptionOccurrenceTrendZoom.test.tsx +675 -0
- package/Tests/App/Dashboard/ExceptionOccurrencesZoomSurvivesViewSwitch.test.tsx +405 -0
- package/Tests/App/Dashboard/ExceptionSignalPages.test.tsx +67 -3
- package/Tests/App/Dashboard/ExceptionTablesSpanStatus.test.tsx +515 -0
- package/Tests/App/Dashboard/ExplorerOwnZoomRegression.test.tsx +739 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisCases.tsx +461 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKathmandu.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisKolkata.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisNewYork.test.tsx +25 -0
- package/Tests/App/Dashboard/FlowBandwidthAxisUtc.test.tsx +24 -0
- package/Tests/App/Dashboard/FlowTopTalkersZoomRefetch.test.tsx +722 -0
- package/Tests/App/Dashboard/FlowViewportGuard.test.tsx +2526 -0
- package/Tests/App/Dashboard/HostPagesSlowLoads.test.tsx +872 -0
- package/Tests/App/Dashboard/HostPagesTimeRangeZoom.test.tsx +1208 -0
- package/Tests/App/Dashboard/IncidentRootCauseTimeRangeZoom.test.tsx +358 -0
- package/Tests/App/Dashboard/InfrastructureGraph.test.tsx +100 -9
- package/Tests/App/Dashboard/InfrastructureMetricsTabTimeRangeZoom.test.tsx +485 -0
- package/Tests/App/Dashboard/InvestigationDrawerCompanionZoom.test.tsx +655 -0
- package/Tests/App/Dashboard/InvestigationDrawerZoom.test.tsx +830 -0
- package/Tests/App/Dashboard/InvestigationNotStarted.test.tsx +338 -2
- package/Tests/App/Dashboard/InvoicesPayInvoice.test.tsx +454 -16
- package/Tests/App/Dashboard/KubernetesClusterAiAccessSettings.test.ts +799 -819
- package/Tests/App/Dashboard/KubernetesClusterAiAgentOverviewCard.test.tsx +367 -0
- package/Tests/App/Dashboard/KubernetesClusterAiAgentStatus.test.ts +1202 -0
- package/Tests/App/Dashboard/KubernetesClusterAiDocumentationMarkdown.test.ts +106 -0
- package/Tests/App/Dashboard/KubernetesClusterAiNavigation.test.tsx +354 -0
- package/Tests/App/Dashboard/KubernetesClusterAiPage.test.tsx +1884 -2054
- package/Tests/App/Dashboard/KubernetesClusterAiPageServerParity.test.ts +478 -179
- package/Tests/App/Dashboard/KubernetesClusterMetricTooltips.test.ts +50 -0
- package/Tests/App/Dashboard/KubernetesClusterOverviewTooltips.test.tsx +2 -1
- package/Tests/App/Dashboard/KubernetesClusterOverviewZoom.test.tsx +1217 -0
- package/Tests/App/Dashboard/KubernetesClusterPagesZoom.test.tsx +731 -0
- package/Tests/App/Dashboard/KubernetesCostsLoadRecovery.test.tsx +869 -0
- package/Tests/App/Dashboard/KubernetesCostsZoom.test.tsx +1214 -0
- package/Tests/App/Dashboard/KubernetesCostsZoomExactHours.test.tsx +692 -0
- package/Tests/App/Dashboard/KubernetesNetworkThroughputReload.test.tsx +902 -0
- package/Tests/App/Dashboard/KubernetesNetworkZoomBucketEdges.test.tsx +571 -0
- package/Tests/App/Dashboard/KubernetesOverviewZoomBucketEdges.test.tsx +697 -0
- package/Tests/App/Dashboard/KubernetesResourceMetricsTabZoom.test.tsx +633 -0
- package/Tests/App/Dashboard/LogsExplorerTimeRangeZoom.test.tsx +470 -0
- package/Tests/App/Dashboard/LogsInsightsTimeRangeZoom.test.tsx +948 -0
- package/Tests/App/Dashboard/LogsViewerPinnedWindowZoom.test.tsx +486 -0
- package/Tests/App/Dashboard/MetricChartsBucketInspectorDoubleClick.test.tsx +1001 -0
- package/Tests/App/Dashboard/MetricExplorerInvestigationAutoRefresh.test.tsx +514 -0
- package/Tests/App/Dashboard/MetricExplorerSavedViewZoomRoundTrip.test.tsx +581 -0
- package/Tests/App/Dashboard/MetricExplorerTimeRangeZoom.test.tsx +1162 -0
- package/Tests/App/Dashboard/MetricMonitorPreviewZoomLabel.test.ts +72 -0
- package/Tests/App/Dashboard/MetricRowSparklineTimeRangeZoom.test.tsx +204 -0
- package/Tests/App/Dashboard/MetricViewFirstResultLands.test.tsx +408 -0
- package/Tests/App/Dashboard/MetricViewOwnZoomResetPlacement.test.tsx +952 -0
- package/Tests/App/Dashboard/MetricViewTimeRange.test.ts +139 -0
- package/Tests/App/Dashboard/MetricViewTimeRangeZoom.test.tsx +556 -0
- package/Tests/App/Dashboard/MonitorMetricsChartWrappersZoom.test.tsx +552 -0
- package/Tests/App/Dashboard/MonitorMetricsTabsTimeRangeZoom.test.tsx +740 -0
- package/Tests/App/Dashboard/MonitorOverviewChartsTimeRangeZoom.test.tsx +735 -0
- package/Tests/App/Dashboard/MonitorStepPreviewsTimeRangeZoom.test.tsx +835 -0
- package/Tests/App/Dashboard/NetworkDeviceTimeRangeZoom.test.tsx +1115 -0
- package/Tests/App/Dashboard/OnCallPreventionGuards.test.tsx +186 -17
- package/Tests/App/Dashboard/OnCallRulesDeleteGuard.test.tsx +155 -46
- package/Tests/App/Dashboard/PasskeySettings.test.tsx +87 -2
- package/Tests/App/Dashboard/ProjectAISettingsPages.test.tsx +884 -0
- package/Tests/App/Dashboard/ProxmoxNodeRemove.test.tsx +621 -0
- package/Tests/App/Dashboard/ProxmoxOverviewSlowLoads.test.tsx +584 -0
- package/Tests/App/Dashboard/ProxmoxPagesTimeRangeZoom.test.tsx +823 -0
- package/Tests/App/Dashboard/ProxmoxRateChartReload.test.tsx +1253 -0
- package/Tests/App/Dashboard/PublicDashboardBoardWideZoom.test.tsx +605 -0
- package/Tests/App/Dashboard/RealRowsLineChart.tsx +250 -0
- package/Tests/App/Dashboard/RechartsHostsPlotCursor.test.tsx +513 -0
- package/Tests/App/Dashboard/RowActionPlacementOverrides.test.tsx +865 -0
- package/Tests/App/Dashboard/RumOverviewTimeRangeZoom.test.tsx +877 -0
- package/Tests/App/Dashboard/RunnerKubernetesAgentForm.test.tsx +20 -4
- package/Tests/App/Dashboard/RunnerSupersededByAiAgentNote.test.tsx +296 -0
- package/Tests/App/Dashboard/SecurityEventConnectionsTable.test.tsx +146 -36
- package/Tests/App/Dashboard/ServiceDatabaseSlowLoads.test.tsx +1166 -0
- package/Tests/App/Dashboard/ServiceMapGraph.test.tsx +1243 -104
- package/Tests/App/Dashboard/ServiceOverviewTimeRangeZoom.test.tsx +792 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoom.test.tsx +771 -0
- package/Tests/App/Dashboard/SloBudgetBurnDownZoomAffordances.test.tsx +380 -0
- package/Tests/App/Dashboard/SloEventMetricsBarChartZoom.test.tsx +369 -0
- package/Tests/App/Dashboard/SloHistoryChartsTimeRangeZoom.test.tsx +694 -0
- package/Tests/App/Dashboard/SloHistoryChartsZoomAffordances.test.tsx +497 -0
- package/Tests/App/Dashboard/SloMetricsTimeRangeZoom.test.tsx +541 -0
- package/Tests/App/Dashboard/SlowLoadHarness.ts +123 -0
- package/Tests/App/Dashboard/SnapshotExplorerPrimaryZoom.test.tsx +1194 -0
- package/Tests/App/Dashboard/SnapshotViewerPinnedWindow.test.tsx +620 -0
- package/Tests/App/Dashboard/TelemetrySnapshotPanelTimeRangeZoom.test.tsx +765 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoom.test.tsx +516 -0
- package/Tests/App/Dashboard/TelemetrySnapshotZoomAcrossTabs.test.tsx +827 -0
- package/Tests/App/Dashboard/TimeRangeZoomPageHarness.tsx +463 -0
- package/Tests/App/Dashboard/TraceChartEditorStatusSplit.test.tsx +543 -0
- package/Tests/App/Dashboard/TraceStatusPresentation.test.tsx +967 -0
- package/Tests/App/Dashboard/TraceTimelineMinimapZoom.test.tsx +315 -0
- package/Tests/App/Dashboard/TracesAnalyticsStatusSplit.test.tsx +778 -0
- package/Tests/App/Dashboard/TracesAnalyticsViewZoom.test.tsx +349 -0
- package/Tests/App/Dashboard/TracesExplorerTimeRangeZoom.test.tsx +551 -0
- package/Tests/App/Dashboard/TracesStatusChartAndFacet.test.tsx +687 -0
- package/Tests/App/Dashboard/TracesViewerStaleResponses.test.tsx +458 -0
- package/Tests/App/Dashboard/UptimeStripsExcludedFromZoom.test.tsx +406 -0
- package/Tests/App/Dashboard/VMwareOverviewSlowLoads.test.tsx +535 -0
- package/Tests/App/Dashboard/VMwarePagesTimeRangeZoom.test.tsx +606 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineStaleHover.test.tsx +1292 -0
- package/Tests/App/Dashboard/ValueWidgetSparklineZoom.test.tsx +765 -0
- package/Tests/App/StatusPage/StatusPageResourceExplorer.test.tsx +98 -0
- package/Tests/Models/DatabaseModels/KubernetesAiAgentModel.test.ts +295 -0
- package/Tests/Models/DatabaseModels/TeamComplianceSettingModel.test.ts +347 -0
- package/Tests/Server/API/AIChatRouteAuthorization.test.ts +58 -0
- package/Tests/Server/API/AIGenerationProjectAIToggle.test.ts +1 -1
- package/Tests/Server/API/AIInvestigationAPI.test.ts +123 -45
- package/Tests/Server/API/AutoRemediationApproveClusterRound.test.ts +1329 -0
- package/Tests/Server/API/AutoRemediationApproveKubectlRecheck.test.ts +25 -2
- package/Tests/Server/API/KubernetesClusterAiAccessAPI.test.ts +2 -0
- package/Tests/Server/API/KubernetesClusterAiAccessAPIAgent.test.ts +903 -0
- package/Tests/Server/API/MicrosoftTeamsChatsRefreshRoute.test.ts +667 -0
- package/Tests/Server/API/MicrosoftTeamsManifest.test.ts +294 -6
- package/Tests/Server/API/ProxmoxResourceAPIRemoveNode.test.ts +1932 -0
- package/Tests/Server/Infrastructure/GlobalCache.test.ts +195 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesAiAgentAndAiDefaultsMigration.test.ts +623 -0
- package/Tests/Server/Infrastructure/Postgres/AddKubernetesClusterAiAccessMigration.test.ts +87 -10
- package/Tests/Server/Infrastructure/Postgres/AddTeamComplianceRuleScopeMigration.test.ts +622 -0
- package/Tests/Server/Middleware/TelemetryIngestBrowserKey.test.ts +61 -4
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerPinnedKey.test.ts +98 -2
- package/Tests/Server/Middleware/TelemetryIngestKubernetesAgentRunnerRateLimit.test.ts +105 -2
- package/Tests/Server/Services/AIServiceBalanceBlocker.test.ts +282 -0
- package/Tests/Server/Services/AlertMonitorClearWritesNull.test.ts +409 -0
- package/Tests/Server/Services/AlertMonitorEditFeed.test.ts +474 -0
- package/Tests/Server/Services/AlertMonitorEditGuard.test.ts +610 -0
- package/Tests/Server/Services/AutoRemediationClusterRuleEngine.test.ts +2 -0
- package/Tests/Server/Services/AutoRemediationRuleCommandRunners.test.ts +2 -1
- package/Tests/Server/Services/IncidentAffectedMonitorsEdit.test.ts +1360 -0
- package/Tests/Server/Services/IncidentAlertSloLinkSurvivesEdit.test.ts +3 -1
- package/Tests/Server/Services/IncidentSeverityDeletePausesComplianceRules.test.ts +488 -0
- package/Tests/Server/Services/KubernetesAiAgentJobService.test.ts +329 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceHelpers.test.ts +618 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceLifecycle.test.ts +1444 -0
- package/Tests/Server/Services/KubernetesAiAgentServiceRegister.test.ts +1666 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessDeletedRunnerRemedy.test.ts +140 -111
- package/Tests/Server/Services/KubernetesClusterAiAccessGapNextSteps.test.ts +267 -105
- package/Tests/Server/Services/KubernetesClusterAiAccessLegacySuperseded.test.ts +440 -0
- package/Tests/Server/Services/KubernetesClusterAiAccessService.test.ts +567 -411
- package/Tests/Server/Services/KubernetesClusterAiAccessTarget.test.ts +1168 -0
- package/Tests/Server/Services/KubernetesClusterAiSettingsPermission.test.ts +410 -12
- package/Tests/Server/Services/KubernetesClusterServiceAiAccessFeed.test.ts +55 -0
- package/Tests/Server/Services/ProjectServiceNewProjectAiDefaults.test.ts +288 -0
- package/Tests/Server/Services/ProxmoxResourcePostgres.test.ts +5633 -0
- package/Tests/Server/Services/ProxmoxResourceService.test.ts +6458 -5
- package/Tests/Server/Services/RunnerJobEnqueueKubectl.test.ts +48 -15
- package/Tests/Server/Services/RunnerJobEnqueueKubectlAiAgent.test.ts +620 -0
- package/Tests/Server/Services/RunnerJobKubectlWriteScope.test.ts +170 -3
- package/Tests/Server/Services/RunnerJobTimeoutReason.test.ts +72 -0
- package/Tests/Server/Services/RunnerServiceAgentName.test.ts +6 -4
- package/Tests/Server/Services/ScheduledMaintenanceAttachmentEdit.test.ts +2113 -0
- package/Tests/Server/Services/ScheduledMaintenanceMonitorRelease.test.ts +972 -0
- package/Tests/Server/Services/TeamComplianceSettingPostgres.test.ts +2237 -0
- package/Tests/Server/Services/TeamComplianceSettingService.test.ts +3988 -138
- package/Tests/Server/Utils/AI/ClusterAccessContext.test.ts +107 -1
- package/Tests/Server/Utils/AI/IncidentPostmortemRunner.test.ts +351 -0
- package/Tests/Server/Utils/AI/InvestigationClusterAccessWiring.test.ts +2 -0
- package/Tests/Server/Utils/AI/InvestigationEligibility.test.ts +310 -0
- package/Tests/Server/Utils/AI/KubectlInvestigationToolkit.test.ts +70 -0
- package/Tests/Server/Utils/AI/KubectlJobRunnerFinishedJob.test.ts +58 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectl.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsKubectlOutcome.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsLiveCluster.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationCommandToolsRunnerScope.test.ts +125 -1
- package/Tests/Server/Utils/AI/RemediationExecutionRunner.test.ts +252 -3
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerClusterMode.test.ts +2 -0
- package/Tests/Server/Utils/AI/RemediationExecutionRunnerProjectGates.test.ts +240 -0
- package/Tests/Server/Utils/AI/RemediationKubectlCopyParity.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutor.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanExecutorUnknownRun.test.ts +2 -0
- package/Tests/Server/Utils/AutoRemediation/CommandPlanVerification.test.ts +2 -0
- package/Tests/Server/Utils/Telemetry/HostAssetAttributesFromOtlp.test.ts +68 -0
- package/Tests/Server/Utils/Telemetry/InventoryMirrorPostgres.test.ts +370 -0
- package/Tests/Server/Utils/Telemetry/NetworkDeviceInventoryAttributes.test.ts +522 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.test.ts +155 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.test.ts +8631 -0
- package/Tests/Server/Utils/Telemetry/ProxmoxNativePush.test.ts +2852 -0
- package/Tests/Server/Utils/Telemetry/TelemetryEntity.test.ts +367 -1
- package/Tests/Server/Utils/Telemetry/TelemetryIngestionKeyGuard.test.ts +45 -3
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsChats.test.ts +25 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNames.test.ts +3119 -0
- package/Tests/Server/Utils/Workspace/MicrosoftTeamsGroupChatNamesPostgres.test.ts +1721 -0
- package/Tests/Types/Kubernetes/KubernetesAgentChartAiAccess.test.ts +581 -113
- package/Tests/Types/Kubernetes/KubernetesAiAgentAccessContract.test.ts +510 -0
- package/Tests/Types/Team/ComplianceRule.test.ts +1029 -0
- package/Tests/Types/Team/TeamComplianceStatus.test.ts +176 -0
- package/Tests/UI/Components/ActionButton/RowActions.test.tsx +641 -0
- package/Tests/UI/Components/ActionButton/RowActionsAccessibility.test.tsx +414 -0
- package/Tests/UI/Components/ActionButton/SplitActionButtons.test.ts +565 -0
- package/Tests/UI/Components/Charts/BarChartDragToSelect.test.tsx +386 -0
- package/Tests/UI/Components/Charts/ChartDoubleClickResetWithoutDblclick.test.tsx +1025 -0
- package/Tests/UI/Components/Charts/ChartGroupPageZoomHint.test.tsx +196 -0
- package/Tests/UI/Components/Charts/ChartPlaceholdersTakeDoubleClick.test.tsx +1861 -0
- package/Tests/UI/Components/Charts/ChartPlotClicksWhileResetArmed.test.tsx +382 -0
- package/Tests/UI/Components/Charts/ChartRangeSelection.test.tsx +704 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionDoubleClickReset.test.tsx +1481 -0
- package/Tests/UI/Components/Charts/ChartRangeSelectionRealRecharts.test.tsx +918 -0
- package/Tests/UI/Components/Charts/ChartRowBucketStart.test.ts +350 -0
- package/Tests/UI/Components/Charts/DoubleClickReset.test.tsx +996 -0
- package/Tests/UI/Components/Charts/HistogramDoubleClickResetRealRecharts.test.tsx +965 -0
- package/Tests/UI/Components/Charts/HistogramDriftThenRefetch.test.tsx +277 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionDataChange.test.tsx +1757 -0
- package/Tests/UI/Components/Charts/HistogramRangeSelectionRealRecharts.test.tsx +560 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ChartPageTimeRangeZoom.test.tsx +513 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.test.tsx +845 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.test.tsx +119 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHintTailwind.test.tsx +274 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.test.ts +433 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.test.tsx +863 -0
- package/Tests/UI/Components/Charts/TimeRangeZoom/ZoomDocsPromises.test.tsx +1147 -0
- package/Tests/UI/Components/DisabledButtonTooltip.test.tsx +14 -3
- package/Tests/UI/Components/EntityDropdownEscape.test.tsx +454 -0
- package/Tests/UI/Components/EntityDropdownFocusLeave.test.tsx +420 -0
- package/Tests/UI/Components/Forms/FormFieldDropdownDisabled.test.tsx +233 -0
- package/Tests/UI/Components/Input.test.tsx +80 -1
- package/Tests/UI/Components/LogsAnalyticsViewZoom.test.tsx +947 -0
- package/Tests/UI/Components/LogsHistogramDragTooltip.test.tsx +17 -5
- package/Tests/UI/Components/LogsViewerAnalyticsZoom.test.tsx +501 -0
- package/Tests/UI/Components/LogsViewerHistogramZoom.test.tsx +1 -1
- package/Tests/UI/Components/ModelTable/BaseModelTableBulkDelete.test.tsx +6 -4
- package/Tests/UI/Components/ModelTable/BaseModelTablePermissionGating.test.tsx +60 -8
- package/Tests/UI/Components/ModelTable/BaseModelTableRowActions.test.tsx +476 -0
- package/Tests/UI/Components/ModelTable/ModelTableWrapContent.test.tsx +7 -5
- package/Tests/UI/Components/MoreMenuPortal.test.tsx +623 -0
- package/Tests/UI/Components/StatusPage/ResourceGridActions.test.tsx +538 -0
- package/Tests/UI/Components/StatusPage/ResourceGridSelection.test.tsx +14 -4
- package/Tests/UI/Components/TableCellWrapping.test.tsx +3 -3
- package/Tests/UI/Components/TableRowActions.test.tsx +472 -0
- package/Tests/UI/Components/TelemetryHistogramDragTooltip.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerHistogramClickZoom.test.tsx +1 -1
- package/Tests/UI/Components/TelemetryViewer/TelemetryViewerZoomScope.test.tsx +470 -0
- package/Tests/UI/Components/TelemetryViewer/UseViewerTimeRangeZoom.test.tsx +571 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerDoubleClickResetUnderDataChange.test.tsx +928 -0
- package/Tests/UI/Components/TelemetryViewer/ViewerFollowsEnclosingZoom.test.tsx +1569 -0
- package/Tests/UI/Components/Toggle.test.tsx +66 -0
- package/Tests/UI/MicrosoftTeams/MicrosoftTeamsChatsCardRefreshNames.test.tsx +1023 -0
- package/Tests/UI/Rum/ChunkLoader.test.ts +201 -0
- package/Tests/UI/Rum/FidelityNotices.test.ts +54 -0
- package/Tests/UI/Rum/ReplayCorrelationPanel.test.tsx +72 -0
- package/Tests/UI/Rum/ReplayFrameCapture.test.ts +1009 -9
- package/Tests/UI/Rum/ReplayRecordedAssets.test.ts +1069 -0
- package/Tests/UI/Rum/ReplayStage.test.tsx +913 -2
- package/Tests/UI/Rum/SessionReplayTable.test.tsx +443 -8
- package/Tests/UI/Rum/SessionReplayUsersTable.test.tsx +439 -9
- package/Tests/UI/Rum/UserFlowTablesRowActions.test.tsx +501 -0
- package/Tests/Utils/AiRemediation/KubectlPolicyChartRbacParity.test.ts +47 -19
- package/Tests/Utils/AiRemediation/KubectlWriteScopeCallerParity.test.ts +2 -0
- package/Tests/Utils/AiRemediation/KubernetesAiAgentPolicyCopyParity.test.ts +396 -0
- package/Tests/Utils/Dashboard/DashboardTimeRangeZoom.test.ts +104 -1
- package/Types/AI/AIRunType.ts +9 -4
- package/Types/AI/InvestigationNotStartedReason.ts +6 -0
- package/Types/AutoRemediation/AiRemediationCommandPlan.ts +14 -16
- package/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.ts +28 -0
- package/Types/Dashboard/DashboardComponents/DashboardTraceChartComponent.ts +4 -2
- package/Types/Kubernetes/KubernetesClusterAiAccess.ts +263 -32
- package/Types/Kubernetes/KubernetesClusterAiAccessPermissions.ts +20 -16
- package/Types/Kubernetes/KubernetesClusterAiInsights.ts +81 -0
- package/Types/Monitor/ProxmoxAlertTemplates.ts +4 -4
- package/Types/Team/ComplianceNotificationChannel.ts +22 -0
- package/Types/Team/ComplianceRule.ts +434 -0
- package/Types/Team/ComplianceRuleType.ts +21 -0
- package/Types/Team/TeamComplianceStatus.ts +81 -0
- package/Types/Telemetry/TelemetryIngestSurface.ts +28 -0
- package/UI/Components/ActionButton/ActionButtonSchema.ts +21 -0
- package/UI/Components/ActionButton/RowActions.tsx +238 -0
- package/UI/Components/ActionButton/SplitActionButtons.ts +173 -0
- package/UI/Components/Charts/Area/AreaChart.tsx +32 -2
- package/UI/Components/Charts/Bar/BarChart.tsx +40 -3
- package/UI/Components/Charts/ChartGroup/ChartGroup.tsx +37 -11
- package/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.tsx +80 -243
- package/UI/Components/Charts/ChartLibrary/BarChart/BarChart.tsx +122 -31
- package/UI/Components/Charts/ChartLibrary/LineChart/LineChart.tsx +95 -244
- package/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.ts +214 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.ts +32 -0
- package/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.ts +599 -0
- package/UI/Components/Charts/Line/LineChart.tsx +32 -2
- package/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.tsx +70 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.tsx +226 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.tsx +73 -0
- package/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.ts +163 -0
- package/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.ts +215 -0
- package/UI/Components/Charts/Utils/DataPoint.ts +20 -8
- package/UI/Components/Charts/Utils/XAxis.ts +75 -0
- package/UI/Components/Charts/Utils/useHistogramRangeSelection.ts +493 -59
- package/UI/Components/EntityDropdown/EntityDropdown.tsx +107 -11
- package/UI/Components/Forms/Fields/FormField.tsx +10 -0
- package/UI/Components/Input/Input.tsx +90 -57
- package/UI/Components/List/ListRow.tsx +8 -74
- package/UI/Components/LogsViewer/LogsViewer.tsx +40 -11
- package/UI/Components/LogsViewer/components/LogTimeRangePicker.tsx +22 -6
- package/UI/Components/LogsViewer/components/LogsAnalyticsView.tsx +209 -6
- package/UI/Components/LogsViewer/components/LogsHistogram.tsx +127 -84
- package/UI/Components/LogsViewer/components/LogsViewerToolbar.tsx +3 -0
- package/UI/Components/ModelTable/BaseModelTable.tsx +12 -1
- package/UI/Components/MoreMenu/MoreMenu.tsx +365 -30
- package/UI/Components/MoreMenu/MoreMenuItem.tsx +70 -17
- package/UI/Components/OrderedStatesList/Item.tsx +7 -72
- package/UI/Components/StatusPage/ResourceGrid.tsx +126 -77
- package/UI/Components/Table/TableBody.tsx +8 -2
- package/UI/Components/Table/TableRow.tsx +20 -140
- package/UI/Components/TelemetryViewer/TelemetryViewer.tsx +41 -10
- package/UI/Components/TelemetryViewer/components/TelemetryHistogram.tsx +47 -9
- package/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.tsx +25 -6
- package/UI/Components/TelemetryViewer/types.ts +2 -0
- package/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.ts +448 -0
- package/UI/Components/Toggle/Toggle.tsx +26 -0
- package/UI/Components/Tooltip/Tooltip.tsx +8 -1
- package/UI/Styles/Theme.css +14 -2
- package/Utils/AiRemediation/CommandPolicy.ts +1 -1
- package/Utils/AiRemediation/KubectlPolicy.ts +1 -1
- package/Utils/Dashboard/DashboardTimeRangeZoom.ts +46 -2
- package/build/dist/Models/DatabaseModels/Index.js +2 -0
- package/build/dist/Models/DatabaseModels/Index.js.map +1 -1
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js +385 -0
- package/build/dist/Models/DatabaseModels/KubernetesAiAgent.js.map +1 -0
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js +3 -3
- package/build/dist/Models/DatabaseModels/KubernetesCluster.js.map +1 -1
- package/build/dist/Models/DatabaseModels/Project.js +56 -5
- package/build/dist/Models/DatabaseModels/Project.js.map +1 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js +51 -1
- package/build/dist/Models/DatabaseModels/ProxmoxResource.js.map +1 -1
- package/build/dist/Models/DatabaseModels/RunnerJob.js +74 -0
- package/build/dist/Models/DatabaseModels/RunnerJob.js.map +1 -1
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js +134 -3
- package/build/dist/Models/DatabaseModels/TeamComplianceSetting.js.map +1 -1
- package/build/dist/Models/DatabaseModels/WorkspaceProjectAuthToken.js.map +1 -1
- package/build/dist/Server/API/AIChatAPI.js +6 -1
- package/build/dist/Server/API/AIChatAPI.js.map +1 -1
- package/build/dist/Server/API/AIInvestigationAPI.js +17 -10
- package/build/dist/Server/API/AIInvestigationAPI.js.map +1 -1
- package/build/dist/Server/API/AutoRemediationAPI.js +144 -24
- package/build/dist/Server/API/AutoRemediationAPI.js.map +1 -1
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js +436 -22
- package/build/dist/Server/API/KubernetesClusterAiAccessAPI.js.map +1 -1
- package/build/dist/Server/API/MicrosoftTeamsAPI.js +69 -13
- package/build/dist/Server/API/MicrosoftTeamsAPI.js.map +1 -1
- package/build/dist/Server/API/ProxmoxResourceAPI.js +187 -14
- package/build/dist/Server/API/ProxmoxResourceAPI.js.map +1 -1
- package/build/dist/Server/Infrastructure/GlobalCache.js +27 -0
- package/build/dist/Server/Infrastructure/GlobalCache.js.map +1 -1
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js +50 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796000000000-AddTeamComplianceRuleScope.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js +84 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796100000000-AddKubernetesAiAgentAndAiDefaults.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js +31 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/1796200000000-AddProxmoxResourceNativePushColumns.js.map +1 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js +6 -0
- package/build/dist/Server/Infrastructure/Postgres/SchemaMigrations/Index.js.map +1 -1
- package/build/dist/Server/Middleware/TelemetryIngest.js +34 -19
- package/build/dist/Server/Middleware/TelemetryIngest.js.map +1 -1
- package/build/dist/Server/Services/AIService.js +59 -1
- package/build/dist/Server/Services/AIService.js.map +1 -1
- package/build/dist/Server/Services/AlertService.js +248 -13
- package/build/dist/Server/Services/AlertService.js.map +1 -1
- package/build/dist/Server/Services/AlertSeverityService.js +51 -15
- package/build/dist/Server/Services/AlertSeverityService.js.map +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js +1 -1
- package/build/dist/Server/Services/AutoRemediationRuleService.js.map +1 -1
- package/build/dist/Server/Services/IncidentService.js +199 -72
- package/build/dist/Server/Services/IncidentService.js.map +1 -1
- package/build/dist/Server/Services/IncidentSeverityService.js +51 -15
- package/build/dist/Server/Services/IncidentSeverityService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js +158 -0
- package/build/dist/Server/Services/KubernetesAiAgentJobService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js +1145 -0
- package/build/dist/Server/Services/KubernetesAiAgentService.js.map +1 -0
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js +842 -371
- package/build/dist/Server/Services/KubernetesClusterAiAccessService.js.map +1 -1
- package/build/dist/Server/Services/KubernetesClusterService.js +86 -17
- package/build/dist/Server/Services/KubernetesClusterService.js.map +1 -1
- package/build/dist/Server/Services/ProjectService.js +22 -0
- package/build/dist/Server/Services/ProjectService.js.map +1 -1
- package/build/dist/Server/Services/ProxmoxResourceService.js +258 -3
- package/build/dist/Server/Services/ProxmoxResourceService.js.map +1 -1
- package/build/dist/Server/Services/RunnerJobService.js +178 -92
- package/build/dist/Server/Services/RunnerJobService.js.map +1 -1
- package/build/dist/Server/Services/RunnerService.js +1 -1
- package/build/dist/Server/Services/RunnerService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceService.js +535 -3
- package/build/dist/Server/Services/ScheduledMaintenanceService.js.map +1 -1
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js +316 -46
- package/build/dist/Server/Services/ScheduledMaintenanceStateTimelineService.js.map +1 -1
- package/build/dist/Server/Services/TeamComplianceSettingService.js +833 -7
- package/build/dist/Server/Services/TeamComplianceSettingService.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js +52 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/ClusterAccessContext.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js +13 -9
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlInvestigationToolkit.js.map +1 -1
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js +7 -3
- package/build/dist/Server/Utils/AI/ClusterAccess/KubectlJobRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js +29 -15
- package/build/dist/Server/Utils/AI/Remediation/RemediationCommandTools.js.map +1 -1
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js +56 -20
- package/build/dist/Server/Utils/AI/Remediation/RemediationExecutionRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js +39 -5
- package/build/dist/Server/Utils/AI/SRE/AIInvestigationEngine.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js +30 -3
- package/build/dist/Server/Utils/AI/SRE/IncidentPostmortemRunner.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js +13 -2
- package/build/dist/Server/Utils/AI/SRE/InvestigationEligibility.js.map +1 -1
- package/build/dist/Server/Utils/AI/SRE/InvestigationGrader.js +1 -1
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js +4 -2
- package/build/dist/Server/Utils/AutoRemediation/CommandPlanExecutor.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js +111 -6
- package/build/dist/Server/Utils/Telemetry/InventoryEntityRegistry.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js +40 -4
- package/build/dist/Server/Utils/Telemetry/ProxmoxCephSnapshotScan.js.map +1 -1
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js +296 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativeNodeLiveness.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js +699 -0
- package/build/dist/Server/Utils/Telemetry/ProxmoxNativePush.js.map +1 -0
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js +77 -19
- package/build/dist/Server/Utils/Telemetry/TelemetryEntity.js.map +1 -1
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js +505 -42
- package/build/dist/Server/Utils/Workspace/MicrosoftTeams/MicrosoftTeams.js.map +1 -1
- package/build/dist/Types/AI/AIRunType.js +9 -4
- package/build/dist/Types/AI/AIRunType.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js +10 -16
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPlan.js.map +1 -1
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js +28 -0
- package/build/dist/Types/AutoRemediation/AiRemediationCommandPolicyVerdict.js.map +1 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js +72 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccess.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js +20 -16
- package/build/dist/Types/Kubernetes/KubernetesClusterAiAccessPermissions.js.map +1 -1
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js +17 -0
- package/build/dist/Types/Kubernetes/KubernetesClusterAiInsights.js.map +1 -0
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js +4 -4
- package/build/dist/Types/Monitor/ProxmoxAlertTemplates.js.map +1 -1
- package/build/dist/Types/Team/ComplianceNotificationChannel.js +23 -0
- package/build/dist/Types/Team/ComplianceNotificationChannel.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRule.js +304 -0
- package/build/dist/Types/Team/ComplianceRule.js.map +1 -0
- package/build/dist/Types/Team/ComplianceRuleType.js +21 -0
- package/build/dist/Types/Team/ComplianceRuleType.js.map +1 -1
- package/build/dist/Types/Team/TeamComplianceStatus.js +2 -0
- package/build/dist/Types/Team/TeamComplianceStatus.js.map +1 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js +25 -0
- package/build/dist/Types/Telemetry/TelemetryIngestSurface.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js +20 -1
- package/build/dist/UI/Components/ActionButton/ActionButtonSchema.js.map +1 -1
- package/build/dist/UI/Components/ActionButton/RowActions.js +113 -0
- package/build/dist/UI/Components/ActionButton/RowActions.js.map +1 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js +71 -0
- package/build/dist/UI/Components/ActionButton/SplitActionButtons.js.map +1 -0
- package/build/dist/UI/Components/Charts/Area/AreaChart.js +16 -1
- package/build/dist/UI/Components/Charts/Area/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js +16 -1
- package/build/dist/UI/Components/Charts/Bar/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js +27 -5
- package/build/dist/UI/Components/Charts/ChartGroup/ChartGroup.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js +61 -194
- package/build/dist/UI/Components/Charts/ChartLibrary/AreaChart/AreaChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js +84 -26
- package/build/dist/UI/Components/Charts/ChartLibrary/BarChart/BarChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js +75 -196
- package/build/dist/UI/Components/Charts/ChartLibrary/LineChart/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js +133 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/DoubleClick.js.map +1 -1
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js +26 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartBucketsChange.js.map +1 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js +371 -0
- package/build/dist/UI/Components/Charts/ChartLibrary/Utils/UseChartRangeSelection.js.map +1 -0
- package/build/dist/UI/Components/Charts/Line/LineChart.js +16 -1
- package/build/dist/UI/Components/Charts/Line/LineChart.js.map +1 -1
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js +35 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/ResetTimeRangeZoomButton.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js +107 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomContext.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js +41 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomHint.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js +117 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/TimeRangeZoomUtil.js.map +1 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js +121 -0
- package/build/dist/UI/Components/Charts/TimeRangeZoom/UseTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js +15 -7
- package/build/dist/UI/Components/Charts/Utils/DataPoint.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/XAxis.js +70 -0
- package/build/dist/UI/Components/Charts/Utils/XAxis.js.map +1 -1
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js +305 -38
- package/build/dist/UI/Components/Charts/Utils/useHistogramRangeSelection.js.map +1 -1
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js +89 -14
- package/build/dist/UI/Components/EntityDropdown/EntityDropdown.js.map +1 -1
- package/build/dist/UI/Components/Forms/Fields/FormField.js +11 -2
- package/build/dist/UI/Components/Forms/Fields/FormField.js.map +1 -1
- package/build/dist/UI/Components/Input/Input.js +69 -54
- package/build/dist/UI/Components/Input/Input.js.map +1 -1
- package/build/dist/UI/Components/List/ListRow.js +4 -39
- package/build/dist/UI/Components/List/ListRow.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js +31 -8
- package/build/dist/UI/Components/LogsViewer/LogsViewer.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js +10 -1
- package/build/dist/UI/Components/LogsViewer/components/LogTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js +139 -21
- package/build/dist/UI/Components/LogsViewer/components/LogsAnalyticsView.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js +30 -8
- package/build/dist/UI/Components/LogsViewer/components/LogsHistogram.js.map +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js +1 -1
- package/build/dist/UI/Components/LogsViewer/components/LogsViewerToolbar.js.map +1 -1
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js +10 -0
- package/build/dist/UI/Components/ModelTable/BaseModelTable.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js +253 -14
- package/build/dist/UI/Components/MoreMenu/MoreMenu.js.map +1 -1
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js +43 -13
- package/build/dist/UI/Components/MoreMenu/MoreMenuItem.js.map +1 -1
- package/build/dist/UI/Components/OrderedStatesList/Item.js +2 -38
- package/build/dist/UI/Components/OrderedStatesList/Item.js.map +1 -1
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js +53 -15
- package/build/dist/UI/Components/StatusPage/ResourceGrid.js.map +1 -1
- package/build/dist/UI/Components/Table/TableBody.js +6 -2
- package/build/dist/UI/Components/Table/TableBody.js.map +1 -1
- package/build/dist/UI/Components/Table/TableRow.js +12 -75
- package/build/dist/UI/Components/Table/TableRow.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js +32 -7
- package/build/dist/UI/Components/TelemetryViewer/TelemetryViewer.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js +27 -9
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryHistogram.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js +11 -1
- package/build/dist/UI/Components/TelemetryViewer/components/TelemetryTimeRangePicker.js.map +1 -1
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js +272 -0
- package/build/dist/UI/Components/TelemetryViewer/useViewerTimeRangeZoom.js.map +1 -0
- package/build/dist/UI/Components/Toggle/Toggle.js +14 -1
- package/build/dist/UI/Components/Toggle/Toggle.js.map +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js +1 -1
- package/build/dist/UI/Components/Tooltip/Tooltip.js.map +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/CommandPolicy.js.map +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js +1 -1
- package/build/dist/Utils/AiRemediation/KubectlPolicy.js.map +1 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js +27 -1
- package/build/dist/Utils/Dashboard/DashboardTimeRangeZoom.js.map +1 -1
- package/package.json +1 -1
|
@@ -7,32 +7,840 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
|
|
|
7
7
|
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
8
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
9
|
};
|
|
10
|
+
import AlertSeverityService from "./AlertSeverityService";
|
|
10
11
|
import DatabaseService from "./DatabaseService";
|
|
12
|
+
import IncidentSeverityService from "./IncidentSeverityService";
|
|
13
|
+
import AlertSeverity from "../../Models/DatabaseModels/AlertSeverity";
|
|
14
|
+
import IncidentSeverity from "../../Models/DatabaseModels/IncidentSeverity";
|
|
11
15
|
import Model from "../../Models/DatabaseModels/TeamComplianceSetting";
|
|
16
|
+
import ModelPermission from "../Types/Database/Permissions/Index";
|
|
17
|
+
import QueryHelper from "../Types/Database/QueryHelper";
|
|
18
|
+
import Includes from "../../Types/BaseDatabase/Includes";
|
|
19
|
+
import { LIMIT_PER_PROJECT } from "../../Types/Database/LimitMax";
|
|
12
20
|
import BadDataException from "../../Types/Exception/BadDataException";
|
|
21
|
+
import ObjectID from "../../Types/ObjectID";
|
|
22
|
+
import ComplianceRule, { ComplianceSeverityKind, } from "../../Types/Team/ComplianceRule";
|
|
23
|
+
import ComplianceRuleType from "../../Types/Team/ComplianceRuleType";
|
|
13
24
|
import CaptureSpan from "../Utils/Telemetry/CaptureSpan";
|
|
25
|
+
export const DUPLICATE_COMPLIANCE_RULE_MESSAGE = "This team already has a compliance rule that checks exactly the same thing. Edit that rule instead of adding another.";
|
|
26
|
+
/*
|
|
27
|
+
* The key a severity delete sets to true in TeamComplianceSetting.options on
|
|
28
|
+
* a rule it left with none of the severities it was scoped to (see SEVERITY
|
|
29
|
+
* DELETES below). Such a rule checks nothing until an admin picks new
|
|
30
|
+
* severities: it is paused, it cannot be turned back on as it is, it is no
|
|
31
|
+
* duplicate of anything, and the compliance page says why. Any update that
|
|
32
|
+
* changes the rule's scope removes the key.
|
|
33
|
+
*/
|
|
34
|
+
export const SEVERITIES_DELETED_OPTION = "severitiesDeleted";
|
|
35
|
+
export const SEVERITIES_DELETED_ENABLE_MESSAGE = "Every severity this rule was scoped to has been deleted. Edit the rule to choose new severities before turning it back on.";
|
|
36
|
+
export const OPTIONS_DIFFER_MESSAGE = "These compliance rules carry different options, so their scope cannot be changed in one update. Update them one at a time.";
|
|
37
|
+
/*
|
|
38
|
+
* The two severity relations, and the model each one's items are written as.
|
|
39
|
+
* Every list a payload carries for them is rewritten through
|
|
40
|
+
* normaliseSeverityLists before anything is checked.
|
|
41
|
+
*/
|
|
42
|
+
const SEVERITY_LISTS = [
|
|
43
|
+
{
|
|
44
|
+
key: "incidentSeverities",
|
|
45
|
+
kind: ComplianceSeverityKind.Incident,
|
|
46
|
+
buildItem: (id) => {
|
|
47
|
+
const severity = new IncidentSeverity();
|
|
48
|
+
severity._id = id;
|
|
49
|
+
return severity;
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
key: "alertSeverities",
|
|
54
|
+
kind: ComplianceSeverityKind.Alert,
|
|
55
|
+
buildItem: (id) => {
|
|
56
|
+
const severity = new AlertSeverity();
|
|
57
|
+
severity._id = id;
|
|
58
|
+
return severity;
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
];
|
|
14
62
|
export class TeamComplianceSettingService extends DatabaseService {
|
|
15
63
|
constructor() {
|
|
16
64
|
super(Model);
|
|
17
65
|
}
|
|
18
66
|
async onBeforeCreate(createBy) {
|
|
19
|
-
|
|
20
|
-
|
|
67
|
+
/*
|
|
68
|
+
* Authorisation first. DatabaseService.create only checks the caller's
|
|
69
|
+
* create permission AFTER this hook, and every check below reads as root
|
|
70
|
+
* in the project the request names - which a caller picks with a header,
|
|
71
|
+
* member of that project or not. Answering "that severity is not in this
|
|
72
|
+
* project" or "that team already has this rule" before the permission
|
|
73
|
+
* refusal would describe another project's severities and rules to
|
|
74
|
+
* anyone signed in. The columns this hook clears later carry the same
|
|
75
|
+
* create permissions, so the check DatabaseService repeats still passes.
|
|
76
|
+
*/
|
|
77
|
+
if (!createBy.props.isRoot && !createBy.props.isMasterAdmin) {
|
|
78
|
+
ModelPermission.checkCreatePermissions(this.modelType, createBy.data, createBy.props);
|
|
79
|
+
}
|
|
80
|
+
const ruleType = this.assertKnownRuleType(createBy.data.ruleType);
|
|
81
|
+
this.assertValidChannel(createBy.data.notificationChannel);
|
|
82
|
+
/*
|
|
83
|
+
* Normalise before anything is stored: a method rule carries no channel
|
|
84
|
+
* or severities, and an incident rule carries no alert severities. The
|
|
85
|
+
* form hides the fields that do not apply, but the API accepts whatever it
|
|
86
|
+
* is sent, and a stray alert severity on an incident rule would otherwise
|
|
87
|
+
* sit in the database looking like part of the rule.
|
|
88
|
+
*/
|
|
89
|
+
this.clearOptionsThatDoNotApply(createBy.data, ruleType, {
|
|
90
|
+
clearUnsentOptions: false,
|
|
91
|
+
});
|
|
92
|
+
TeamComplianceSettingService.normaliseSeverityLists(createBy.data);
|
|
93
|
+
const projectId = createBy.data.projectId || createBy.props.tenantId;
|
|
94
|
+
if (!projectId) {
|
|
95
|
+
throw new BadDataException("Project ID is required.");
|
|
96
|
+
}
|
|
97
|
+
if (!createBy.data.teamId) {
|
|
98
|
+
throw new BadDataException("Team ID is required.");
|
|
99
|
+
}
|
|
100
|
+
const scope = TeamComplianceSettingService.getScope({
|
|
101
|
+
ruleType: ruleType,
|
|
102
|
+
notificationChannel: createBy.data.notificationChannel,
|
|
103
|
+
incidentSeverities: createBy.data.incidentSeverities,
|
|
104
|
+
alertSeverities: createBy.data.alertSeverities,
|
|
105
|
+
});
|
|
106
|
+
await this.assertSeveritiesBelongToProject(projectId, scope);
|
|
107
|
+
await this.assertNoIdenticalRule({
|
|
108
|
+
teamId: createBy.data.teamId,
|
|
109
|
+
projectId: projectId,
|
|
110
|
+
scope: scope,
|
|
111
|
+
excludeSettingId: undefined,
|
|
112
|
+
});
|
|
113
|
+
return { createBy, carryForward: null };
|
|
114
|
+
}
|
|
115
|
+
/*
|
|
116
|
+
* Toggling a rule on or off touches none of the fields that define it, so a
|
|
117
|
+
* disable returns before reading anything: it must never fail because of
|
|
118
|
+
* the rule's configuration. Turning a rule ON reads one thing - whether a
|
|
119
|
+
* severity delete left it with nothing to check (SEVERITIES_DELETED_OPTION)
|
|
120
|
+
* - because turning such a rule on as it is would make it check every
|
|
121
|
+
* severity of its kind, a rule nobody wrote.
|
|
122
|
+
*/
|
|
123
|
+
async onBeforeUpdate(updateBy) {
|
|
124
|
+
const data = updateBy.data;
|
|
125
|
+
const changesScope = data["ruleType"] !== undefined ||
|
|
126
|
+
data["notificationChannel"] !== undefined ||
|
|
127
|
+
data["incidentSeverities"] !== undefined ||
|
|
128
|
+
data["alertSeverities"] !== undefined;
|
|
129
|
+
if (!changesScope) {
|
|
130
|
+
if (data["enabled"] === true) {
|
|
131
|
+
await this.assertNoRuleLeftWithoutSeveritiesIsTurnedOn(updateBy);
|
|
132
|
+
}
|
|
133
|
+
return { updateBy, carryForward: null };
|
|
134
|
+
}
|
|
135
|
+
/*
|
|
136
|
+
* As on create: _updateBy checks the caller's update permission only
|
|
137
|
+
* after this hook, and updateBy / updateOneBy do not check it before. The
|
|
138
|
+
* reads below are scoped to the caller's project, but that project is
|
|
139
|
+
* whatever the request named, so a caller who may not change rules there
|
|
140
|
+
* is refused before any of them can answer.
|
|
141
|
+
*/
|
|
142
|
+
if (!updateBy.props.isRoot && !updateBy.props.isMasterAdmin) {
|
|
143
|
+
await this.assertMayUpdate(updateBy);
|
|
144
|
+
}
|
|
145
|
+
const newRuleType = data["ruleType"] !== undefined
|
|
146
|
+
? this.assertKnownRuleType(data["ruleType"])
|
|
147
|
+
: undefined;
|
|
148
|
+
this.assertValidChannel(data["notificationChannel"]);
|
|
149
|
+
if (newRuleType) {
|
|
150
|
+
/*
|
|
151
|
+
* A new type makes the row's STORED options stale too, not only the ones
|
|
152
|
+
* this update sends: switching "Call for Critical incidents" to
|
|
153
|
+
* "Verified email" must not leave Call and Critical on the row, to come
|
|
154
|
+
* back as the scope if the type is ever switched again.
|
|
155
|
+
*/
|
|
156
|
+
this.clearOptionsThatDoNotApply(data, newRuleType, {
|
|
157
|
+
clearUnsentOptions: true,
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
/*
|
|
161
|
+
* The rows being changed, read to work out what each will check once the
|
|
162
|
+
* update lands. Scoped to the caller's project when there is one, so the
|
|
163
|
+
* rows of another project are never checked, let alone described.
|
|
164
|
+
*/
|
|
165
|
+
const existingSettings = await this.findBy({
|
|
166
|
+
query: TeamComplianceSettingService.getTargetQuery(updateBy),
|
|
167
|
+
select: {
|
|
168
|
+
_id: true,
|
|
169
|
+
teamId: true,
|
|
170
|
+
projectId: true,
|
|
171
|
+
ruleType: true,
|
|
172
|
+
notificationChannel: true,
|
|
173
|
+
options: true,
|
|
174
|
+
incidentSeverities: {
|
|
175
|
+
_id: true,
|
|
176
|
+
},
|
|
177
|
+
alertSeverities: {
|
|
178
|
+
_id: true,
|
|
179
|
+
},
|
|
180
|
+
},
|
|
181
|
+
limit: LIMIT_PER_PROJECT,
|
|
182
|
+
skip: 0,
|
|
183
|
+
props: {
|
|
184
|
+
isRoot: true,
|
|
185
|
+
},
|
|
186
|
+
});
|
|
187
|
+
if (!newRuleType) {
|
|
188
|
+
/*
|
|
189
|
+
* The type stays, so drop whatever this update sends that the type does
|
|
190
|
+
* not use - as create does - rather than store alert severities on an
|
|
191
|
+
* incident rule unvalidated. Only when every row being updated has the
|
|
192
|
+
* same type, because `data` is written to all of them.
|
|
193
|
+
*/
|
|
194
|
+
const storedRuleTypes = new Set();
|
|
195
|
+
for (const existing of existingSettings) {
|
|
196
|
+
if (ComplianceRule.isKnownRuleType(existing.ruleType)) {
|
|
197
|
+
storedRuleTypes.add(existing.ruleType);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const storedRuleType = Array.from(storedRuleTypes)[0];
|
|
201
|
+
if (storedRuleTypes.size === 1 && storedRuleType) {
|
|
202
|
+
this.clearOptionsThatDoNotApply(data, storedRuleType, {
|
|
203
|
+
clearUnsentOptions: false,
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
/*
|
|
208
|
+
* `data` is written to every row the update matches, so the lists it
|
|
209
|
+
* sends are normalised once, here, and each row below is checked against
|
|
210
|
+
* exactly what will be written to it.
|
|
211
|
+
*/
|
|
212
|
+
TeamComplianceSettingService.normaliseSeverityLists(data);
|
|
213
|
+
/*
|
|
214
|
+
* Every list this update sends is written to every row it matches, so it
|
|
215
|
+
* is checked against the project of every one of those rows - by the
|
|
216
|
+
* list's own kind, not by the row's type. A row whose stored type this
|
|
217
|
+
* build does not recognise (legacy data, or written by a newer build) is
|
|
218
|
+
* skipped by the scope checks below, and the relation save would
|
|
219
|
+
* otherwise link it to another project's severity unchecked.
|
|
220
|
+
*/
|
|
221
|
+
const projectIds = new Map();
|
|
222
|
+
for (const existing of existingSettings) {
|
|
223
|
+
if (existing.projectId) {
|
|
224
|
+
projectIds.set(existing.projectId.toString(), existing.projectId);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
for (const projectId of projectIds.values()) {
|
|
228
|
+
for (const list of SEVERITY_LISTS) {
|
|
229
|
+
if (data[list.key] === undefined) {
|
|
230
|
+
continue;
|
|
231
|
+
}
|
|
232
|
+
await this.assertSeveritiesBelongToProject(projectId, {
|
|
233
|
+
severityKind: list.kind,
|
|
234
|
+
severityIds: TeamComplianceSettingService.getIds(data[list.key]),
|
|
235
|
+
});
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
for (const existing of existingSettings) {
|
|
239
|
+
const ruleType = newRuleType ||
|
|
240
|
+
(ComplianceRule.isKnownRuleType(existing.ruleType)
|
|
241
|
+
? existing.ruleType
|
|
242
|
+
: undefined);
|
|
243
|
+
if (!ruleType || !existing.teamId || !existing.projectId) {
|
|
244
|
+
continue;
|
|
245
|
+
}
|
|
246
|
+
const scope = TeamComplianceSettingService.getScope({
|
|
247
|
+
ruleType: ruleType,
|
|
248
|
+
notificationChannel: data["notificationChannel"] !== undefined
|
|
249
|
+
? data["notificationChannel"]
|
|
250
|
+
: existing.notificationChannel,
|
|
251
|
+
incidentSeverities: data["incidentSeverities"] !== undefined
|
|
252
|
+
? data["incidentSeverities"]
|
|
253
|
+
: existing.incidentSeverities,
|
|
254
|
+
alertSeverities: data["alertSeverities"] !== undefined
|
|
255
|
+
? data["alertSeverities"]
|
|
256
|
+
: existing.alertSeverities,
|
|
257
|
+
});
|
|
258
|
+
/*
|
|
259
|
+
* A list this update sends was checked above; one it keeps is the
|
|
260
|
+
* stored list, re-checked here because a type change can make a list
|
|
261
|
+
* the rule did not use until now its scope.
|
|
262
|
+
*/
|
|
263
|
+
const isScopeListSent = SEVERITY_LISTS.some((list) => {
|
|
264
|
+
return (list.kind === scope.severityKind && data[list.key] !== undefined);
|
|
265
|
+
});
|
|
266
|
+
if (!isScopeListSent) {
|
|
267
|
+
await this.assertSeveritiesBelongToProject(existing.projectId, scope);
|
|
268
|
+
}
|
|
269
|
+
await this.assertNoIdenticalRule({
|
|
270
|
+
teamId: existing.teamId,
|
|
271
|
+
projectId: existing.projectId,
|
|
272
|
+
scope: scope,
|
|
273
|
+
excludeSettingId: existing.id || undefined,
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
/*
|
|
277
|
+
* The admin has chosen what the rule checks now - new severities, or
|
|
278
|
+
* deliberately every severity - so a rule a severity delete left empty
|
|
279
|
+
* is an ordinary rule again, in the same write.
|
|
280
|
+
*/
|
|
281
|
+
TeamComplianceSettingService.clearSeveritiesDeletedMark(data, existingSettings);
|
|
282
|
+
return { updateBy, carryForward: null };
|
|
283
|
+
}
|
|
284
|
+
/*
|
|
285
|
+
* An update that turns a rule on without re-scoping it is refused when a
|
|
286
|
+
* severity delete left the rule with nothing to check: turned on as it is,
|
|
287
|
+
* it would check every severity of its kind. The caller's permission is
|
|
288
|
+
* checked first, as for a scope change, so the refusal describes nothing
|
|
289
|
+
* to a caller who may not change the rule.
|
|
290
|
+
*/
|
|
291
|
+
async assertNoRuleLeftWithoutSeveritiesIsTurnedOn(updateBy) {
|
|
292
|
+
if (!updateBy.props.isRoot && !updateBy.props.isMasterAdmin) {
|
|
293
|
+
await this.assertMayUpdate(updateBy);
|
|
294
|
+
}
|
|
295
|
+
const rules = await this.findBy({
|
|
296
|
+
query: TeamComplianceSettingService.getTargetQuery(updateBy),
|
|
297
|
+
select: {
|
|
298
|
+
_id: true,
|
|
299
|
+
ruleType: true,
|
|
300
|
+
options: true,
|
|
301
|
+
incidentSeverities: {
|
|
302
|
+
_id: true,
|
|
303
|
+
},
|
|
304
|
+
alertSeverities: {
|
|
305
|
+
_id: true,
|
|
306
|
+
},
|
|
307
|
+
},
|
|
308
|
+
limit: LIMIT_PER_PROJECT,
|
|
309
|
+
skip: 0,
|
|
310
|
+
props: {
|
|
311
|
+
isRoot: true,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
if (rules.some((rule) => {
|
|
315
|
+
return TeamComplianceSettingService.isLeftWithoutSeverities(rule);
|
|
316
|
+
})) {
|
|
317
|
+
throw new BadDataException(SEVERITIES_DELETED_ENABLE_MESSAGE);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
/*
|
|
321
|
+
* The update's own query, narrowed to the caller's project when there is
|
|
322
|
+
* one - a copy, so the caller's query object is left as it was.
|
|
323
|
+
*/
|
|
324
|
+
static getTargetQuery(updateBy) {
|
|
325
|
+
const query = Object.assign({}, updateBy.query);
|
|
326
|
+
if (updateBy.props.tenantId) {
|
|
327
|
+
query["projectId"] = updateBy.props.tenantId;
|
|
328
|
+
}
|
|
329
|
+
return query;
|
|
330
|
+
}
|
|
331
|
+
/*
|
|
332
|
+
* True when TeamComplianceSetting.options carries SEVERITIES_DELETED_OPTION.
|
|
333
|
+
* Exported for the compliance status, which lists such a rule as paused
|
|
334
|
+
* with nothing to check.
|
|
335
|
+
*/
|
|
336
|
+
static hasSeveritiesDeletedMark(options) {
|
|
337
|
+
return (TeamComplianceSettingService.isPlainObject(options) &&
|
|
338
|
+
options[SEVERITIES_DELETED_OPTION] === true);
|
|
339
|
+
}
|
|
340
|
+
/*
|
|
341
|
+
* A rule a severity delete left with nothing to check: marked, of a type
|
|
342
|
+
* scoped by severity, and still without a severity of its kind. A mark on a
|
|
343
|
+
* rule that has severities again - it lost a race with a re-scope - is
|
|
344
|
+
* stale, and the rule is judged by the severities it has.
|
|
345
|
+
*/
|
|
346
|
+
static isLeftWithoutSeverities(rule) {
|
|
347
|
+
const kind = ComplianceRule.getSeverityKind(rule.ruleType);
|
|
348
|
+
return (Boolean(kind) &&
|
|
349
|
+
TeamComplianceSettingService.hasSeveritiesDeletedMark(rule.options) &&
|
|
350
|
+
TeamComplianceSettingService.getSeverityIdsOfKind(rule, kind).length ===
|
|
351
|
+
0);
|
|
352
|
+
}
|
|
353
|
+
/*
|
|
354
|
+
* Writes `options` without SEVERITIES_DELETED_OPTION, keeping every other
|
|
355
|
+
* key: the options sent with the update when it sends any, else the
|
|
356
|
+
* options stored on the rows it changes. `data` is written to every row
|
|
357
|
+
* the update matches, so stored options are only rewritten when those rows
|
|
358
|
+
* agree on them - otherwise one row's options would overwrite another's.
|
|
359
|
+
*/
|
|
360
|
+
static clearSeveritiesDeletedMark(data, existingSettings) {
|
|
361
|
+
var _a, _b;
|
|
362
|
+
if (data["options"] !== undefined) {
|
|
363
|
+
if (TeamComplianceSettingService.isPlainObject(data["options"]) &&
|
|
364
|
+
SEVERITIES_DELETED_OPTION in data["options"]) {
|
|
365
|
+
data["options"] = TeamComplianceSettingService.withoutMark(data["options"]);
|
|
366
|
+
}
|
|
367
|
+
return;
|
|
368
|
+
}
|
|
369
|
+
const isAnyMarked = existingSettings.some((existing) => {
|
|
370
|
+
return TeamComplianceSettingService.hasSeveritiesDeletedMark(existing.options);
|
|
371
|
+
});
|
|
372
|
+
if (!isAnyMarked) {
|
|
373
|
+
return;
|
|
374
|
+
}
|
|
375
|
+
const cleared = existingSettings.map((existing) => {
|
|
376
|
+
return TeamComplianceSettingService.isPlainObject(existing.options)
|
|
377
|
+
? TeamComplianceSettingService.withoutMark(existing.options)
|
|
378
|
+
: null;
|
|
379
|
+
});
|
|
380
|
+
const first = JSON.stringify((_a = cleared[0]) !== null && _a !== void 0 ? _a : null);
|
|
381
|
+
if (cleared.some((options) => {
|
|
382
|
+
return JSON.stringify(options) !== first;
|
|
383
|
+
})) {
|
|
384
|
+
throw new BadDataException(OPTIONS_DIFFER_MESSAGE);
|
|
385
|
+
}
|
|
386
|
+
data["options"] = (_b = cleared[0]) !== null && _b !== void 0 ? _b : null;
|
|
387
|
+
}
|
|
388
|
+
// A copy of `options` without the mark; null when nothing else is left.
|
|
389
|
+
static withoutMark(options) {
|
|
390
|
+
const rest = Object.assign({}, options);
|
|
391
|
+
delete rest[SEVERITIES_DELETED_OPTION];
|
|
392
|
+
return Object.keys(rest).length > 0 ? rest : null;
|
|
393
|
+
}
|
|
394
|
+
static isPlainObject(value) {
|
|
395
|
+
return Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
396
|
+
}
|
|
397
|
+
/*
|
|
398
|
+
* SEVERITY DELETES. A rule's severity scope is stored only as join rows,
|
|
399
|
+
* and those cascade away with the severity. A rule left with no severity
|
|
400
|
+
* reads exactly like one created for every severity of its kind, so
|
|
401
|
+
* deleting the only severity "Call for Critical incidents" was scoped to
|
|
402
|
+
* would quietly turn it into "Call for every incident severity" - failing
|
|
403
|
+
* members against a rule nobody wrote.
|
|
404
|
+
*
|
|
405
|
+
* The severity services therefore ask, before the delete and while the join
|
|
406
|
+
* rows still say what each rule was scoped to, which rules reference any of
|
|
407
|
+
* the severities about to go (getRulesScopedToAnyOf), and once the delete
|
|
408
|
+
* has actually happened they have those that were left with none paused
|
|
409
|
+
* and marked (pauseRulesLeftWithoutSeverities, SEVERITIES_DELETED_OPTION).
|
|
410
|
+
* The Compliance page lists a marked rule as having lost its severities,
|
|
411
|
+
* for an admin to re-scope or delete; it cannot be turned back on as it is.
|
|
412
|
+
* Pausing only after the delete means a delete that is refused - by the
|
|
413
|
+
* permission layer, or because incidents still use the severity - pauses
|
|
414
|
+
* nothing.
|
|
415
|
+
*
|
|
416
|
+
* ANY reference is carried forward, not only rules scoped to nothing but
|
|
417
|
+
* the severities this delete removes: two deletes running side by side -
|
|
418
|
+
* the two severities of a "Critical and Major" rule, deleted by Terraform
|
|
419
|
+
* in parallel - each see the other's severity still attached before
|
|
420
|
+
* either commits, so neither would carry the rule, and it would be left
|
|
421
|
+
* scoped to nothing and checking everything. Each request re-reads after
|
|
422
|
+
* its own delete has committed, so whichever commits last sees the rule
|
|
423
|
+
* empty and pauses it. A rule that still has another severity is left
|
|
424
|
+
* alone by the re-read.
|
|
425
|
+
*
|
|
426
|
+
* Paused rules are carried too: turning one on later must not make it
|
|
427
|
+
* check every severity either.
|
|
428
|
+
*/
|
|
429
|
+
async getRulesScopedToAnyOf(data) {
|
|
430
|
+
var _a, _b;
|
|
431
|
+
const deletedIdsByProject = new Map();
|
|
432
|
+
for (const severity of data.severities) {
|
|
433
|
+
const id = (_a = severity._id) === null || _a === void 0 ? void 0 : _a.toString().toLowerCase();
|
|
434
|
+
const projectId = (_b = severity.projectId) === null || _b === void 0 ? void 0 : _b.toString();
|
|
435
|
+
if (!id || !projectId) {
|
|
436
|
+
continue;
|
|
437
|
+
}
|
|
438
|
+
const ids = deletedIdsByProject.get(projectId) || new Set();
|
|
439
|
+
ids.add(id);
|
|
440
|
+
deletedIdsByProject.set(projectId, ids);
|
|
441
|
+
}
|
|
442
|
+
const ruleTypes = Object.values(ComplianceRuleType).filter((ruleType) => {
|
|
443
|
+
return ComplianceRule.getSeverityKind(ruleType) === data.severityKind;
|
|
444
|
+
});
|
|
445
|
+
const settingIds = [];
|
|
446
|
+
for (const [projectId, deletedIds] of deletedIdsByProject) {
|
|
447
|
+
const rules = await this.findBy({
|
|
448
|
+
query: {
|
|
449
|
+
projectId: new ObjectID(projectId),
|
|
450
|
+
ruleType: new Includes(ruleTypes),
|
|
451
|
+
},
|
|
452
|
+
select: {
|
|
453
|
+
_id: true,
|
|
454
|
+
ruleType: true,
|
|
455
|
+
incidentSeverities: {
|
|
456
|
+
_id: true,
|
|
457
|
+
},
|
|
458
|
+
alertSeverities: {
|
|
459
|
+
_id: true,
|
|
460
|
+
},
|
|
461
|
+
},
|
|
462
|
+
limit: LIMIT_PER_PROJECT,
|
|
463
|
+
skip: 0,
|
|
464
|
+
props: {
|
|
465
|
+
isRoot: true,
|
|
466
|
+
},
|
|
467
|
+
});
|
|
468
|
+
for (const rule of rules) {
|
|
469
|
+
if (rule._id &&
|
|
470
|
+
ComplianceRule.getSeverityKind(rule.ruleType) === data.severityKind &&
|
|
471
|
+
TeamComplianceSettingService.getSeverityIdsOfKind(rule, data.severityKind).some((id) => {
|
|
472
|
+
return deletedIds.has(id);
|
|
473
|
+
})) {
|
|
474
|
+
settingIds.push(rule._id.toString());
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return settingIds;
|
|
479
|
+
}
|
|
480
|
+
/*
|
|
481
|
+
* The second half of a severity delete: of the rules getRulesScopedToAnyOf
|
|
482
|
+
* named, pause and mark (SEVERITIES_DELETED_OPTION) the ones now left with
|
|
483
|
+
* no severity of their kind, in one write per rule's own options so every
|
|
484
|
+
* other key they hold is kept. Re-read rather than assumed, so a rule
|
|
485
|
+
* whose severity survived (the delete matched fewer rows than the lookup
|
|
486
|
+
* did, another of its severities is still there, or it was re-scoped in the
|
|
487
|
+
* meantime) stays as it is.
|
|
488
|
+
*/
|
|
489
|
+
async pauseRulesLeftWithoutSeverities(data) {
|
|
490
|
+
if (data.settingIds.length === 0) {
|
|
491
|
+
return [];
|
|
492
|
+
}
|
|
493
|
+
const rules = await this.findBy({
|
|
21
494
|
query: {
|
|
22
|
-
|
|
23
|
-
|
|
495
|
+
_id: new Includes(data.settingIds),
|
|
496
|
+
},
|
|
497
|
+
select: {
|
|
498
|
+
_id: true,
|
|
499
|
+
ruleType: true,
|
|
500
|
+
enabled: true,
|
|
501
|
+
options: true,
|
|
502
|
+
incidentSeverities: {
|
|
503
|
+
_id: true,
|
|
504
|
+
},
|
|
505
|
+
alertSeverities: {
|
|
506
|
+
_id: true,
|
|
507
|
+
},
|
|
24
508
|
},
|
|
509
|
+
limit: LIMIT_PER_PROJECT,
|
|
510
|
+
skip: 0,
|
|
25
511
|
props: {
|
|
26
512
|
isRoot: true,
|
|
27
513
|
},
|
|
514
|
+
});
|
|
515
|
+
// Rule ids by the options they are written with, marked.
|
|
516
|
+
const toPause = new Map();
|
|
517
|
+
const paused = [];
|
|
518
|
+
for (const rule of rules) {
|
|
519
|
+
if (!rule._id ||
|
|
520
|
+
ComplianceRule.getSeverityKind(rule.ruleType) !== data.severityKind ||
|
|
521
|
+
TeamComplianceSettingService.getSeverityIdsOfKind(rule, data.severityKind).length > 0) {
|
|
522
|
+
continue;
|
|
523
|
+
}
|
|
524
|
+
const isAlreadyPausedAndMarked = rule.enabled === false &&
|
|
525
|
+
TeamComplianceSettingService.hasSeveritiesDeletedMark(rule.options);
|
|
526
|
+
if (isAlreadyPausedAndMarked) {
|
|
527
|
+
continue;
|
|
528
|
+
}
|
|
529
|
+
const options = Object.assign(Object.assign({}, (TeamComplianceSettingService.isPlainObject(rule.options)
|
|
530
|
+
? rule.options
|
|
531
|
+
: {})), { [SEVERITIES_DELETED_OPTION]: true });
|
|
532
|
+
const key = JSON.stringify(options);
|
|
533
|
+
const group = toPause.get(key) || { options: options, ids: [] };
|
|
534
|
+
group.ids.push(rule._id.toString());
|
|
535
|
+
toPause.set(key, group);
|
|
536
|
+
paused.push(rule._id.toString());
|
|
537
|
+
}
|
|
538
|
+
for (const group of toPause.values()) {
|
|
539
|
+
/*
|
|
540
|
+
* Neither the type, the channel nor a severity list: the update hook
|
|
541
|
+
* reads nothing for it. (Cast: the write type of a JSON column is too
|
|
542
|
+
* deep for the compiler to spell out.)
|
|
543
|
+
*/
|
|
544
|
+
const pause = {
|
|
545
|
+
enabled: false,
|
|
546
|
+
options: group.options,
|
|
547
|
+
};
|
|
548
|
+
await this.updateBy({
|
|
549
|
+
query: {
|
|
550
|
+
_id: new Includes(group.ids),
|
|
551
|
+
},
|
|
552
|
+
data: pause,
|
|
553
|
+
limit: group.ids.length,
|
|
554
|
+
skip: 0,
|
|
555
|
+
props: {
|
|
556
|
+
isRoot: true,
|
|
557
|
+
},
|
|
558
|
+
});
|
|
559
|
+
}
|
|
560
|
+
return paused;
|
|
561
|
+
}
|
|
562
|
+
/*
|
|
563
|
+
* What a rule checks, from its stored (or about-to-be-stored) fields. Only
|
|
564
|
+
* the options its type uses count: a channel on a method rule, or alert
|
|
565
|
+
* severities on an incident rule, change nothing about what is checked.
|
|
566
|
+
*/
|
|
567
|
+
static getScope(data) {
|
|
568
|
+
const severityKind = ComplianceRule.getSeverityKind(data.ruleType);
|
|
569
|
+
const notificationChannel = ComplianceRule.supportsChannel(data.ruleType) &&
|
|
570
|
+
ComplianceRule.isKnownChannel(data.notificationChannel)
|
|
571
|
+
? data.notificationChannel
|
|
572
|
+
: null;
|
|
573
|
+
let severityIds = [];
|
|
574
|
+
if (severityKind === ComplianceSeverityKind.Incident) {
|
|
575
|
+
severityIds = TeamComplianceSettingService.getIds(data.incidentSeverities);
|
|
576
|
+
}
|
|
577
|
+
else if (severityKind === ComplianceSeverityKind.Alert) {
|
|
578
|
+
severityIds = TeamComplianceSettingService.getIds(data.alertSeverities);
|
|
579
|
+
}
|
|
580
|
+
return {
|
|
581
|
+
ruleType: data.ruleType,
|
|
582
|
+
notificationChannel: notificationChannel,
|
|
583
|
+
severityKind: severityKind || null,
|
|
584
|
+
severityIds: severityIds,
|
|
585
|
+
};
|
|
586
|
+
}
|
|
587
|
+
static isSameScope(a, b) {
|
|
588
|
+
return (a.ruleType === b.ruleType &&
|
|
589
|
+
a.notificationChannel === b.notificationChannel &&
|
|
590
|
+
a.severityIds.join(",") === b.severityIds.join(","));
|
|
591
|
+
}
|
|
592
|
+
/*
|
|
593
|
+
* Severity ids from a severity list as it is STORED (or once a payload has
|
|
594
|
+
* been through normaliseSeverityLists): an id string, an ObjectID, a model,
|
|
595
|
+
* or `{_id}` / `{id}` JSON. Sorted and de-duplicated so two lists compare by
|
|
596
|
+
* content, and lower-cased because Postgres compares uuids without case and
|
|
597
|
+
* hands them back lower case: "ABC..." sent by a client is the stored
|
|
598
|
+
* "abc...", and must not slip past the duplicate check as a different id.
|
|
599
|
+
*
|
|
600
|
+
* Lenient on purpose - an unreadable item is skipped - so it is NOT the
|
|
601
|
+
* reader for a payload: what the relation save writes is decided by
|
|
602
|
+
* DatabaseService.sanitizeCreateOrUpdate, and resolveSentSeverityIds reads
|
|
603
|
+
* a payload exactly the way that does.
|
|
604
|
+
*/
|
|
605
|
+
static getIds(value) {
|
|
606
|
+
if (!Array.isArray(value)) {
|
|
607
|
+
return [];
|
|
608
|
+
}
|
|
609
|
+
const ids = new Set();
|
|
610
|
+
for (const item of value) {
|
|
611
|
+
let id = undefined;
|
|
612
|
+
if (typeof item === "string") {
|
|
613
|
+
id = item;
|
|
614
|
+
}
|
|
615
|
+
else if (item instanceof ObjectID) {
|
|
616
|
+
id = item.toString();
|
|
617
|
+
}
|
|
618
|
+
else if (item && typeof item === "object") {
|
|
619
|
+
const candidate = item["_id"] || item["id"];
|
|
620
|
+
if (typeof candidate === "string") {
|
|
621
|
+
id = candidate;
|
|
622
|
+
}
|
|
623
|
+
else if (candidate instanceof ObjectID) {
|
|
624
|
+
id = candidate.toString();
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
if (id && id.trim()) {
|
|
628
|
+
ids.add(id.trim().toLowerCase());
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
return Array.from(ids).sort();
|
|
632
|
+
}
|
|
633
|
+
/*
|
|
634
|
+
* The severity ids a SENT list asks the relation save to write, read item
|
|
635
|
+
* by item with exactly the precedence DatabaseService.sanitizeCreateOrUpdate
|
|
636
|
+
* uses - an id string or ObjectID, else a string `_id`, else a string `id`,
|
|
637
|
+
* else a model's own `_id` - so the ids this service validates are the ids
|
|
638
|
+
* that would be written. An item that yields no valid uuid is refused
|
|
639
|
+
* rather than skipped: the sanitizer would drop it (or write garbage), and
|
|
640
|
+
* a list that silently lost its only item is a rule for EVERY severity.
|
|
641
|
+
*
|
|
642
|
+
* Trimmed, lower-cased (Postgres hands uuids back lower case, and TypeORM
|
|
643
|
+
* compares join rows as case-sensitive strings) and de-duplicated (the join
|
|
644
|
+
* table's primary key refuses the same severity twice), first occurrence
|
|
645
|
+
* kept. Null is an empty list, as the relation save treats it; anything
|
|
646
|
+
* else that is not a list is refused.
|
|
647
|
+
*/
|
|
648
|
+
static resolveSentSeverityIds(value, kind) {
|
|
649
|
+
if (value === null) {
|
|
650
|
+
return [];
|
|
651
|
+
}
|
|
652
|
+
const noun = kind === ComplianceSeverityKind.Incident ? "incident" : "alert";
|
|
653
|
+
if (!Array.isArray(value)) {
|
|
654
|
+
throw new BadDataException(`The ${noun} severities of a compliance rule must be a list of severity ids.`);
|
|
655
|
+
}
|
|
656
|
+
const ids = [];
|
|
657
|
+
for (const item of value) {
|
|
658
|
+
const id = TeamComplianceSettingService.resolveWrittenId(item);
|
|
659
|
+
if (id === undefined) {
|
|
660
|
+
throw new BadDataException(`Every ${noun} severity of a compliance rule must be a severity id.`);
|
|
661
|
+
}
|
|
662
|
+
const normalised = id.trim().toLowerCase();
|
|
663
|
+
ObjectID.validateUUID(normalised);
|
|
664
|
+
if (!ids.includes(normalised)) {
|
|
665
|
+
ids.push(normalised);
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
return ids;
|
|
669
|
+
}
|
|
670
|
+
/*
|
|
671
|
+
* Replaces each severity list a payload sends with the list
|
|
672
|
+
* resolveSentSeverityIds read from it, as models carrying only `_id`. From
|
|
673
|
+
* here on the scope that is validated and compared, and the join rows the
|
|
674
|
+
* relation save writes, are one and the same list. Lists that are not sent
|
|
675
|
+
* stay unsent.
|
|
676
|
+
*/
|
|
677
|
+
static normaliseSeverityLists(data) {
|
|
678
|
+
const target = data;
|
|
679
|
+
for (const list of SEVERITY_LISTS) {
|
|
680
|
+
if (target[list.key] === undefined) {
|
|
681
|
+
continue;
|
|
682
|
+
}
|
|
683
|
+
target[list.key] = TeamComplianceSettingService.resolveSentSeverityIds(target[list.key], list.kind).map((id) => {
|
|
684
|
+
return list.buildItem(id);
|
|
685
|
+
});
|
|
686
|
+
}
|
|
687
|
+
}
|
|
688
|
+
// DatabaseService.sanitizeCreateOrUpdate's reading of one EntityArray item.
|
|
689
|
+
static resolveWrittenId(item) {
|
|
690
|
+
if (typeof item === "string" || item instanceof ObjectID) {
|
|
691
|
+
return item.toString();
|
|
692
|
+
}
|
|
693
|
+
if (!item || typeof item !== "object") {
|
|
694
|
+
return undefined;
|
|
695
|
+
}
|
|
696
|
+
const json = item;
|
|
697
|
+
if (json["_id"] && typeof json["_id"] === "string") {
|
|
698
|
+
return json["_id"];
|
|
699
|
+
}
|
|
700
|
+
if (json["id"] && typeof json["id"] === "string") {
|
|
701
|
+
return json["id"];
|
|
702
|
+
}
|
|
703
|
+
/*
|
|
704
|
+
* A model reaches this line only when its `_id` is empty or not a string,
|
|
705
|
+
* and the sanitizer writes it as it is - with no id a uuid column takes.
|
|
706
|
+
*/
|
|
707
|
+
return undefined;
|
|
708
|
+
}
|
|
709
|
+
static getSeverityIdsOfKind(rule, kind) {
|
|
710
|
+
return TeamComplianceSettingService.getIds(kind === ComplianceSeverityKind.Incident
|
|
711
|
+
? rule.incidentSeverities
|
|
712
|
+
: rule.alertSeverities);
|
|
713
|
+
}
|
|
714
|
+
/*
|
|
715
|
+
* The update permission check _updateBy makes after this hook, made first.
|
|
716
|
+
* It narrows a copy of the query; this hook's own reads add the project
|
|
717
|
+
* themselves, so only the refusal matters here.
|
|
718
|
+
*/
|
|
719
|
+
async assertMayUpdate(updateBy) {
|
|
720
|
+
await ModelPermission.checkUpdateQueryPermissions(this.modelType, Object.assign({}, updateBy.query), updateBy.data, updateBy.props);
|
|
721
|
+
}
|
|
722
|
+
assertKnownRuleType(value) {
|
|
723
|
+
if (!ComplianceRule.isKnownRuleType(value)) {
|
|
724
|
+
throw new BadDataException(`"${String(value)}" is not a compliance rule type.`);
|
|
725
|
+
}
|
|
726
|
+
return value;
|
|
727
|
+
}
|
|
728
|
+
// Null / undefined is "any channel" and always valid.
|
|
729
|
+
assertValidChannel(value) {
|
|
730
|
+
if (value === undefined || value === null) {
|
|
731
|
+
return;
|
|
732
|
+
}
|
|
733
|
+
if (!ComplianceRule.isKnownChannel(value)) {
|
|
734
|
+
throw new BadDataException(`"${String(value)}" is not a notification channel a compliance rule can require.`);
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
/*
|
|
738
|
+
* `clearUnsentOptions` also clears the options `data` does not mention, so
|
|
739
|
+
* an update writes them empty on the row instead of leaving what is stored.
|
|
740
|
+
*/
|
|
741
|
+
clearOptionsThatDoNotApply(data, ruleType, options) {
|
|
742
|
+
const target = data;
|
|
743
|
+
const severityKind = ComplianceRule.getSeverityKind(ruleType);
|
|
744
|
+
if (!ComplianceRule.supportsChannel(ruleType)) {
|
|
745
|
+
if (options.clearUnsentOptions ||
|
|
746
|
+
(target["notificationChannel"] !== undefined &&
|
|
747
|
+
target["notificationChannel"] !== null)) {
|
|
748
|
+
target["notificationChannel"] = null;
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
if (severityKind !== ComplianceSeverityKind.Incident &&
|
|
752
|
+
(options.clearUnsentOptions || target["incidentSeverities"] !== undefined)) {
|
|
753
|
+
target["incidentSeverities"] = [];
|
|
754
|
+
}
|
|
755
|
+
if (severityKind !== ComplianceSeverityKind.Alert &&
|
|
756
|
+
(options.clearUnsentOptions || target["alertSeverities"] !== undefined)) {
|
|
757
|
+
target["alertSeverities"] = [];
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
/*
|
|
761
|
+
* The severity relation accepts any id, and the compliance status is read as
|
|
762
|
+
* root, so a severity from another project would otherwise be stored against
|
|
763
|
+
* this rule and have its name shown on this project's page.
|
|
764
|
+
*/
|
|
765
|
+
async assertSeveritiesBelongToProject(projectId, scope) {
|
|
766
|
+
if (scope.severityIds.length === 0 || !scope.severityKind) {
|
|
767
|
+
return;
|
|
768
|
+
}
|
|
769
|
+
for (const id of scope.severityIds) {
|
|
770
|
+
ObjectID.validateUUID(id);
|
|
771
|
+
}
|
|
772
|
+
const count = scope.severityKind === ComplianceSeverityKind.Incident
|
|
773
|
+
? await IncidentSeverityService.countBy({
|
|
774
|
+
query: {
|
|
775
|
+
_id: new Includes(scope.severityIds),
|
|
776
|
+
projectId: projectId,
|
|
777
|
+
},
|
|
778
|
+
props: {
|
|
779
|
+
isRoot: true,
|
|
780
|
+
},
|
|
781
|
+
})
|
|
782
|
+
: await AlertSeverityService.countBy({
|
|
783
|
+
query: {
|
|
784
|
+
_id: new Includes(scope.severityIds),
|
|
785
|
+
projectId: projectId,
|
|
786
|
+
},
|
|
787
|
+
props: {
|
|
788
|
+
isRoot: true,
|
|
789
|
+
},
|
|
790
|
+
});
|
|
791
|
+
if (count.toNumber() !== scope.severityIds.length) {
|
|
792
|
+
throw new BadDataException(`One or more of the selected ${scope.severityKind === ComplianceSeverityKind.Incident ? "incident" : "alert"} severities do not exist in this project.`);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
async assertNoIdenticalRule(data) {
|
|
796
|
+
const query = {
|
|
797
|
+
teamId: data.teamId,
|
|
798
|
+
projectId: data.projectId,
|
|
799
|
+
ruleType: data.scope.ruleType,
|
|
800
|
+
};
|
|
801
|
+
if (data.excludeSettingId) {
|
|
802
|
+
query["_id"] = QueryHelper.notEquals(data.excludeSettingId.toString());
|
|
803
|
+
}
|
|
804
|
+
const siblings = await this.findBy({
|
|
805
|
+
query: query,
|
|
28
806
|
select: {
|
|
29
807
|
_id: true,
|
|
808
|
+
ruleType: true,
|
|
809
|
+
notificationChannel: true,
|
|
810
|
+
options: true,
|
|
811
|
+
incidentSeverities: {
|
|
812
|
+
_id: true,
|
|
813
|
+
},
|
|
814
|
+
alertSeverities: {
|
|
815
|
+
_id: true,
|
|
816
|
+
},
|
|
817
|
+
},
|
|
818
|
+
limit: LIMIT_PER_PROJECT,
|
|
819
|
+
skip: 0,
|
|
820
|
+
props: {
|
|
821
|
+
isRoot: true,
|
|
30
822
|
},
|
|
31
823
|
});
|
|
32
|
-
|
|
33
|
-
|
|
824
|
+
for (const sibling of siblings) {
|
|
825
|
+
/*
|
|
826
|
+
* A rule a severity delete left with nothing to check is stored with
|
|
827
|
+
* no severities, which is also how a rule for EVERY severity is
|
|
828
|
+
* stored. It checks nothing and cannot be turned on until it is
|
|
829
|
+
* re-scoped - when it is compared again - so it duplicates nothing.
|
|
830
|
+
*/
|
|
831
|
+
if (TeamComplianceSettingService.isLeftWithoutSeverities(sibling)) {
|
|
832
|
+
continue;
|
|
833
|
+
}
|
|
834
|
+
const siblingScope = TeamComplianceSettingService.getScope({
|
|
835
|
+
ruleType: data.scope.ruleType,
|
|
836
|
+
notificationChannel: sibling.notificationChannel,
|
|
837
|
+
incidentSeverities: sibling.incidentSeverities,
|
|
838
|
+
alertSeverities: sibling.alertSeverities,
|
|
839
|
+
});
|
|
840
|
+
if (TeamComplianceSettingService.isSameScope(siblingScope, data.scope)) {
|
|
841
|
+
throw new BadDataException(DUPLICATE_COMPLIANCE_RULE_MESSAGE);
|
|
842
|
+
}
|
|
34
843
|
}
|
|
35
|
-
return { createBy, carryForward: null };
|
|
36
844
|
}
|
|
37
845
|
}
|
|
38
846
|
__decorate([
|
|
@@ -41,5 +849,23 @@ __decorate([
|
|
|
41
849
|
__metadata("design:paramtypes", [Object]),
|
|
42
850
|
__metadata("design:returntype", Promise)
|
|
43
851
|
], TeamComplianceSettingService.prototype, "onBeforeCreate", null);
|
|
852
|
+
__decorate([
|
|
853
|
+
CaptureSpan(),
|
|
854
|
+
__metadata("design:type", Function),
|
|
855
|
+
__metadata("design:paramtypes", [Object]),
|
|
856
|
+
__metadata("design:returntype", Promise)
|
|
857
|
+
], TeamComplianceSettingService.prototype, "onBeforeUpdate", null);
|
|
858
|
+
__decorate([
|
|
859
|
+
CaptureSpan(),
|
|
860
|
+
__metadata("design:type", Function),
|
|
861
|
+
__metadata("design:paramtypes", [Object]),
|
|
862
|
+
__metadata("design:returntype", Promise)
|
|
863
|
+
], TeamComplianceSettingService.prototype, "getRulesScopedToAnyOf", null);
|
|
864
|
+
__decorate([
|
|
865
|
+
CaptureSpan(),
|
|
866
|
+
__metadata("design:type", Function),
|
|
867
|
+
__metadata("design:paramtypes", [Object]),
|
|
868
|
+
__metadata("design:returntype", Promise)
|
|
869
|
+
], TeamComplianceSettingService.prototype, "pauseRulesLeftWithoutSeverities", null);
|
|
44
870
|
export default new TeamComplianceSettingService();
|
|
45
871
|
//# sourceMappingURL=TeamComplianceSettingService.js.map
|