Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions src/config/degradationConfig.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
export type ServiceName = 'streaming' | 'payment';
export type FeatureFlagKey = 'streaming_courses' | 'payment_form';

export interface FeatureFlagEntry {
flagKey: FeatureFlagKey;
adminOverride?: boolean;
}

export const HEALTH_TO_FEATURE_MAP: Record<ServiceName, FeatureFlagEntry[]> = {
streaming: [{ flagKey: 'streaming_courses', adminOverride: false }],
payment: [{ flagKey: 'payment_form', adminOverride: false }],
};

export const DEGRADED_STATUSES = ['degraded', 'down', 'error', 'unhealthy'] as const;
export type DegradedStatus = (typeof DEGRADED_STATUSES)[number];

export function isServiceDegraded(status: string): boolean {
return (DEGRADED_STATUSES as readonly string[]).includes(status);
}
98 changes: 62 additions & 36 deletions src/store/healthDashboardStore.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,4 @@
/**
* healthDashboardStore — Zustand store for the real-time health dashboard.
*
* Holds the latest health snapshot, active alerts, user-configured thresholds,
* and polling state. Intentionally NOT persisted — always starts fresh.
*/

import { create } from 'zustand';
import { create } from 'zustand';
import { devtools } from 'zustand/middleware';

import {
Expand All @@ -15,33 +8,29 @@ import {
MetricAlert,
} from '../services/healthMetrics';
import { shallowDiff } from '../utils/stateDiff';

// ─── Types ────────────────────────────────────────────────────────────────────
import { isServiceDegraded, HEALTH_TO_FEATURE_MAP, ServiceName } from '../config/degradationConfig';
import { useFeatureFlagStore } from './featureFlagStore';

export type DashboardStatus = 'idle' | 'polling' | 'error';

export interface ServiceHealthStatus {
service: ServiceName;
status: string;
}

interface HealthDashboardState {
// Data
snapshot: HealthSnapshot | null;
alerts: MetricAlert[];
thresholds: AlertThresholds;

// UI state
serviceHealthStatuses: ServiceHealthStatus[];
status: DashboardStatus;
lastUpdated: number | null;
/** Timestamp of last successful data read (cache or network) */
lastChecked: number | null;
/** Timestamp of last actual network API call */
lastNetworkCheck: number | null;
/** How often to refresh in ms */
refreshIntervalMs: number;
/** Whether auto-refresh is active */
isAutoRefresh: boolean;
/** Dismissed alert IDs (cleared on next full refresh) */
dismissedAlertIds: Set<string>;

// Actions
setSnapshot: (snapshot: HealthSnapshot) => void;
setSnapshot: (snapshot: HealthSnapshot, serviceStatuses?: ServiceHealthStatus[]) => void;
setAlerts: (alerts: MetricAlert[]) => void;
setThresholds: (thresholds: Partial<AlertThresholds>) => void;
setStatus: (status: DashboardStatus) => void;
Expand All @@ -54,38 +43,69 @@ interface HealthDashboardState {
reset: () => void;
}

// ─── Initial state ────────────────────────────────────────────────────────────

const initialState = {
snapshot: null,
alerts: [],
thresholds: DEFAULT_THRESHOLDS,
serviceHealthStatuses: [] as ServiceHealthStatus[],
status: 'idle' as DashboardStatus,
lastUpdated: null,
lastChecked: null,
lastNetworkCheck: null,
refreshIntervalMs: 10_000, // 10 seconds default
refreshIntervalMs: 10_000,
isAutoRefresh: true,
dismissedAlertIds: new Set<string>(),
};

// ─── Store ────────────────────────────────────────────────────────────────────
function applyDegradationFlags(serviceStatuses: ServiceHealthStatus[]): void {
const flagStore = useFeatureFlagStore.getState();
for (const { service, status } of serviceStatuses) {
const flagEntries = HEALTH_TO_FEATURE_MAP[service];
if (!flagEntries) continue;
const degraded = isServiceDegraded(status);
for (const entry of flagEntries) {
if (entry.adminOverride) continue;
const currentDef = flagStore.getDefinition(entry.flagKey);
const updatedDef = { ...(currentDef ?? {}), enabled: !degraded };
useFeatureFlagStore.setState(state => ({
flags: {
...state.flags,
flags: { ...state.flags.flags, [entry.flagKey]: updatedDef },
},
}));
}
}
}

export const useHealthDashboardStore = create<HealthDashboardState>()(
devtools(
set => ({
...initialState,

setSnapshot: snapshot =>
setSnapshot: (snapshot, serviceStatuses) =>
set(
state => {
if (!state.snapshot && !snapshot) return state;
if (!state.snapshot) return { snapshot, lastUpdated: Date.now() };
const diff = shallowDiff(state.snapshot, snapshot);
if (!diff) return state;
let nextSnapshot: HealthSnapshot;
if (!state.snapshot) {
nextSnapshot = snapshot;
} else {
const diff = shallowDiff(state.snapshot, snapshot);
if (!diff) {
if (serviceStatuses && serviceStatuses.length > 0) {
applyDegradationFlags(serviceStatuses);
}
return state;
}
nextSnapshot = { ...state.snapshot, ...diff } as HealthSnapshot;
}
if (serviceStatuses && serviceStatuses.length > 0) {
applyDegradationFlags(serviceStatuses);
}
return {
snapshot: { ...state.snapshot, ...diff } as HealthSnapshot,
snapshot: nextSnapshot,
lastUpdated: Date.now(),
serviceHealthStatuses: serviceStatuses ?? state.serviceHealthStatuses,
};
},
false,
Expand All @@ -98,7 +118,7 @@ export const useHealthDashboardStore = create<HealthDashboardState>()(
set(
state => {
const diff = shallowDiff(state.thresholds, partial);
if (!diff) return state; // Return unchanged state to bypass allocation
if (!diff) return state;
return { thresholds: { ...state.thresholds, ...diff } };
},
false,
Expand Down Expand Up @@ -141,16 +161,22 @@ export const useHealthDashboardStore = create<HealthDashboardState>()(
)
);

// ─── Selectors ────────────────────────────────────────────────────────────────

/** Returns only non-dismissed alerts */
export const selectVisibleAlerts = (state: HealthDashboardState): MetricAlert[] =>
state.alerts.filter(a => !state.dismissedAlertIds.has(a.id));

/** Returns the highest severity across all visible alerts */
export const selectOverallStatus = (state: HealthDashboardState): 'ok' | 'warning' | 'critical' => {
export const selectOverallStatus = (
state: HealthDashboardState
): 'ok' | 'warning' | 'critical' => {
const visible = selectVisibleAlerts(state);
if (visible.some(a => a.severity === 'critical')) return 'critical';
if (visible.some(a => a.severity === 'warning')) return 'warning';
return 'ok';
};

export const selectIsServiceDegraded =
(service: ServiceName) =>
(state: HealthDashboardState): boolean => {
const entry = state.serviceHealthStatuses.find(s => s.service === service);
if (!entry) return false;
return isServiceDegraded(entry.status);
};
145 changes: 145 additions & 0 deletions tests/store/healthDashboardStore.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,145 @@
import { beforeEach, describe, expect, it } from '@jest/globals';
import { useHealthDashboardStore, selectIsServiceDegraded } from '../../src/store/healthDashboardStore';
import { useFeatureFlagStore } from '../../src/store/featureFlagStore';
import { HEALTH_TO_FEATURE_MAP } from '../../src/config/degradationConfig';

function makeSnapshot(overrides = {}) {
return {
capturedAt: Date.now(),
crashCount: 0, errorCount: 0, crashRate: 0, errorRatePerMinute: 0,
apiLatencyP50: 50, apiLatencyP95: 100, apiLatencyP99: 200,
apiCallCount: 10, apiErrorCount: 0, apiErrorRate: 0,
activeSessions: 5, totalSessionsInWindow: 5,
fps: 60, jsBusyRatio: 0.1, isOnline: true, networkType: 'wifi',
...overrides,
};
}

beforeEach(() => {
useHealthDashboardStore.getState().reset();
useFeatureFlagStore.setState(state => ({
flags: {
...state.flags,
flags: {
...state.flags.flags,
streaming_courses: { enabled: true },
payment_form: { enabled: true },
},
},
}));
});

describe('Health-check to Feature Flag auto-disable', () => {

describe('Degradation', () => {
it('disables streaming_courses when streaming is degraded', () => {
useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'streaming', status: 'degraded' },
]);
expect(useFeatureFlagStore.getState().isEnabled('streaming_courses', true)).toBe(false);
});

it('disables streaming_courses when streaming status is down', () => {
useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'streaming', status: 'down' },
]);
expect(useFeatureFlagStore.getState().isEnabled('streaming_courses', true)).toBe(false);
});

it('disables payment_form when payment is degraded', () => {
useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'payment', status: 'degraded' },
]);
expect(useFeatureFlagStore.getState().isEnabled('payment_form', true)).toBe(false);
});
});

describe('Fallback UI state', () => {
it('selectIsServiceDegraded returns true for a degraded service', () => {
useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'streaming', status: 'degraded' },
]);
expect(
selectIsServiceDegraded('streaming')(useHealthDashboardStore.getState())
).toBe(true);
});

it('selectIsServiceDegraded returns false for a healthy service', () => {
useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'streaming', status: 'healthy' },
]);
expect(
selectIsServiceDegraded('streaming')(useHealthDashboardStore.getState())
).toBe(false);
});

it('selectIsServiceDegraded returns false when no status recorded yet', () => {
expect(
selectIsServiceDegraded('streaming')(useHealthDashboardStore.getState())
).toBe(false);
});
});

describe('Recovery', () => {
it('re-enables streaming_courses after recovery', () => {
useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'streaming', status: 'degraded' },
]);
expect(useFeatureFlagStore.getState().isEnabled('streaming_courses', true)).toBe(false);

useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'streaming', status: 'healthy' },
]);
expect(useFeatureFlagStore.getState().isEnabled('streaming_courses', true)).toBe(true);
});

it('re-enables payment_form after payment service recovers', () => {
useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'payment', status: 'down' },
]);
expect(useFeatureFlagStore.getState().isEnabled('payment_form', true)).toBe(false);

useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'payment', status: 'healthy' },
]);
expect(useFeatureFlagStore.getState().isEnabled('payment_form', true)).toBe(true);
});

it('only re-enables the recovered service, not unrelated flags', () => {
useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'streaming', status: 'degraded' },
{ service: 'payment', status: 'degraded' },
]);
useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'streaming', status: 'healthy' },
{ service: 'payment', status: 'degraded' },
]);
expect(useFeatureFlagStore.getState().isEnabled('streaming_courses', true)).toBe(true);
expect(useFeatureFlagStore.getState().isEnabled('payment_form', true)).toBe(false);
});
});

describe('Admin override', () => {
it('does NOT disable the flag when adminOverride is true', () => {
const original = HEALTH_TO_FEATURE_MAP.streaming[0].adminOverride;
HEALTH_TO_FEATURE_MAP.streaming[0].adminOverride = true;
try {
useHealthDashboardStore.getState().setSnapshot(makeSnapshot(), [
{ service: 'streaming', status: 'degraded' },
]);
expect(useFeatureFlagStore.getState().isEnabled('streaming_courses', true)).toBe(true);
} finally {
HEALTH_TO_FEATURE_MAP.streaming[0].adminOverride = original;
}
});
});

describe('Backward compatibility', () => {
it('does not crash when setSnapshot is called without serviceStatuses', () => {
useHealthDashboardStore.getState().setSnapshot(makeSnapshot());
expect(useFeatureFlagStore.getState().isEnabled('streaming_courses', true)).toBe(true);
expect(useFeatureFlagStore.getState().isEnabled('payment_form', true)).toBe(true);
});
});

});
Loading