Watch
0
0
Fork
You've already forked hyperhive
0

swarm-ui: test which alert instances the alerts page lists

The firing-only filter and the helpers beside it move out of the page
component into a plain module, with tests for the state match, the
never-active time and the row key.
This commit is contained in:
iris 2026-10-18 13:14:22 +02:00 • committed by mara
commit b39e095aa8
3 changed files with 104 additions and 26 deletions

View file

@ -15,40 +15,16 @@ import {
type RefreshIntervalMs,
} from "../ui/refresh-interval/RefreshInterval.js";
import { Table, type TableColumn } from "../ui/table/Table.js";
interface Alert {
name: string;
// Grafana's state for the instance: `Alerting`, `Pending`, `NoData` or
// `Error`.
state: string;
active_at: string | null;
summary: string | null;
labels: Record<string, string>;
}
import { activeSinceMs, alertKey, firingAlerts, type Alert } from "./alerts.js";
// Grafana's alert-groups view, where the instances this page leaves out are.
const GRAFANA_ALERTS_URL =
"https://grafana.constellation.darkest.space/alerting/groups";
const FIRING = "Alerting";
// Versioned: the state column used to be a filter, and a saved "not Pending"
// selection under the old key would now hide every row.
const FILTERS_STORAGE_KEY = "swarm-ui:alerts:table-filters:v2";
// Grafana reports an instance that never became active with the zero time,
// which `Date.parse` reads as year 1.
function activeSinceMs(a: Alert): number | null {
if (a.active_at === null) return null;
const ms = Date.parse(a.active_at);
return Number.isNaN(ms) || ms <= 0 ? null : ms;
}
// A row needs a key that is unique among instances of the same rule.
function alertKey(a: Alert): string {
return JSON.stringify(Object.entries(a.labels).sort());
}
const COLUMNS: TableColumn<Alert>[] = [
{
key: "name",
@ -93,7 +69,7 @@ const DEFAULT_INTERVAL_MS: RefreshIntervalMs = 30_000;
export function AlertsPage() {
const [alerts, setAlerts] = useState<Alert[] | null>(null);
const [error, setError] = useState<ProblemDetails | null>(null);
const firing = alerts?.filter((a) => a.state === FIRING) ?? null;
const firing = alerts ? firingAlerts(alerts) : null;
const [intervalMs, setIntervalMs] =
useState<RefreshIntervalMs>(DEFAULT_INTERVAL_MS);

View file

@ -0,0 +1,34 @@
// What the alerts page lists, apart from how it draws it: the shape of one
// instance from swarm-controller's `GET /api/alerts` and which of them count
// as firing.
export interface Alert {
name: string;
// Grafana's state for the instance: `Alerting`, `Pending`, `NoData` or
// `Error`.
state: string;
active_at: string | null;
summary: string | null;
labels: Record<string, string>;
}
const FIRING = "Alerting";
// The instances the page shows: the ones Grafana says are firing now. Pending,
// no-data and errored ones are left to Grafana's own list.
export function firingAlerts(alerts: Alert[]): Alert[] {
return alerts.filter((a) => a.state === FIRING);
}
// Grafana reports an instance that never became active with the zero time,
// which `Date.parse` reads as year 1.
export function activeSinceMs(a: Alert): number | null {
if (a.active_at === null) return null;
const ms = Date.parse(a.active_at);
return Number.isNaN(ms) || ms <= 0 ? null : ms;
}
// A row needs a key that is unique among instances of the same rule.
export function alertKey(a: Alert): string {
return JSON.stringify(Object.entries(a.labels).sort());
}