feat(notifications): add rich alert fields

This commit is contained in:
Marcos Paulo Diniz committed 2026-08-07 14:55:05 -03:00
1 parent 2c780b8101
commit 865fa7cce6
2 files changed
+98 -7

No files matched your search

@@ -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(),
},
],
@@ -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();