/*
Single SigNoz API client module for alert rule and channel management (RFC-0342).
- No dashboard management.
- No user management.
- RFC-0342: initial implementation.
*/
export interface SignozApiClientConfig {
apiUrl: string;
apiToken: string;
}
export interface SignozAlertRule {
id: string;
name: string;
severity: string;
promql?: string;
builder?: unknown;
evalWindow: string;
forDuration: string;
condition: { op: string; target: number };
channels: string[];
labels?: Record;
description: string;
}
export interface SignozChannel {
id: string;
kind: string;
target: string[];
}
export interface SignozApplyPlan {
rules: {
create: SignozAlertRule[];
update: SignozAlertRule[];
delete: string[];
};
channels: {
create: SignozChannel[];
update: SignozChannel[];
delete: string[];
};
}
export interface SignozApiClient {
listManagedRules(): Promise;
listManagedChannels(): Promise;
createRule(rule: SignozAlertRule): Promise;
updateRule(rule: SignozAlertRule): Promise;
deleteRule(id: string): Promise;
createChannel(channel: SignozChannel): Promise;
updateChannel(channel: SignozChannel): Promise;
deleteChannel(id: string): Promise;
}
export function createSignozApiClient(config: SignozApiClientConfig): SignozApiClient {
const headers: Record = {
"Content-Type": "application/json",
Authorization: `Bearer ${config.apiToken}`,
};
async function apiCall(path: string, options: RequestInit = {}): Promise {
const url = `${config.apiUrl}/api${path}`;
const response = await fetch(url, {
...options,
headers: { ...headers, ...(options.headers as Record) },
});
if (!response.ok) {
throw new Error(`SigNoz API ${path} returned ${response.status}: ${await response.text()}`);
}
if (response.status === 204) return null;
return response.json();
}
return {
async listManagedRules(): Promise {
const data = (await apiCall("/alerts/rules")) as { data?: SignozAlertRule[] };
const rules = data?.data ?? [];
return rules.filter((r) => r.labels?.managed_by === "warpgogol");
},
async listManagedChannels(): Promise {
const data = (await apiCall("/alerts/channels")) as { data?: SignozChannel[] };
const channels = data?.data ?? [];
return channels.filter((c) => c.id.startsWith("warpgogol-"));
},
async createRule(rule: SignozAlertRule): Promise {
await apiCall("/alerts/rules", {
method: "POST",
body: JSON.stringify({ ...rule, labels: { ...rule.labels, managed_by: "warpgogol" } }),
});
},
async updateRule(rule: SignozAlertRule): Promise {
await apiCall(`/alerts/rules/${rule.id}`, {
method: "PUT",
body: JSON.stringify({ ...rule, labels: { ...rule.labels, managed_by: "warpgogol" } }),
});
},
async deleteRule(id: string): Promise {
await apiCall(`/alerts/rules/${id}`, { method: "DELETE" });
},
async createChannel(channel: SignozChannel): Promise {
await apiCall("/alerts/channels", {
method: "POST",
body: JSON.stringify({ ...channel, id: `warpgogol-${channel.id}` }),
});
},
async updateChannel(channel: SignozChannel): Promise {
await apiCall(`/alerts/channels/warpgogol-${channel.id}`, {
method: "PUT",
body: JSON.stringify(channel),
});
},
async deleteChannel(id: string): Promise {
await apiCall(`/alerts/channels/warpgogol-${id}`, { method: "DELETE" });
},
};
}