diff --git a/server/src/domain/notifications/providers/rocketChat.ts b/server/src/domain/notifications/providers/rocketChat.ts index 821515b25..217dfa795 100644 --- a/server/src/domain/notifications/providers/rocketChat.ts +++ b/server/src/domain/notifications/providers/rocketChat.ts @@ -84,6 +84,39 @@ export class RocketChatProvider extends NotificationProvider { } private buildPayload(message: NotificationMessage): RocketChatPayload { + const fields: RocketChatField[] = [ + { title: "Monitor", value: message.monitor.name, short: true }, + { title: "Type", value: message.monitor.type, short: true }, + { title: "Status", value: message.monitor.status, short: true }, + { title: "URL", value: message.monitor.url, short: false }, + ]; + + if (message.content.thresholds?.length) { + fields.push( + ...message.content.thresholds.map((breach) => ({ + title: breach.metric.toUpperCase(), + value: `${breach.formattedValue} (threshold: ${breach.threshold}${breach.unit})`, + short: true, + })) + ); + } + + if (message.content.details?.length) { + fields.push({ + title: "Details", + value: message.content.details.map((detail) => `- ${detail}`).join("\n"), + short: false, + }); + } + + if (message.content.incident) { + fields.push({ + title: "Incident", + value: message.content.incident.url, + short: false, + }); + } + return { text: message.content.summary, attachments: [ @@ -91,12 +124,7 @@ export class RocketChatProvider extends NotificationProvider { color: this.severityColor(message.severity), title: message.content.title, title_link: `${message.clientHost}/infrastructure/${message.monitor.id}`, - fields: [ - { title: "Monitor", value: message.monitor.name, short: true }, - { title: "Type", value: message.monitor.type, short: true }, - { title: "Status", value: message.monitor.status, short: true }, - { title: "URL", value: message.monitor.url, short: false }, - ], + fields, ts: message.content.timestamp.toISOString(), }, ], diff --git a/server/test/unit/providers/notifications/rocketChatProvider.test.ts b/server/test/unit/providers/notifications/rocketChatProvider.test.ts index 401cac58b..7a5b1f2a5 100644 --- a/server/test/unit/providers/notifications/rocketChatProvider.test.ts +++ b/server/test/unit/providers/notifications/rocketChatProvider.test.ts @@ -1,7 +1,7 @@ import { describe, expect, it, jest } from "@jest/globals"; import type { NotificationSeverity } from "../../../../src/domain/notifications/notification.type.ts"; import { createMockLogger } from "../../../helpers/createMockLogger.ts"; -import { makeMessage, makeNotification } from "../../../helpers/notificationMessage.ts"; +import { makeMessage, makeMessageWithIncident, makeMessageWithThresholds, makeNotification } from "../../../helpers/notificationMessage.ts"; import { testNotificationProviderContract } from "../../../helpers/notificationProviderContract.ts"; const mockGotPost = jest.fn().mockResolvedValue({}); @@ -108,6 +108,11 @@ describe("RocketChatProvider", () => { { title: "Type", value: "http", short: true }, { title: "Status", value: "down", short: true }, { title: "URL", value: "https://example.com", short: false }, + { + title: "Details", + value: "- URL: https://example.com\n- Status: Down", + short: false, + }, ], ts: "2025-01-01T00:00:00.000Z", }, @@ -138,6 +143,64 @@ describe("RocketChatProvider", () => { expect(payload.attachments[0].color).toBe(color); }); + it("appends threshold fields before details", async () => { + const { provider } = createProvider(); + + await provider.sendMessage(makeNotification({ type: "rocket_chat" }), makeMessageWithThresholds()); + + const fields = mockGotPost.mock.calls[0][1].json.attachments[0].fields; + expect(fields).toEqual([ + { title: "Monitor", value: "Infra Server", short: true }, + { title: "Type", value: "hardware", short: true }, + { title: "Status", value: "up", short: true }, + { title: "URL", value: "https://infra.example.com", short: false }, + { title: "CPU", value: "90.0% (threshold: 80%)", short: true }, + { title: "MEMORY", value: "85.0% (threshold: 70%)", short: true }, + { title: "Details", value: "- URL: https://infra.example.com", short: false }, + ]); + }); + + it("appends the incident URL after details", async () => { + const { provider } = createProvider(); + + await provider.sendMessage(makeNotification({ type: "rocket_chat" }), makeMessageWithIncident()); + + const fields = mockGotPost.mock.calls[0][1].json.attachments[0].fields; + expect(fields).toEqual([ + { title: "Monitor", value: "Test Monitor", short: true }, + { title: "Type", value: "http", short: true }, + { title: "Status", value: "down", short: true }, + { title: "URL", value: "https://example.com", short: false }, + { title: "Details", value: "- URL: https://example.com\n- Status: Down", short: false }, + { title: "Incident", value: "https://app.example.com/incidents/inc-1", short: false }, + ]); + }); + + it("omits empty optional fields", async () => { + const { provider } = createProvider(); + const message = makeMessage(); + + await provider.sendMessage( + makeNotification({ type: "rocket_chat" }), + makeMessage({ + content: { + ...message.content, + details: [], + thresholds: [], + incident: undefined, + }, + }) + ); + + const fields = mockGotPost.mock.calls[0][1].json.attachments[0].fields; + expect(fields).toEqual([ + { title: "Monitor", value: "Test Monitor", short: true }, + { title: "Type", value: "http", short: true }, + { title: "Status", value: "down", short: true }, + { title: "URL", value: "https://example.com", short: false }, + ]); + }); + it("returns false when the message webhook URL is missing", async () => { const { provider } = createProvider();