Extract thresholds recovery defaults owner

This commit is contained in:
rcourtman
2026-03-22 16:17:47 +00:00
parent d3b9bc5eda
commit ba92ddb194
9 changed files with 375 additions and 210 deletions
@@ -132,6 +132,8 @@ so governed agent, guest, and storage rows do not leak raw names when the
threshold editor saves or re-renders them.
That threshold editor data shaping now lives under
`frontend-modern/src/features/alerts/thresholds/hooks/useThresholdsData.ts`,
backup and snapshot default sanitization plus factory-drift policy now live in
`frontend-modern/src/features/alerts/thresholds/hooks/useThresholdsRecoveryDefaultsState.ts`,
while `frontend-modern/src/features/alerts/thresholds/hooks/useThresholdsTableState.ts`
owns threshold-table route sync, section collapse state, search/edit shell
state, and bulk-edit dialog control. Override persistence, bulk threshold
@@ -236,9 +236,12 @@ inline.
The thresholds editor now follows that same split more tightly:
`frontend-modern/src/features/alerts/thresholds/hooks/useThresholdsTableState.ts`
must stay the table-shell owner for route sync and local UI state, while
`frontend-modern/src/features/alerts/thresholds/hooks/useThresholdsRecoveryDefaultsState.ts`
owns backup/snapshot default sanitization and factory-drift policy, and
`frontend-modern/src/features/alerts/thresholds/hooks/useThresholdsOverrideMutations.ts`
owns override save/bulk/toggle persistence and alert-removal side effects. The
table-shell hook should not re-accumulate raw override mutation logic inline.
table-shell hook should not re-accumulate raw override mutation logic or
recovery-threshold defaults policy inline.
The updates settings surface now follows the same presentation-owner rule.
`frontend-modern/src/components/Settings/UpdatesSettingsPanel.tsx` stays the
@@ -430,6 +433,7 @@ is now explicitly a feature consumer rather than the data or controller owner.
Canonical threshold row shaping, override-ID compatibility, grouped resource
normalization, and thresholds-table controller state live in
`frontend-modern/src/features/alerts/thresholds/hooks/useThresholdsData.ts`,
`frontend-modern/src/features/alerts/thresholds/hooks/useThresholdsRecoveryDefaultsState.ts`,
`frontend-modern/src/features/alerts/thresholds/hooks/useThresholdsTableState.ts`,
so future cleanup should extend those feature hooks instead of rebuilding
resource normalization or thresholds-table runtime state inside the table
@@ -0,0 +1,122 @@
import { afterEach, describe, expect, it } from 'vitest';
import { cleanup, render } from '@solidjs/testing-library';
import type { ThresholdsTableProps } from '@/features/alerts/thresholds/types';
import { useThresholdsRecoveryDefaultsState } from '../useThresholdsRecoveryDefaultsState';
const buildProps = (): ThresholdsTableProps =>
({
backupDefaults: () => ({
alertOrphaned: false,
criticalDays: 8,
enabled: true,
freshHours: 24,
ignoreVMIDs: ['100', '200'],
staleHours: 72,
warningDays: 4,
}),
backupFactoryDefaults: {
alertOrphaned: true,
criticalDays: 7,
enabled: false,
freshHours: 12,
ignoreVMIDs: ['100'],
staleHours: 48,
warningDays: 3,
},
snapshotDefaults: () => ({
criticalDays: 10,
criticalSizeGiB: 18,
enabled: true,
warningDays: 6,
warningSizeGiB: 12,
}),
snapshotFactoryDefaults: {
criticalDays: 7,
criticalSizeGiB: 16,
enabled: false,
warningDays: 3,
warningSizeGiB: 8,
},
}) as ThresholdsTableProps;
afterEach(() => {
cleanup();
});
describe('useThresholdsRecoveryDefaultsState', () => {
it('owns backup and snapshot default-policy sanitization', () => {
let captured: ReturnType<typeof useThresholdsRecoveryDefaultsState> | undefined;
const Harness = () => {
captured = useThresholdsRecoveryDefaultsState(buildProps());
return null;
};
render(() => <Harness />);
expect(captured).toBeDefined();
expect(
captured!.sanitizeSnapshotConfig({
criticalDays: 4,
criticalSizeGiB: 1.08,
enabled: true,
warningDays: 9,
warningSizeGiB: 3.16,
}),
).toEqual({
criticalDays: 4,
criticalSizeGiB: 1.1,
enabled: true,
warningDays: 4,
warningSizeGiB: 1.1,
});
expect(
captured!.sanitizeBackupConfig({
alertOrphaned: false,
criticalDays: 2,
enabled: true,
freshHours: 9,
ignoreVMIDs: ['101', ' ', '101', '202'],
staleHours: 4,
warningDays: 7,
}),
).toEqual({
alertOrphaned: false,
criticalDays: 2,
enabled: true,
freshHours: 9,
ignoreVMIDs: ['101', '202'],
staleHours: 9,
warningDays: 2,
});
});
it('tracks factory drift for backup and snapshot defaults', () => {
let captured: ReturnType<typeof useThresholdsRecoveryDefaultsState> | undefined;
const Harness = () => {
captured = useThresholdsRecoveryDefaultsState(buildProps());
return null;
};
render(() => <Harness />);
expect(captured).toBeDefined();
expect(captured!.snapshotDefaultsRecord()).toEqual({
'critical days': 10,
'critical size (gib)': 18,
'warning days': 6,
'warning size (gib)': 12,
});
expect(captured!.backupFactoryDefaultsRecord()).toEqual({
'critical days': 7,
'fresh hours': 12,
'stale hours': 48,
'warning days': 3,
});
expect(captured!.snapshotOverridesCount()).toBe(1);
expect(captured!.backupOverridesCount()).toBe(1);
});
});
@@ -30,10 +30,6 @@ vi.mock('../useThresholdsData', () => ({
agentDisksGroupedByAgent: () => ({}),
agentDisksWithOverrides: () => [],
agentsWithOverrides: () => [],
backupDefaultsRecord: () => ({}),
backupFactoryConfig: () => ({ enabled: false }),
backupFactoryDefaultsRecord: () => ({}),
backupOverridesCount: () => 0,
dockerContainersFlat: () => [],
dockerContainersGroupedByHost: () => ({}),
dockerHostGroupMeta: () => ({}),
@@ -45,15 +41,24 @@ vi.mock('../useThresholdsData', () => ({
pbsServersWithOverrides: () => [],
pmgGlobalDefaults: () => ({}),
pmgServersWithOverrides: () => [],
storageGroupedByNode: () => ({}),
storageWithOverrides: () => [],
totalDockerContainers: () => 0,
}),
}));
vi.mock('../useThresholdsRecoveryDefaultsState', () => ({
useThresholdsRecoveryDefaultsState: () => ({
backupDefaultsRecord: () => ({}),
backupFactoryConfig: () => ({ enabled: false }),
backupFactoryDefaultsRecord: () => ({}),
backupOverridesCount: () => 0,
sanitizeBackupConfig: <T,>(value: T) => value,
sanitizeSnapshotConfig: <T,>(value: T) => value,
snapshotDefaultsRecord: () => ({}),
snapshotFactoryConfig: () => ({ enabled: false }),
snapshotFactoryDefaultsRecord: () => ({}),
snapshotOverridesCount: () => 0,
storageGroupedByNode: () => ({}),
storageWithOverrides: () => [],
totalDockerContainers: () => 0,
}),
}));
@@ -14,16 +14,8 @@ import {
PMG_THRESHOLD_COLUMNS,
PMG_KEY_TO_NORMALIZED,
PMG_NORMALIZED_TO_KEY,
DEFAULT_SNAPSHOT_WARNING,
DEFAULT_SNAPSHOT_CRITICAL,
DEFAULT_SNAPSHOT_WARNING_SIZE,
DEFAULT_SNAPSHOT_CRITICAL_SIZE,
DEFAULT_BACKUP_WARNING,
DEFAULT_BACKUP_CRITICAL,
DEFAULT_BACKUP_FRESH_HOURS,
DEFAULT_BACKUP_STALE_HOURS,
} from '../constants';
import type { PMGThresholdDefaults, SnapshotAlertConfig, BackupAlertConfig } from '@/types/alerts';
import type { PMGThresholdDefaults } from '@/types/alerts';
import type { ThresholdsTableProps, Override } from '../types';
import type { GroupHeaderMeta, Resource as TableResource } from '../tableTypes';
@@ -773,179 +765,6 @@ export function useThresholdsData(
return meta;
});
const snapshotFactoryConfig = () =>
props.snapshotFactoryDefaults ?? {
enabled: false,
warningDays: DEFAULT_SNAPSHOT_WARNING,
criticalDays: DEFAULT_SNAPSHOT_CRITICAL,
warningSizeGiB: DEFAULT_SNAPSHOT_WARNING_SIZE,
criticalSizeGiB: DEFAULT_SNAPSHOT_CRITICAL_SIZE,
};
const sanitizeSnapshotConfig = (config: SnapshotAlertConfig): SnapshotAlertConfig => {
let warning = Math.max(0, Math.round(config.warningDays ?? 0));
let critical = Math.max(0, Math.round(config.criticalDays ?? 0));
if (critical > 0 && warning > critical) {
warning = critical;
}
if (critical === 0 && warning > 0) {
critical = warning;
}
const rawWarningSize = Number.isFinite(config.warningSizeGiB)
? Number(config.warningSizeGiB)
: DEFAULT_SNAPSHOT_WARNING_SIZE;
const rawCriticalSize = Number.isFinite(config.criticalSizeGiB)
? Number(config.criticalSizeGiB)
: DEFAULT_SNAPSHOT_CRITICAL_SIZE;
const roundSize = (value: number) => Math.round(Math.max(0, value) * 10) / 10;
let warningSize = roundSize(rawWarningSize);
let criticalSize = roundSize(rawCriticalSize);
if (criticalSize > 0 && warningSize > criticalSize) {
warningSize = criticalSize;
}
if (criticalSize === 0 && warningSize > 0) {
criticalSize = warningSize;
}
return {
enabled: !!config.enabled,
warningDays: warning,
criticalDays: critical,
warningSizeGiB: warningSize,
criticalSizeGiB: criticalSize,
};
};
const backupFactoryConfig = () =>
props.backupFactoryDefaults ?? {
enabled: false,
warningDays: DEFAULT_BACKUP_WARNING,
criticalDays: DEFAULT_BACKUP_CRITICAL,
freshHours: DEFAULT_BACKUP_FRESH_HOURS,
staleHours: DEFAULT_BACKUP_STALE_HOURS,
alertOrphaned: true,
ignoreVMIDs: [],
};
const sanitizeBackupConfig = (config: BackupAlertConfig): BackupAlertConfig => {
let warning = Math.max(0, Math.round(config.warningDays ?? 0));
let critical = Math.max(0, Math.round(config.criticalDays ?? 0));
let fresh = Math.max(0, Math.round(config.freshHours ?? DEFAULT_BACKUP_FRESH_HOURS));
let stale = Math.max(0, Math.round(config.staleHours ?? DEFAULT_BACKUP_STALE_HOURS));
const alertOrphaned = config.alertOrphaned ?? true;
const ignoreVMIDs = Array.from(
new Set(
(config.ignoreVMIDs ?? []).map((value) => value.trim()).filter((value) => value.length > 0),
),
);
if (critical > 0 && warning > critical) {
warning = critical;
}
if (critical === 0 && warning > 0) {
critical = warning;
}
// Ensure stale is at least fresh
if (stale < fresh) {
stale = fresh;
}
return {
enabled: !!config.enabled,
warningDays: warning,
criticalDays: critical,
freshHours: fresh,
staleHours: stale,
alertOrphaned,
ignoreVMIDs,
};
};
const snapshotDefaultsRecord = createMemo(() => {
const current = props.snapshotDefaults();
return {
'warning days': current.warningDays ?? 0,
'critical days': current.criticalDays ?? 0,
'warning size (gib)': current.warningSizeGiB ?? 0,
'critical size (gib)': current.criticalSizeGiB ?? 0,
};
});
const snapshotFactoryDefaultsRecord = createMemo(() => {
const factory = snapshotFactoryConfig();
return {
'warning days': factory.warningDays ?? DEFAULT_SNAPSHOT_WARNING,
'critical days': factory.criticalDays ?? DEFAULT_SNAPSHOT_CRITICAL,
'warning size (gib)': factory.warningSizeGiB ?? DEFAULT_SNAPSHOT_WARNING_SIZE,
'critical size (gib)': factory.criticalSizeGiB ?? DEFAULT_SNAPSHOT_CRITICAL_SIZE,
};
});
const backupDefaultsRecord = createMemo(() => {
const current = props.backupDefaults();
return {
'fresh hours': current.freshHours ?? DEFAULT_BACKUP_FRESH_HOURS,
'stale hours': current.staleHours ?? DEFAULT_BACKUP_STALE_HOURS,
'warning days': current.warningDays ?? 0,
'critical days': current.criticalDays ?? 0,
};
});
const backupFactoryDefaultsRecord = createMemo(() => {
const factory = backupFactoryConfig();
return {
'fresh hours': factory.freshHours ?? DEFAULT_BACKUP_FRESH_HOURS,
'stale hours': factory.staleHours ?? DEFAULT_BACKUP_STALE_HOURS,
'warning days': factory.warningDays ?? DEFAULT_BACKUP_WARNING,
'critical days': factory.criticalDays ?? DEFAULT_BACKUP_CRITICAL,
};
});
const snapshotOverridesCount = createMemo(() => {
const current = props.snapshotDefaults();
const factory = snapshotFactoryConfig();
const differs =
current.enabled !== factory.enabled ||
(current.warningDays ?? DEFAULT_SNAPSHOT_WARNING) !==
(factory.warningDays ?? DEFAULT_SNAPSHOT_WARNING) ||
(current.criticalDays ?? DEFAULT_SNAPSHOT_CRITICAL) !==
(factory.criticalDays ?? DEFAULT_SNAPSHOT_CRITICAL) ||
(current.warningSizeGiB ?? DEFAULT_SNAPSHOT_WARNING_SIZE) !==
(factory.warningSizeGiB ?? DEFAULT_SNAPSHOT_WARNING_SIZE) ||
(current.criticalSizeGiB ?? DEFAULT_SNAPSHOT_CRITICAL_SIZE) !==
(factory.criticalSizeGiB ?? DEFAULT_SNAPSHOT_CRITICAL_SIZE);
return differs ? 1 : 0;
});
const backupOverridesCount = createMemo(() => {
const backupCurrent = props.backupDefaults();
const backupFactory = backupFactoryConfig();
const currentIgnore = backupCurrent.ignoreVMIDs ?? [];
const factoryIgnore = backupFactory.ignoreVMIDs ?? [];
const ignoreDiff =
currentIgnore.length !== factoryIgnore.length ||
currentIgnore.some((value, index) => value !== factoryIgnore[index]);
return backupCurrent.enabled !== backupFactory.enabled ||
(backupCurrent.warningDays ?? DEFAULT_BACKUP_WARNING) !==
(backupFactory.warningDays ?? DEFAULT_BACKUP_WARNING) ||
(backupCurrent.criticalDays ?? DEFAULT_BACKUP_CRITICAL) !==
(backupFactory.criticalDays ?? DEFAULT_BACKUP_CRITICAL) ||
(backupCurrent.freshHours ?? DEFAULT_BACKUP_FRESH_HOURS) !==
(backupFactory.freshHours ?? DEFAULT_BACKUP_FRESH_HOURS) ||
(backupCurrent.staleHours ?? DEFAULT_BACKUP_STALE_HOURS) !==
(backupFactory.staleHours ?? DEFAULT_BACKUP_STALE_HOURS) ||
(backupCurrent.alertOrphaned ?? true) !== (backupFactory.alertOrphaned ?? true) ||
ignoreDiff
? 1
: 0;
});
// Process guests with their overrides and group by node
const guestsGroupedByNode = createMemo<Record<string, TableResource[]>>((prev = {}) => {
// If we're currently editing, return the previous value to avoid re-renders
@@ -1294,16 +1113,6 @@ export function useThresholdsData(
dockerContainersFlat,
totalDockerContainers,
dockerHostGroupMeta,
snapshotFactoryConfig,
sanitizeSnapshotConfig,
backupFactoryConfig,
sanitizeBackupConfig,
snapshotDefaultsRecord,
snapshotFactoryDefaultsRecord,
backupDefaultsRecord,
backupFactoryDefaultsRecord,
snapshotOverridesCount,
backupOverridesCount,
guestsGroupedByNode,
guestsFlat,
guestGroupHeaderMeta,
@@ -0,0 +1,201 @@
import { createMemo } from 'solid-js';
import type { BackupAlertConfig, SnapshotAlertConfig } from '@/types/alerts';
import type { ThresholdsTableProps } from '@/features/alerts/thresholds/types';
import {
DEFAULT_BACKUP_CRITICAL,
DEFAULT_BACKUP_FRESH_HOURS,
DEFAULT_BACKUP_STALE_HOURS,
DEFAULT_BACKUP_WARNING,
DEFAULT_SNAPSHOT_CRITICAL,
DEFAULT_SNAPSHOT_CRITICAL_SIZE,
DEFAULT_SNAPSHOT_WARNING,
DEFAULT_SNAPSHOT_WARNING_SIZE,
} from '../constants';
export function useThresholdsRecoveryDefaultsState(props: ThresholdsTableProps) {
const snapshotFactoryConfig = () =>
props.snapshotFactoryDefaults ?? {
enabled: false,
warningDays: DEFAULT_SNAPSHOT_WARNING,
criticalDays: DEFAULT_SNAPSHOT_CRITICAL,
warningSizeGiB: DEFAULT_SNAPSHOT_WARNING_SIZE,
criticalSizeGiB: DEFAULT_SNAPSHOT_CRITICAL_SIZE,
};
const sanitizeSnapshotConfig = (config: SnapshotAlertConfig): SnapshotAlertConfig => {
let warning = Math.max(0, Math.round(config.warningDays ?? 0));
let critical = Math.max(0, Math.round(config.criticalDays ?? 0));
if (critical > 0 && warning > critical) {
warning = critical;
}
if (critical === 0 && warning > 0) {
critical = warning;
}
const rawWarningSize = Number.isFinite(config.warningSizeGiB)
? Number(config.warningSizeGiB)
: DEFAULT_SNAPSHOT_WARNING_SIZE;
const rawCriticalSize = Number.isFinite(config.criticalSizeGiB)
? Number(config.criticalSizeGiB)
: DEFAULT_SNAPSHOT_CRITICAL_SIZE;
const roundSize = (value: number) => Math.round(Math.max(0, value) * 10) / 10;
let warningSize = roundSize(rawWarningSize);
let criticalSize = roundSize(rawCriticalSize);
if (criticalSize > 0 && warningSize > criticalSize) {
warningSize = criticalSize;
}
if (criticalSize === 0 && warningSize > 0) {
criticalSize = warningSize;
}
return {
enabled: !!config.enabled,
warningDays: warning,
criticalDays: critical,
warningSizeGiB: warningSize,
criticalSizeGiB: criticalSize,
};
};
const backupFactoryConfig = () =>
props.backupFactoryDefaults ?? {
enabled: false,
warningDays: DEFAULT_BACKUP_WARNING,
criticalDays: DEFAULT_BACKUP_CRITICAL,
freshHours: DEFAULT_BACKUP_FRESH_HOURS,
staleHours: DEFAULT_BACKUP_STALE_HOURS,
alertOrphaned: true,
ignoreVMIDs: [],
};
const sanitizeBackupConfig = (config: BackupAlertConfig): BackupAlertConfig => {
let warning = Math.max(0, Math.round(config.warningDays ?? 0));
let critical = Math.max(0, Math.round(config.criticalDays ?? 0));
let fresh = Math.max(0, Math.round(config.freshHours ?? DEFAULT_BACKUP_FRESH_HOURS));
let stale = Math.max(0, Math.round(config.staleHours ?? DEFAULT_BACKUP_STALE_HOURS));
const alertOrphaned = config.alertOrphaned ?? true;
const ignoreVMIDs = Array.from(
new Set(
(config.ignoreVMIDs ?? []).map((value) => value.trim()).filter((value) => value.length > 0),
),
);
if (critical > 0 && warning > critical) {
warning = critical;
}
if (critical === 0 && warning > 0) {
critical = warning;
}
if (stale < fresh) {
stale = fresh;
}
return {
enabled: !!config.enabled,
warningDays: warning,
criticalDays: critical,
freshHours: fresh,
staleHours: stale,
alertOrphaned,
ignoreVMIDs,
};
};
const snapshotDefaultsRecord = createMemo(() => {
const current = props.snapshotDefaults();
return {
'warning days': current.warningDays ?? 0,
'critical days': current.criticalDays ?? 0,
'warning size (gib)': current.warningSizeGiB ?? 0,
'critical size (gib)': current.criticalSizeGiB ?? 0,
};
});
const snapshotFactoryDefaultsRecord = createMemo(() => {
const factory = snapshotFactoryConfig();
return {
'warning days': factory.warningDays ?? DEFAULT_SNAPSHOT_WARNING,
'critical days': factory.criticalDays ?? DEFAULT_SNAPSHOT_CRITICAL,
'warning size (gib)': factory.warningSizeGiB ?? DEFAULT_SNAPSHOT_WARNING_SIZE,
'critical size (gib)': factory.criticalSizeGiB ?? DEFAULT_SNAPSHOT_CRITICAL_SIZE,
};
});
const backupDefaultsRecord = createMemo(() => {
const current = props.backupDefaults();
return {
'fresh hours': current.freshHours ?? DEFAULT_BACKUP_FRESH_HOURS,
'stale hours': current.staleHours ?? DEFAULT_BACKUP_STALE_HOURS,
'warning days': current.warningDays ?? 0,
'critical days': current.criticalDays ?? 0,
};
});
const backupFactoryDefaultsRecord = createMemo(() => {
const factory = backupFactoryConfig();
return {
'fresh hours': factory.freshHours ?? DEFAULT_BACKUP_FRESH_HOURS,
'stale hours': factory.staleHours ?? DEFAULT_BACKUP_STALE_HOURS,
'warning days': factory.warningDays ?? DEFAULT_BACKUP_WARNING,
'critical days': factory.criticalDays ?? DEFAULT_BACKUP_CRITICAL,
};
});
const snapshotOverridesCount = createMemo(() => {
const current = props.snapshotDefaults();
const factory = snapshotFactoryConfig();
const differs =
current.enabled !== factory.enabled ||
(current.warningDays ?? DEFAULT_SNAPSHOT_WARNING) !==
(factory.warningDays ?? DEFAULT_SNAPSHOT_WARNING) ||
(current.criticalDays ?? DEFAULT_SNAPSHOT_CRITICAL) !==
(factory.criticalDays ?? DEFAULT_SNAPSHOT_CRITICAL) ||
(current.warningSizeGiB ?? DEFAULT_SNAPSHOT_WARNING_SIZE) !==
(factory.warningSizeGiB ?? DEFAULT_SNAPSHOT_WARNING_SIZE) ||
(current.criticalSizeGiB ?? DEFAULT_SNAPSHOT_CRITICAL_SIZE) !==
(factory.criticalSizeGiB ?? DEFAULT_SNAPSHOT_CRITICAL_SIZE);
return differs ? 1 : 0;
});
const backupOverridesCount = createMemo(() => {
const current = props.backupDefaults();
const factory = backupFactoryConfig();
const currentIgnore = current.ignoreVMIDs ?? [];
const factoryIgnore = factory.ignoreVMIDs ?? [];
const ignoreDiff =
currentIgnore.length !== factoryIgnore.length ||
currentIgnore.some((value, index) => value !== factoryIgnore[index]);
return current.enabled !== factory.enabled ||
(current.warningDays ?? DEFAULT_BACKUP_WARNING) !==
(factory.warningDays ?? DEFAULT_BACKUP_WARNING) ||
(current.criticalDays ?? DEFAULT_BACKUP_CRITICAL) !==
(factory.criticalDays ?? DEFAULT_BACKUP_CRITICAL) ||
(current.freshHours ?? DEFAULT_BACKUP_FRESH_HOURS) !==
(factory.freshHours ?? DEFAULT_BACKUP_FRESH_HOURS) ||
(current.staleHours ?? DEFAULT_BACKUP_STALE_HOURS) !==
(factory.staleHours ?? DEFAULT_BACKUP_STALE_HOURS) ||
(current.alertOrphaned ?? true) !== (factory.alertOrphaned ?? true) ||
ignoreDiff
? 1
: 0;
});
return {
backupDefaultsRecord,
backupFactoryConfig,
backupFactoryDefaultsRecord,
backupOverridesCount,
sanitizeBackupConfig,
sanitizeSnapshotConfig,
snapshotDefaultsRecord,
snapshotFactoryConfig,
snapshotFactoryDefaultsRecord,
snapshotOverridesCount,
};
}
@@ -40,6 +40,7 @@ import type {
} from '@/features/alerts/thresholds/tableTypes';
import { useCollapsedSections } from '@/components/Alerts/Thresholds/hooks/useCollapsedSections';
import { useThresholdsData } from './useThresholdsData';
import { useThresholdsRecoveryDefaultsState } from './useThresholdsRecoveryDefaultsState';
import { useThresholdsOverrideMutations } from './useThresholdsOverrideMutations';
const HELP_BANNER_KEY = 'pulse-thresholds-help-dismissed';
@@ -189,16 +190,6 @@ export function useThresholdsTableState(props: ThresholdsTableProps) {
dockerContainersFlat,
totalDockerContainers,
dockerHostGroupMeta,
snapshotFactoryConfig,
sanitizeSnapshotConfig,
backupFactoryConfig,
sanitizeBackupConfig,
snapshotDefaultsRecord,
snapshotFactoryDefaultsRecord,
backupDefaultsRecord,
backupFactoryDefaultsRecord,
snapshotOverridesCount,
backupOverridesCount,
guestsGroupedByNode,
guestsFlat,
guestGroupHeaderMeta,
@@ -209,6 +200,19 @@ export function useThresholdsTableState(props: ThresholdsTableProps) {
storageGroupedByNode,
} = useThresholdsData(props, editingId, searchTerm);
const {
backupDefaultsRecord,
backupFactoryConfig,
backupFactoryDefaultsRecord,
backupOverridesCount,
sanitizeBackupConfig,
sanitizeSnapshotConfig,
snapshotDefaultsRecord,
snapshotFactoryConfig,
snapshotFactoryDefaultsRecord,
snapshotOverridesCount,
} = useThresholdsRecoveryDefaultsState(props);
const countOverrides = (resources: TableResource[] | undefined) =>
resources?.filter(
(resource) => resource.hasOverride || resource.disabled || resource.disableConnectivity,
@@ -17,6 +17,7 @@ import alertThresholdsTabSource from '@/features/alerts/tabs/ThresholdsTab.tsx?r
import recentAlertsPanelSource from '@/components/Alerts/RecentAlertsPanel.tsx?raw';
import thresholdsTableSource from '@/components/Alerts/ThresholdsTable.tsx?raw';
import thresholdsDataHookSource from '@/features/alerts/thresholds/hooks/useThresholdsData.ts?raw';
import thresholdsRecoveryDefaultsStateHookSource from '@/features/alerts/thresholds/hooks/useThresholdsRecoveryDefaultsState.ts?raw';
import thresholdsTableStateHookSource from '@/features/alerts/thresholds/hooks/useThresholdsTableState.ts?raw';
import thresholdsOverrideMutationsHookSource from '@/features/alerts/thresholds/hooks/useThresholdsOverrideMutations.ts?raw';
@@ -276,11 +277,19 @@ describe('tab path helpers', () => {
expect(thresholdsDataHookSource).toContain('export function useThresholdsData');
expect(thresholdsTableStateHookSource).toContain('export function useThresholdsTableState');
expect(thresholdsTableStateHookSource).toContain('useThresholdsData(props, editingId, searchTerm)');
expect(thresholdsTableStateHookSource).toContain('useThresholdsRecoveryDefaultsState(props)');
expect(thresholdsTableStateHookSource).toContain('useThresholdsOverrideMutations');
expect(thresholdsTableStateHookSource).not.toContain('const saveEdit = (resourceId: string) => {');
expect(thresholdsTableStateHookSource).not.toContain(
'const toggleNodeConnectivity = (resourceId: string, forceState?: boolean) => {',
);
expect(thresholdsDataHookSource).not.toContain('const sanitizeSnapshotConfig =');
expect(thresholdsDataHookSource).not.toContain('const sanitizeBackupConfig =');
expect(thresholdsRecoveryDefaultsStateHookSource).toContain(
'export function useThresholdsRecoveryDefaultsState',
);
expect(thresholdsRecoveryDefaultsStateHookSource).toContain('const sanitizeSnapshotConfig =');
expect(thresholdsRecoveryDefaultsStateHookSource).toContain('const sanitizeBackupConfig =');
expect(thresholdsOverrideMutationsHookSource).toContain(
'export function useThresholdsOverrideMutations',
);
@@ -339,6 +339,7 @@ import alertScheduleTabSource from '@/features/alerts/tabs/ScheduleTab.tsx?raw';
import alertThresholdsTabSource from '@/features/alerts/tabs/ThresholdsTab.tsx?raw';
import thresholdsTableSource from '@/components/Alerts/ThresholdsTable.tsx?raw';
import thresholdsDataHookSource from '@/features/alerts/thresholds/hooks/useThresholdsData.ts?raw';
import thresholdsRecoveryDefaultsStateHookSource from '@/features/alerts/thresholds/hooks/useThresholdsRecoveryDefaultsState.ts?raw';
import thresholdsTableStateHookSource from '@/features/alerts/thresholds/hooks/useThresholdsTableState.ts?raw';
import thresholdsOverrideMutationsHookSource from '@/features/alerts/thresholds/hooks/useThresholdsOverrideMutations.ts?raw';
import alertIncidentPresentationSource from '@/utils/alertIncidentPresentation.ts?raw';
@@ -2888,11 +2889,19 @@ describe('frontend resource type boundaries', () => {
expect(thresholdsTableStateHookSource).toContain('export function useThresholdsTableState');
expect(thresholdsTableStateHookSource).toContain('useCollapsedSections()');
expect(thresholdsTableStateHookSource).toContain('useThresholdsData(props, editingId, searchTerm)');
expect(thresholdsTableStateHookSource).toContain('useThresholdsRecoveryDefaultsState(props)');
expect(thresholdsTableStateHookSource).toContain('useThresholdsOverrideMutations');
expect(thresholdsTableStateHookSource).not.toContain('const saveEdit = (resourceId: string) => {');
expect(thresholdsTableStateHookSource).not.toContain(
'const toggleNodeConnectivity = (resourceId: string, forceState?: boolean) => {',
);
expect(thresholdsDataHookSource).not.toContain('const sanitizeSnapshotConfig =');
expect(thresholdsDataHookSource).not.toContain('const sanitizeBackupConfig =');
expect(thresholdsRecoveryDefaultsStateHookSource).toContain(
'export function useThresholdsRecoveryDefaultsState',
);
expect(thresholdsRecoveryDefaultsStateHookSource).toContain('const sanitizeSnapshotConfig =');
expect(thresholdsRecoveryDefaultsStateHookSource).toContain('const sanitizeBackupConfig =');
expect(thresholdsOverrideMutationsHookSource).toContain(
'export function useThresholdsOverrideMutations',
);