diff --git a/packages/api/src/controllers/alerts.ts b/packages/api/src/controllers/alerts.ts index f1537fcfe..2ecc77d6c 100644 --- a/packages/api/src/controllers/alerts.ts +++ b/packages/api/src/controllers/alerts.ts @@ -23,6 +23,10 @@ export type AlertInput = { type: AlertType; threshold: number; + // Message template + templateTitle?: string | null; + templateBody?: string | null; + // Log alerts groupBy?: string; logViewId?: string; @@ -86,6 +90,14 @@ const makeAlert = (alert: AlertInput) => { source: alert.source, threshold: alert.threshold, type: alert.type, + + // Message template + // If they're undefined/null, set it to null so we clear out the field + // due to mongoose behavior: + // https://mongoosejs.com/docs/migrating_to_6.html#removed-omitundefined + templateTitle: alert.templateTitle == null ? null : alert.templateTitle, + templateBody: alert.templateBody == null ? null : alert.templateBody, + // Log alerts logView: alert.logViewId, groupBy: alert.groupBy, diff --git a/packages/api/src/fixtures.ts b/packages/api/src/fixtures.ts index 32bb9ef22..62f7658a9 100644 --- a/packages/api/src/fixtures.ts +++ b/packages/api/src/fixtures.ts @@ -402,11 +402,15 @@ export const makeExternalAlert = ({ chartId, threshold = 8, interval = '15m', + name, + message, }: { dashboardId: string; chartId: string; threshold?: number; interval?: '15m' | '1m' | '5m' | '30m' | '1h' | '6h' | '12h' | '1d'; + name?: string; + message?: string; }): z.infer => ({ channel: { type: 'slack_webhook', @@ -418,4 +422,6 @@ export const makeExternalAlert = ({ source: 'chart', dashboardId, chartId, + name, + message, }); diff --git a/packages/api/src/models/alert.ts b/packages/api/src/models/alert.ts index 21742b6a6..09a886f96 100644 --- a/packages/api/src/models/alert.ts +++ b/packages/api/src/models/alert.ts @@ -42,8 +42,8 @@ export interface IAlert { type: AlertType; // Message template - templateTitle?: string; - templateBody?: string; + templateTitle?: string | null; + templateBody?: string | null; // Log alerts groupBy?: string; diff --git a/packages/api/src/routers/external-api/__tests__/alerts.test.ts b/packages/api/src/routers/external-api/__tests__/alerts.test.ts index 9780ed59f..0a3971f32 100644 --- a/packages/api/src/routers/external-api/__tests__/alerts.test.ts +++ b/packages/api/src/routers/external-api/__tests__/alerts.test.ts @@ -43,7 +43,7 @@ describe('/api/v1/alerts', () => { // Create alerts for all charts const dashboard = initialDashboards.body.data[0]; await Promise.all( - dashboard.charts.map(chart => + dashboard.charts.map((chart, i) => agent .post('/api/v1/alerts') .set('Authorization', `Bearer ${user?.accessKey}`) @@ -51,6 +51,12 @@ describe('/api/v1/alerts', () => { makeExternalAlert({ dashboardId: dashboard._id, chartId: chart.id, + ...(i % 2 == 0 + ? { + name: 'test {{hello}}', + message: 'my message {{hello}}', + } + : undefined), }), ) .expect(200), @@ -92,6 +98,8 @@ Array [ }, "chartId": "aaaaaaa", "interval": "15m", + "message": "my message {{hello}}", + "name": "test {{hello}}", "source": "chart", "threshold": 8, "threshold_type": "above", @@ -103,6 +111,8 @@ Array [ }, "chartId": "bbbbbbb", "interval": "15m", + "message": null, + "name": null, "source": "chart", "threshold": 8, "threshold_type": "above", @@ -114,6 +124,8 @@ Array [ }, "chartId": "ccccccc", "interval": "15m", + "message": "my message {{hello}}", + "name": "test {{hello}}", "source": "chart", "threshold": 8, "threshold_type": "above", @@ -125,6 +137,8 @@ Array [ }, "chartId": "ddddddd", "interval": "15m", + "message": null, + "name": null, "source": "chart", "threshold": 8, "threshold_type": "above", @@ -136,6 +150,8 @@ Array [ }, "chartId": "eeeeeee", "interval": "15m", + "message": "my message {{hello}}", + "name": "test {{hello}}", "source": "chart", "threshold": 8, "threshold_type": "above", @@ -179,6 +195,8 @@ Object { "webhookId": "65ad876b6b08426ab4ba7830", }, "interval": "1h", + "message": null, + "name": null, "source": "chart", "threshold": 1000, "threshold_type": "above", @@ -200,6 +218,8 @@ Object { "webhookId": "65ad876b6b08426ab4ba7830", }, "interval": "1h", + "message": null, + "name": null, "source": "chart", "threshold": 1000, "threshold_type": "above", diff --git a/packages/api/src/tasks/checkAlerts.ts b/packages/api/src/tasks/checkAlerts.ts index 9b2a132c6..5d9d38d74 100644 --- a/packages/api/src/tasks/checkAlerts.ts +++ b/packages/api/src/tasks/checkAlerts.ts @@ -173,7 +173,7 @@ export const buildAlertMessageTemplateTitle = ({ template, view, }: { - template?: string; + template?: string | null; view: AlertMessageTemplateDefaultView; }) => { const { alert, dashboard, savedSearch, value } = view; @@ -210,7 +210,7 @@ export const buildAlertMessageTemplateBody = async ({ template, view, }: { - template?: string; + template?: string | null; view: AlertMessageTemplateDefaultView; }) => { const { diff --git a/packages/api/src/utils/zod.ts b/packages/api/src/utils/zod.ts index 7c10af480..43239a492 100644 --- a/packages/api/src/utils/zod.ts +++ b/packages/api/src/utils/zod.ts @@ -215,6 +215,8 @@ export const alertSchema = z threshold: z.number().min(0), type: z.enum(['presence', 'absence']), source: z.enum(['LOG', 'CHART']).default('LOG'), + templateTitle: z.string().min(1).max(512).nullish(), + templateBody: z.string().min(1).max(4096).nullish(), }) .and(zLogAlert.or(zChartAlert)); @@ -231,7 +233,6 @@ export const externalSearchAlertSchema = z.object({ source: z.literal('search'), groupBy: z.string().optional(), savedSearchId: objectIdSchema, - message: z.string().optional(), }); export const externalChartAlertSchema = z.object({ @@ -247,6 +248,8 @@ export const externalAlertSchema = z threshold: z.number().min(0), threshold_type: z.enum(['above', 'below']), source: z.enum(['search', 'chart']).default('search'), + name: z.string().min(1).max(512).nullish(), + message: z.string().min(1).max(4096).nullish(), }) .and(externalSearchAlertSchema.or(externalChartAlertSchema)); @@ -268,6 +271,8 @@ export const translateExternalAlertToInternalAlert = ( ...alertInput.channel, type: 'webhook', }, + templateTitle: alertInput.name, + templateBody: alertInput.message, ...(alertInput.source === 'search' && alertInput.savedSearchId ? { source: 'LOG', logViewId: alertInput.savedSearchId } : alertInput.source === 'chart' && alertInput.dashboardId @@ -293,6 +298,8 @@ export const translateAlertDocumentToExternalAlert = ( ...alertDoc.channel, type: 'slack_webhook', }, + name: alertDoc.templateTitle, + message: alertDoc.templateBody, ...(alertDoc.source === 'LOG' && alertDoc.logView ? { source: 'search', savedSearchId: alertDoc.logView.toString() } : alertDoc.source === 'CHART' && alertDoc.dashboardId