/** * 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` — 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; export type Severity = NonNullable; export type RouteMetric = NonNullable; export type Comparator = NonNullable; export type JvmAggregation = NonNullable; export type ExchangeFireMode = NonNullable; export type TargetKind = NonNullable; export interface Option { value: T; label: string } function toOptions(labels: Record): Option[] { 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 = { 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 = { CRITICAL: 'Critical', WARNING: 'Warning', INFO: 'Info', }; const ROUTE_METRIC_LABELS: Record = { 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 = { GT: '>', GTE: '\u2265', LT: '<', LTE: '\u2264', EQ: '=', }; const JVM_AGGREGATION_LABELS: Record = { MAX: 'MAX', MIN: 'MIN', AVG: 'AVG', LATEST: 'LATEST', }; const EXCHANGE_FIRE_MODE_LABELS: Record = { PER_EXCHANGE: 'One alert per matching exchange', COUNT_IN_WINDOW: 'Threshold: N matches in window', }; const TARGET_KIND_LABELS: Record = { USER: 'User', GROUP: 'Group', ROLE: 'Role', }; // --------------------------------------------------------------------------- // Exported option arrays (in label-map declaration order). // --------------------------------------------------------------------------- export const CONDITION_KIND_OPTIONS: Option[] = toOptions(CONDITION_KIND_LABELS); export const SEVERITY_OPTIONS: Option[] = toOptions(SEVERITY_LABELS); export const ROUTE_METRIC_OPTIONS: Option[] = toOptions(ROUTE_METRIC_LABELS); export const COMPARATOR_OPTIONS: Option[] = toOptions(COMPARATOR_LABELS); export const JVM_AGGREGATION_OPTIONS: Option[] = toOptions(JVM_AGGREGATION_LABELS); export const EXCHANGE_FIRE_MODE_OPTIONS:Option[] = toOptions(EXCHANGE_FIRE_MODE_LABELS); export const TARGET_KIND_OPTIONS: Option[] = toOptions(TARGET_KIND_LABELS);