diff --git a/server/src/domain/notifications/providers/rocketChat.ts b/server/src/domain/notifications/providers/rocketChat.ts index f5d224439..2ef8d7efb 100644 --- a/server/src/domain/notifications/providers/rocketChat.ts +++ b/server/src/domain/notifications/providers/rocketChat.ts @@ -1,4 +1,5 @@ const SERVICE_NAME = "RocketChatProvider"; +import { supportsUptimeDetails, type MonitorType } from "@/domain/monitors/monitor.type.js"; import type { Notification, NotificationMessage } from "@/domain/notifications/notification.type.js"; import { NotificationProvider } from "@/domain/notifications/providers/INotificationProvider.js"; import { getTestMessage } from "@/domain/notifications/providers/utils.js"; @@ -84,6 +85,7 @@ export class RocketChatProvider extends NotificationProvider { } private buildPayload(message: NotificationMessage): RocketChatPayload { + const monitorUrl = this.monitorUrl(message); const fields: RocketChatField[] = [ { title: "Monitor", value: message.monitor.name, short: true }, { title: "Type", value: message.monitor.type, short: true }, @@ -123,7 +125,7 @@ export class RocketChatProvider extends NotificationProvider { { color: this.severityColor(message.severity), title: message.content.title, - title_link: `${message.clientHost}/infrastructure/${message.monitor.id}`, + ...(monitorUrl ? { title_link: monitorUrl } : {}), fields, ts: message.content.timestamp.toISOString(), }, @@ -131,6 +133,27 @@ export class RocketChatProvider extends NotificationProvider { }; } + private monitorUrl(message: NotificationMessage): string | undefined { + const clientHost = message.clientHost?.trim(); + if (!clientHost || clientHost === "Host not defined") { + return undefined; + } + + if (message.monitor.type === "hardware") { + return `${clientHost}/infrastructure/${message.monitor.id}`; + } + + if (message.monitor.type === "pagespeed") { + return `${clientHost}/pagespeed/${message.monitor.id}`; + } + + if (supportsUptimeDetails(message.monitor.type as MonitorType)) { + return `${clientHost}/uptime/${message.monitor.id}`; + } + + return undefined; + } + private severityColor(severity: NotificationMessage["severity"]): string { switch (severity) { case "critical": diff --git a/server/test/unit/providers/notifications/rocketChatProvider.test.ts b/server/test/unit/providers/notifications/rocketChatProvider.test.ts index dcf1001d0..816e19801 100644 --- a/server/test/unit/providers/notifications/rocketChatProvider.test.ts +++ b/server/test/unit/providers/notifications/rocketChatProvider.test.ts @@ -1,4 +1,5 @@ import { describe, expect, it, jest } from "@jest/globals"; +import type { MonitorType } from "../../../../src/domain/monitors/monitor.type.ts"; import type { NotificationSeverity } from "../../../../src/domain/notifications/notification.type.ts"; import { createMockLogger } from "../../../helpers/createMockLogger.ts"; import { makeMessage, makeMessageWithIncident, makeMessageWithThresholds, makeNotification } from "../../../helpers/notificationMessage.ts"; @@ -102,7 +103,7 @@ describe("RocketChatProvider", () => { { color: "#FF0000", title: "Monitor Down: Test Monitor", - title_link: "https://app.example.com/infrastructure/mon-1", + title_link: "https://app.example.com/uptime/mon-1", fields: [ { title: "Monitor", value: "Test Monitor", short: true }, { title: "Type", value: "http", short: true }, @@ -128,6 +129,32 @@ describe("RocketChatProvider", () => { expect(payload).not.toHaveProperty("channel"); }); + it.each([ + ["http", "https://app.example.com/uptime/mon-1"], + ["ping", "https://app.example.com/uptime/mon-1"], + ["pagespeed", "https://app.example.com/pagespeed/mon-1"], + ["hardware", "https://app.example.com/infrastructure/mon-1"], + ])("links %s monitors to %s", async (type, expectedLink) => { + const { provider } = createProvider(); + + await provider.sendMessage( + makeNotification({ type: "rocket_chat" }), + makeMessage({ monitor: { id: "mon-1", name: "Test Monitor", url: "https://example.com", type: type as MonitorType, status: "down" } }) + ); + + const payload = mockGotPost.mock.calls[0][1].json; + expect(payload.attachments[0].title_link).toBe(expectedLink); + }); + + it.each([["Host not defined"], [""], [" "]])("omits title_link when clientHost is %j", async (clientHost) => { + const { provider } = createProvider(); + + await provider.sendMessage(makeNotification({ type: "rocket_chat" }), makeMessage({ clientHost })); + + const payload = mockGotPost.mock.calls[0][1].json; + expect(payload.attachments[0]).not.toHaveProperty("title_link"); + }); + it.each([ ["critical", "#FF0000"], ["warning", "#FFA500"],