fix incident links with helper
This commit is contained in:
1 parent
618258d282
commit
7fb57d8090
20 files changed
+83
-34
No files matched your search
@@ -51,6 +51,22 @@ export const UptimeDetailsSupportedTypes = ["http", "ping", "port", "game", "grp
|
||||
export type UptimeDetailsSupportedType = (typeof UptimeDetailsSupportedTypes)[number];
|
||||
export const supportsUptimeDetails = (type: MonitorType): type is UptimeDetailsSupportedType => UptimeDetailsSupportedTypes.some((t) => t === type);
|
||||
|
||||
const MonitorPaths: Record<MonitorType, string> = {
|
||||
http: "uptime",
|
||||
port: "uptime",
|
||||
ping: "uptime",
|
||||
game: "uptime",
|
||||
grpc: "uptime",
|
||||
websocket: "uptime",
|
||||
dns: "uptime",
|
||||
unknown: "uptime",
|
||||
docker: "docker/host",
|
||||
hardware: "infrastructure",
|
||||
pagespeed: "pagespeed",
|
||||
};
|
||||
|
||||
export const getMonitorPath = (type: MonitorType): string => MonitorPaths[type];
|
||||
|
||||
// Types whose check leaves the instance, and whose failure to reach the target can therefore be the instance's
|
||||
// own loss of egress. A hardware check is an ordinary outbound HTTP request to the Capture agent, so it counts
|
||||
// however far away the agent is. `unknown` is excluded because no request is made for it.
|
||||
|
||||
@@ -3,7 +3,7 @@ import got from "got";
|
||||
import { NotificationProvider } from "@/domain/notifications/providers/INotificationProvider.js";
|
||||
import type { AlertMatrixPayload, Notification } from "@/domain/notifications/notification.type.js";
|
||||
import type { NotificationMessage } from "@/domain/notifications/notification.type.js";
|
||||
import { getTestMessage } from "@/domain/notifications/providers/utils.js";
|
||||
import { getTestMessage, incidentUrl } from "@/domain/notifications/providers/utils.js";
|
||||
import { randomUUID } from "crypto";
|
||||
|
||||
export class MatrixProvider extends NotificationProvider {
|
||||
@@ -172,10 +172,10 @@ export class MatrixProvider extends NotificationProvider {
|
||||
|
||||
// Incident link (if incident exists)
|
||||
if (message.content.incident) {
|
||||
const incidentUrl = `${message.clientHost}/infrastructure/${message.monitor.id}`;
|
||||
const url = incidentUrl(message);
|
||||
plainLines.push("");
|
||||
plainLines.push(`View Incident: ${incidentUrl}`);
|
||||
htmlLines.push(`<p><a href="${this.escapeHtml(incidentUrl)}">View Incident</a></p>`);
|
||||
plainLines.push(`View Incident: ${url}`);
|
||||
htmlLines.push(`<p><a href="${this.escapeHtml(url)}">View Incident</a></p>`);
|
||||
}
|
||||
|
||||
// Footer with timestamp
|
||||
|
||||
@@ -2,7 +2,7 @@ const SERVICE_NAME = "NtfyProvider";
|
||||
import type { Notification } from "@/domain/notifications/notification.type.js";
|
||||
import { NotificationProvider } from "@/domain/notifications/providers/INotificationProvider.js";
|
||||
import type { NotificationMessage } from "@/domain/notifications/notification.type.js";
|
||||
import { getTestMessage } from "@/domain/notifications/providers/utils.js";
|
||||
import { getTestMessage, incidentUrl } from "@/domain/notifications/providers/utils.js";
|
||||
import got from "got";
|
||||
|
||||
export class NtfyProvider extends NotificationProvider {
|
||||
@@ -142,7 +142,7 @@ export class NtfyProvider extends NotificationProvider {
|
||||
}
|
||||
|
||||
if (message.content.incident) {
|
||||
lines.push("", `${message.clientHost}/infrastructure/${message.monitor.id}`);
|
||||
lines.push("", incidentUrl(message));
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
|
||||
@@ -2,7 +2,7 @@ const SERVICE_NAME = "PushoverProvider";
|
||||
import type { Notification } from "@/domain/notifications/notification.type.js";
|
||||
import { NotificationProvider } from "@/domain/notifications/providers/INotificationProvider.js";
|
||||
import type { NotificationMessage } from "@/domain/notifications/notification.type.js";
|
||||
import { getTestMessage } from "@/domain/notifications/providers/utils.js";
|
||||
import { getTestMessage, incidentUrl } from "@/domain/notifications/providers/utils.js";
|
||||
import got from "got";
|
||||
|
||||
export class PushoverProvider extends NotificationProvider {
|
||||
@@ -101,7 +101,7 @@ export class PushoverProvider extends NotificationProvider {
|
||||
|
||||
if (message.content.incident) {
|
||||
lines.push("");
|
||||
lines.push(`View Incident: ${message.clientHost}/incidents/${message.monitor.id}`);
|
||||
lines.push(`View Incident: ${incidentUrl(message)}`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
const SERVICE_NAME = "RocketChatProvider";
|
||||
import { supportsUptimeDetails, type MonitorType } from "@/domain/monitors/monitor.type.js";
|
||||
import { getMonitorPath, 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";
|
||||
@@ -139,19 +139,7 @@ export class RocketChatProvider extends NotificationProvider {
|
||||
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;
|
||||
return `${clientHost}/${getMonitorPath(message.monitor.type as MonitorType)}/${message.monitor.id}`;
|
||||
}
|
||||
|
||||
private severityColor(severity: NotificationMessage["severity"]): string {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
const SERVICE_NAME = "SignalgridProvider";
|
||||
import type { Notification, NotificationMessage, NotificationSeverity } from "@/domain/notifications/notification.type.js";
|
||||
import { NotificationProvider } from "@/domain/notifications/providers/INotificationProvider.js";
|
||||
import { getTestMessage } from "@/domain/notifications/providers/utils.js";
|
||||
import { getTestMessage, incidentUrl } from "@/domain/notifications/providers/utils.js";
|
||||
import got from "got";
|
||||
|
||||
export class SignalgridProvider extends NotificationProvider {
|
||||
@@ -115,7 +115,7 @@ export class SignalgridProvider extends NotificationProvider {
|
||||
|
||||
if (message.content.incident) {
|
||||
lines.push("");
|
||||
lines.push(`View Incident: ${message.clientHost}/incidents/${message.monitor.id}`);
|
||||
lines.push(`View Incident: ${incidentUrl(message)}`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
|
||||
@@ -2,7 +2,7 @@ const SERVICE_NAME = "SlackProvider";
|
||||
import type { Notification } from "@/domain/notifications/notification.type.js";
|
||||
import { NotificationProvider } from "@/domain/notifications/providers/INotificationProvider.js";
|
||||
import type { NotificationMessage } from "@/domain/notifications/notification.type.js";
|
||||
import { getTestMessage } from "@/domain/notifications/providers/utils.js";
|
||||
import { getTestMessage, incidentUrl } from "@/domain/notifications/providers/utils.js";
|
||||
import got, { HTTPError } from "got";
|
||||
|
||||
export class SlackProvider extends NotificationProvider {
|
||||
@@ -161,7 +161,7 @@ export class SlackProvider extends NotificationProvider {
|
||||
text: "View Incident",
|
||||
emoji: true,
|
||||
},
|
||||
url: `${message.clientHost}/infrastructure/${message.monitor.id}`,
|
||||
url: incidentUrl(message),
|
||||
style: "primary",
|
||||
},
|
||||
],
|
||||
|
||||
@@ -2,7 +2,7 @@ const SERVICE_NAME = "TeamsProvider";
|
||||
import type { Notification } from "@/domain/notifications/notification.type.js";
|
||||
import { NotificationProvider } from "@/domain/notifications/providers/INotificationProvider.js";
|
||||
import type { NotificationMessage } from "@/domain/notifications/notification.type.js";
|
||||
import { getTestMessage } from "@/domain/notifications/providers/utils.js";
|
||||
import { getTestMessage, incidentUrl } from "@/domain/notifications/providers/utils.js";
|
||||
import got, { HTTPError } from "got";
|
||||
|
||||
// Types for Adaptive Card elements
|
||||
@@ -254,7 +254,7 @@ export class TeamsProvider extends NotificationProvider {
|
||||
actions.push({
|
||||
type: "Action.OpenUrl",
|
||||
title: "View Incident",
|
||||
url: `${message.clientHost}/incidents/${message.content.incident.id}`,
|
||||
url: incidentUrl(message),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@ const SERVICE_NAME = "TelegramProvider";
|
||||
import type { Notification } from "@/domain/notifications/notification.type.js";
|
||||
import { NotificationProvider } from "@/domain/notifications/providers/INotificationProvider.js";
|
||||
import type { NotificationMessage } from "@/domain/notifications/notification.type.js";
|
||||
import { getTestMessage } from "@/domain/notifications/providers/utils.js";
|
||||
import { getTestMessage, incidentUrl } from "@/domain/notifications/providers/utils.js";
|
||||
import got from "got";
|
||||
|
||||
export class TelegramProvider extends NotificationProvider {
|
||||
@@ -101,7 +101,7 @@ export class TelegramProvider extends NotificationProvider {
|
||||
|
||||
if (message.content.incident) {
|
||||
lines.push("");
|
||||
lines.push(`<a href="${message.clientHost}/incidents/${message.monitor.id}">View Incident</a>`);
|
||||
lines.push(`<a href="${incidentUrl(message)}">View Incident</a>`);
|
||||
}
|
||||
|
||||
return lines.join("\n");
|
||||
|
||||
@@ -1,4 +1,8 @@
|
||||
import { IEmailService } from "@/service/emailService.js";
|
||||
import type { NotificationMessage } from "@/domain/notifications/notification.type.js";
|
||||
|
||||
export const incidentUrl = (message: NotificationMessage): string => `${message.clientHost}/incidents/${message.monitor.id}`;
|
||||
|
||||
export const buildTestEmail = async (emailService: IEmailService) => {
|
||||
const context = { testName: "Monitoring System" };
|
||||
const html = await emailService.buildEmail("testEmailTemplate", context);
|
||||
|
||||
@@ -2,7 +2,7 @@ const SERVICE_NAME = "WebhookProvider";
|
||||
import type { Notification } from "@/domain/notifications/notification.type.js";
|
||||
import { NotificationProvider } from "@/domain/notifications/providers/INotificationProvider.js";
|
||||
import type { NotificationMessage } from "@/domain/notifications/notification.type.js";
|
||||
import { getTestMessage } from "@/domain/notifications/providers/utils.js";
|
||||
import { getTestMessage, incidentUrl } from "@/domain/notifications/providers/utils.js";
|
||||
import got from "got";
|
||||
|
||||
export class WebhookProvider extends NotificationProvider {
|
||||
@@ -74,7 +74,7 @@ export class WebhookProvider extends NotificationProvider {
|
||||
|
||||
// Incident link
|
||||
if (message.content.incident) {
|
||||
lines.push(`[View Incident](${message.clientHost}/infrastructure/${message.monitor.id})`);
|
||||
lines.push(`[View Incident](${incidentUrl(message)})`);
|
||||
}
|
||||
|
||||
// Return webhook payload with both text and structured data
|
||||
|
||||
@@ -105,6 +105,14 @@ describe("MatrixProvider", () => {
|
||||
expect(body.formatted_body).toContain("View Incident");
|
||||
});
|
||||
|
||||
it("links the incident to the monitor's incidents page", async () => {
|
||||
const { provider } = createProvider();
|
||||
await provider.sendMessage(makeNotification() as any, makeMessageWithIncident());
|
||||
const body = mockGotPut.mock.calls[0][1].json;
|
||||
expect(body.body).toContain("View Incident: https://app.example.com/incidents/mon-1");
|
||||
expect(body.formatted_body).toContain('href="https://app.example.com/incidents/mon-1"');
|
||||
});
|
||||
|
||||
it("omits optional sections when not present", async () => {
|
||||
const { provider } = createProvider();
|
||||
const msg = makeMessage();
|
||||
|
||||
@@ -106,7 +106,7 @@ describe("NtfyProvider", () => {
|
||||
it("includes incident links in text", async () => {
|
||||
const { provider } = createProvider();
|
||||
await provider.sendMessage(makeNtfyNotification() as any, makeMessageWithIncident());
|
||||
expect(mockGotPost.mock.calls[0][1].body).toContain("/infrastructure/mon-1");
|
||||
expect(mockGotPost.mock.calls[0][1].body).toContain("https://app.example.com/incidents/mon-1");
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -99,6 +99,12 @@ describe("PushoverProvider", () => {
|
||||
expect(mockGotPost.mock.calls[0][1].form.message).toContain("View Incident");
|
||||
});
|
||||
|
||||
it("links the incident to the monitor's incidents page", async () => {
|
||||
const { provider } = createProvider();
|
||||
await provider.sendMessage(makeNotification() as any, makeMessageWithIncident());
|
||||
expect(mockGotPost.mock.calls[0][1].form.message).toContain("View Incident: https://app.example.com/incidents/mon-1");
|
||||
});
|
||||
|
||||
it("omits optional sections when not present", async () => {
|
||||
const { provider } = createProvider();
|
||||
const msg = makeMessage();
|
||||
|
||||
@@ -134,6 +134,7 @@ describe("RocketChatProvider", () => {
|
||||
["ping", "https://app.example.com/uptime/mon-1"],
|
||||
["pagespeed", "https://app.example.com/pagespeed/mon-1"],
|
||||
["hardware", "https://app.example.com/infrastructure/mon-1"],
|
||||
["docker", "https://app.example.com/docker/host/mon-1"],
|
||||
])("links %s monitors to %s", async (type, expectedLink) => {
|
||||
const { provider } = createProvider();
|
||||
|
||||
|
||||
@@ -139,6 +139,12 @@ describe("SignalgridProvider", () => {
|
||||
expect(mockGotPost.mock.calls[0][1].form.body).toContain("View Incident");
|
||||
});
|
||||
|
||||
it("links the incident to the monitor's incidents page", async () => {
|
||||
const { provider } = createProvider();
|
||||
await provider.sendMessage(makeNotification() as any, makeMessageWithIncident());
|
||||
expect(mockGotPost.mock.calls[0][1].form.body).toContain("View Incident: https://app.example.com/incidents/mon-1");
|
||||
});
|
||||
|
||||
it("omits optional sections when not present", async () => {
|
||||
const { provider } = createProvider();
|
||||
const msg = makeMessage();
|
||||
|
||||
@@ -96,6 +96,14 @@ describe("SlackProvider", () => {
|
||||
expect(text).toContain("Additional Information");
|
||||
});
|
||||
|
||||
it("links the incident button to the monitor's incidents page", async () => {
|
||||
const { provider } = createProvider();
|
||||
await provider.sendMessage(makeNotification() as any, makeMessageWithIncident());
|
||||
const blocks = mockGotPost.mock.calls[0][1].json.blocks;
|
||||
const actions = blocks.find((b: any) => b.type === "actions");
|
||||
expect(actions.elements[0].url).toBe("https://app.example.com/incidents/mon-1");
|
||||
});
|
||||
|
||||
it("omits threshold, details, and incident sections when not present", async () => {
|
||||
const { provider } = createProvider();
|
||||
const msg = makeMessage();
|
||||
|
||||
@@ -154,7 +154,7 @@ describe("TeamsProvider", () => {
|
||||
expect(card.actions).toHaveLength(1);
|
||||
expect(card.actions[0].type).toBe("Action.OpenUrl");
|
||||
expect(card.actions[0].title).toBe("View Incident");
|
||||
expect(card.actions[0].url).toContain("/incidents/inc-1");
|
||||
expect(card.actions[0].url).toBe("https://app.example.com/incidents/mon-1");
|
||||
});
|
||||
|
||||
it("omits actions when no incident is present", async () => {
|
||||
|
||||
@@ -99,6 +99,12 @@ describe("TelegramProvider", () => {
|
||||
expect(mockGotPost.mock.calls[0][1].json.text).toContain("View Incident");
|
||||
});
|
||||
|
||||
it("links the incident to the monitor's incidents page", async () => {
|
||||
const { provider } = createProvider();
|
||||
await provider.sendMessage(makeNotification() as any, makeMessageWithIncident());
|
||||
expect(mockGotPost.mock.calls[0][1].json.text).toContain('<a href="https://app.example.com/incidents/mon-1">View Incident</a>');
|
||||
});
|
||||
|
||||
it("omits optional sections when not present", async () => {
|
||||
const { provider } = createProvider();
|
||||
const msg = makeMessage();
|
||||
|
||||
@@ -86,6 +86,12 @@ describe("WebhookProvider", () => {
|
||||
expect(mockGotPost.mock.calls[0][1].json.text).toContain("View Incident");
|
||||
});
|
||||
|
||||
it("links the incident to the monitor's incidents page", async () => {
|
||||
const { provider } = createProvider();
|
||||
await provider.sendMessage(makeNotification() as any, makeMessageWithIncident());
|
||||
expect(mockGotPost.mock.calls[0][1].json.text).toContain("[View Incident](https://app.example.com/incidents/mon-1)");
|
||||
});
|
||||
|
||||
it("omits threshold and incident sections when not present", async () => {
|
||||
const { provider } = createProvider();
|
||||
const msg = makeMessage();
|
||||
|
||||
Reference in new issue
Block a user