import { useQuery, useMutation, useQueryClient } from '@tanstack/react-query'; import type { components } from '../schema'; import { apiClient, useSelectedEnv } from './alertMeta'; export type AlertRuleResponse = components['schemas']['AlertRuleResponse']; export type AlertRuleRequest = components['schemas']['AlertRuleRequest']; export type RenderPreviewRequest = components['schemas']['RenderPreviewRequest']; export type RenderPreviewResponse = components['schemas']['RenderPreviewResponse']; export type TestEvaluateRequest = components['schemas']['TestEvaluateRequest']; export type TestEvaluateResponse = components['schemas']['TestEvaluateResponse']; export type AlertCondition = AlertRuleResponse['condition']; // `ConditionKind` lives in `../../pages/Alerts/enums` alongside its label map // and option array — single source of truth to avoid duplicate-type drift. // NOTE ON TYPES: the generated OpenAPI schema for env-scoped alert-rule endpoints // emits `path?: never` plus a `query.env: Environment` parameter because the // server resolves the env via the `@EnvPath` argument resolver, which the // OpenAPI scanner does not recognise as a path variable. At runtime the URL // template `{envSlug}` is substituted from `params.path.envSlug` by // openapi-fetch regardless of what the TS types say; we therefore cast the // call options to `any` on each call to bypass the generated type oddity. /** List alert rules in the current env. */ export function useAlertRules() { const env = useSelectedEnv(); return useQuery({ queryKey: ['alertRules', env], enabled: !!env, queryFn: async () => { if (!env) throw new Error('no env'); const { data, error } = await apiClient.GET( '/environments/{envSlug}/alerts/rules', { params: { path: { envSlug: env } }, } as any, ); if (error) throw error; return data as AlertRuleResponse[]; }, }); } /** Fetch a single alert rule by id. */ export function useAlertRule(id: string | undefined) { const env = useSelectedEnv(); return useQuery({ queryKey: ['alertRules', env, id], enabled: !!env && !!id, queryFn: async () => { if (!env || !id) throw new Error('no env/id'); const { data, error } = await apiClient.GET( '/environments/{envSlug}/alerts/rules/{id}', { params: { path: { envSlug: env, id } }, } as any, ); if (error) throw error; return data as AlertRuleResponse; }, }); } /** Create a new alert rule in the current env. */ export function useCreateAlertRule() { const qc = useQueryClient(); const env = useSelectedEnv(); return useMutation({ mutationFn: async (req: AlertRuleRequest) => { if (!env) throw new Error('no env'); const { data, error } = await apiClient.POST( '/environments/{envSlug}/alerts/rules', { params: { path: { envSlug: env } }, body: req, } as any, ); if (error) throw error; return data as AlertRuleResponse; }, onSuccess: () => { qc.invalidateQueries({ queryKey: ['alertRules', env] }); }, }); } /** Update an existing alert rule. */ export function useUpdateAlertRule(id: string) { const qc = useQueryClient(); const env = useSelectedEnv(); return useMutation({ mutationFn: async (req: AlertRuleRequest) => { if (!env) throw new Error('no env'); const { data, error } = await apiClient.PUT( '/environments/{envSlug}/alerts/rules/{id}', { params: { path: { envSlug: env, id } }, body: req, } as any, ); if (error) throw error; return data as AlertRuleResponse; }, onSuccess: () => { qc.invalidateQueries({ queryKey: ['alertRules', env] }); qc.invalidateQueries({ queryKey: ['alertRules', env, id] }); }, }); } /** Delete an alert rule. */ export function useDeleteAlertRule() { const qc = useQueryClient(); const env = useSelectedEnv(); return useMutation({ mutationFn: async (id: string) => { if (!env) throw new Error('no env'); const { error } = await apiClient.DELETE( '/environments/{envSlug}/alerts/rules/{id}', { params: { path: { envSlug: env, id } }, } as any, ); if (error) throw error; }, onSuccess: () => { qc.invalidateQueries({ queryKey: ['alertRules', env] }); }, }); } /** Enable or disable an alert rule. Routes to /enable or /disable based on the flag. */ export function useSetAlertRuleEnabled() { const qc = useQueryClient(); const env = useSelectedEnv(); return useMutation({ mutationFn: async ({ id, enabled }: { id: string; enabled: boolean }) => { if (!env) throw new Error('no env'); const path = enabled ? '/environments/{envSlug}/alerts/rules/{id}/enable' : '/environments/{envSlug}/alerts/rules/{id}/disable'; const { error } = await apiClient.POST(path, { params: { path: { envSlug: env, id } }, } as any); if (error) throw error; }, onSuccess: () => { qc.invalidateQueries({ queryKey: ['alertRules', env] }); }, }); } /** Render a preview of the notification title + message for a rule using the provided context. */ export function useRenderPreview() { const env = useSelectedEnv(); return useMutation({ mutationFn: async ({ id, req }: { id: string; req: RenderPreviewRequest }) => { if (!env) throw new Error('no env'); const { data, error } = await apiClient.POST( '/environments/{envSlug}/alerts/rules/{id}/render-preview', { params: { path: { envSlug: env, id } }, body: req, } as any, ); if (error) throw error; return data as RenderPreviewResponse; }, }); } /** Test-evaluate a rule (dry-run) without persisting an alert instance. */ export function useTestEvaluate() { const env = useSelectedEnv(); return useMutation({ mutationFn: async ({ id, req }: { id: string; req: TestEvaluateRequest }) => { if (!env) throw new Error('no env'); const { data, error } = await apiClient.POST( '/environments/{envSlug}/alerts/rules/{id}/test-evaluate', { params: { path: { envSlug: env, id } }, body: req, } as any, ); if (error) throw error; return data as TestEvaluateResponse; }, }); }