110 lines
5.0 KiB
TypeScript
110 lines
5.0 KiB
TypeScript
|
|
/**
|
||
|
|
* Alerting option lists derived from the OpenAPI schema.
|
||
|
|
*
|
||
|
|
* Why this module exists: option arrays in condition-form components used to
|
||
|
|
* be hand-typed string literals, and they drifted silently from the backend
|
||
|
|
* enums (e.g. P95_LATENCY_MS appeared in the dropdown without a matching
|
||
|
|
* backend value; LATEST was exposed by the backend but never surfaced in the
|
||
|
|
* UI). Every dropdown value here is derived from a schema.d.ts union type and
|
||
|
|
* every label is required via `Record<T, string>` — TypeScript will refuse to
|
||
|
|
* compile if the backend adds or removes a value and this file isn't updated.
|
||
|
|
*
|
||
|
|
* Workflow when an alerting enum changes on the backend:
|
||
|
|
* 1. `cd ui && npm run generate-api:live` (or `generate-api` after deploy).
|
||
|
|
* 2. The `Record<…, string>` maps below fail to type-check for any new or
|
||
|
|
* removed value. Fix the map.
|
||
|
|
* 3. Every consumer (`METRIC_OPTIONS`, …) regenerates automatically.
|
||
|
|
*
|
||
|
|
* Fields whose backend type is `String` rather than a typed enum (agent state,
|
||
|
|
* log level, deployment states, exchange filter status, JVM metric names)
|
||
|
|
* cannot be derived here today — springdoc emits them as open-ended strings.
|
||
|
|
* Follow-up: add `@Schema(allowableValues = …)` on the relevant Java record
|
||
|
|
* components so they land in schema.d.ts as unions, then fold them in here.
|
||
|
|
*/
|
||
|
|
import type { components } from './schema';
|
||
|
|
|
||
|
|
type AlertRuleRequest = components['schemas']['AlertRuleRequest'];
|
||
|
|
type RouteMetricCondition = components['schemas']['RouteMetricCondition'];
|
||
|
|
type JvmMetricCondition = components['schemas']['JvmMetricCondition'];
|
||
|
|
type ExchangeMatchCondition = components['schemas']['ExchangeMatchCondition'];
|
||
|
|
type AlertRuleTarget = components['schemas']['AlertRuleTarget'];
|
||
|
|
|
||
|
|
export type ConditionKind = NonNullable<AlertRuleRequest['conditionKind']>;
|
||
|
|
export type Severity = NonNullable<AlertRuleRequest['severity']>;
|
||
|
|
export type RouteMetric = NonNullable<RouteMetricCondition['metric']>;
|
||
|
|
export type Comparator = NonNullable<RouteMetricCondition['comparator']>;
|
||
|
|
export type JvmAggregation = NonNullable<JvmMetricCondition['aggregation']>;
|
||
|
|
export type ExchangeFireMode = NonNullable<ExchangeMatchCondition['fireMode']>;
|
||
|
|
export type TargetKind = NonNullable<AlertRuleTarget['kind']>;
|
||
|
|
|
||
|
|
export interface Option<T extends string> { value: T; label: string }
|
||
|
|
|
||
|
|
function toOptions<T extends string>(labels: Record<T, string>): Option<T>[] {
|
||
|
|
return (Object.keys(labels) as T[]).map((value) => ({ value, label: labels[value] }));
|
||
|
|
}
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Label maps — one entry per backend value, TypeScript enforces exhaustiveness.
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
const CONDITION_KIND_LABELS: Record<ConditionKind, string> = {
|
||
|
|
ROUTE_METRIC: 'Route metric (error rate, latency, throughput)',
|
||
|
|
EXCHANGE_MATCH: 'Exchange match (specific failures)',
|
||
|
|
AGENT_STATE: 'Agent state (DEAD / STALE)',
|
||
|
|
DEPLOYMENT_STATE: 'Deployment state (FAILED / DEGRADED)',
|
||
|
|
LOG_PATTERN: 'Log pattern (count of matching logs)',
|
||
|
|
JVM_METRIC: 'JVM metric (heap, GC, inflight)',
|
||
|
|
};
|
||
|
|
|
||
|
|
const SEVERITY_LABELS: Record<Severity, string> = {
|
||
|
|
CRITICAL: 'Critical',
|
||
|
|
WARNING: 'Warning',
|
||
|
|
INFO: 'Info',
|
||
|
|
};
|
||
|
|
|
||
|
|
const ROUTE_METRIC_LABELS: Record<RouteMetric, string> = {
|
||
|
|
ERROR_RATE: 'Error rate',
|
||
|
|
P99_LATENCY_MS: 'P99 latency (ms)',
|
||
|
|
AVG_DURATION_MS: 'Avg duration (ms)',
|
||
|
|
THROUGHPUT: 'Throughput (msg/s)',
|
||
|
|
ERROR_COUNT: 'Error count',
|
||
|
|
};
|
||
|
|
|
||
|
|
const COMPARATOR_LABELS: Record<Comparator, string> = {
|
||
|
|
GT: '>',
|
||
|
|
GTE: '\u2265',
|
||
|
|
LT: '<',
|
||
|
|
LTE: '\u2264',
|
||
|
|
EQ: '=',
|
||
|
|
};
|
||
|
|
|
||
|
|
const JVM_AGGREGATION_LABELS: Record<JvmAggregation, string> = {
|
||
|
|
MAX: 'MAX',
|
||
|
|
MIN: 'MIN',
|
||
|
|
AVG: 'AVG',
|
||
|
|
LATEST: 'LATEST',
|
||
|
|
};
|
||
|
|
|
||
|
|
const EXCHANGE_FIRE_MODE_LABELS: Record<ExchangeFireMode, string> = {
|
||
|
|
PER_EXCHANGE: 'One alert per matching exchange',
|
||
|
|
COUNT_IN_WINDOW: 'Threshold: N matches in window',
|
||
|
|
};
|
||
|
|
|
||
|
|
const TARGET_KIND_LABELS: Record<TargetKind, string> = {
|
||
|
|
USER: 'User',
|
||
|
|
GROUP: 'Group',
|
||
|
|
ROLE: 'Role',
|
||
|
|
};
|
||
|
|
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
// Exported option arrays (in label-map declaration order).
|
||
|
|
// ---------------------------------------------------------------------------
|
||
|
|
|
||
|
|
export const CONDITION_KIND_OPTIONS: Option<ConditionKind>[] = toOptions(CONDITION_KIND_LABELS);
|
||
|
|
export const SEVERITY_OPTIONS: Option<Severity>[] = toOptions(SEVERITY_LABELS);
|
||
|
|
export const ROUTE_METRIC_OPTIONS: Option<RouteMetric>[] = toOptions(ROUTE_METRIC_LABELS);
|
||
|
|
export const COMPARATOR_OPTIONS: Option<Comparator>[] = toOptions(COMPARATOR_LABELS);
|
||
|
|
export const JVM_AGGREGATION_OPTIONS: Option<JvmAggregation>[] = toOptions(JVM_AGGREGATION_LABELS);
|
||
|
|
export const EXCHANGE_FIRE_MODE_OPTIONS:Option<ExchangeFireMode>[] = toOptions(EXCHANGE_FIRE_MODE_LABELS);
|
||
|
|
export const TARGET_KIND_OPTIONS: Option<TargetKind>[] = toOptions(TARGET_KIND_LABELS);
|