From 2cbc667f300b52450ced44e15f245334b8ef8d51 Mon Sep 17 00:00:00 2001 From: "tobias.bruckert@mcon-group.com" <62531735+tb102122@users.noreply.github.com> Date: Mon, 9 Mar 2026 08:43:14 +1100 Subject: [PATCH] add type checks and remove unneeded vars --- .../notificationProviders/teams.ts | 64 +++++++++++++++++-- .../src/validation/notificationValidation.ts | 13 ++-- 2 files changed, 63 insertions(+), 14 deletions(-) diff --git a/server/src/service/infrastructure/notificationProviders/teams.ts b/server/src/service/infrastructure/notificationProviders/teams.ts index dd9b217a7..2a4476807 100644 --- a/server/src/service/infrastructure/notificationProviders/teams.ts +++ b/server/src/service/infrastructure/notificationProviders/teams.ts @@ -6,6 +6,58 @@ import { getTestMessage } from "@/service/infrastructure/notificationProviders/u import type { ILogger } from "@/utils/logger.js"; import got, { HTTPError } from "got"; +// Types for Adaptive Card elements +type TextBlock = { + type: "TextBlock"; + text: string; + weight?: "Bolder" | "Normal" | "Lighter"; + size?: "Small" | "Medium" | "Large" | "ExtraLarge"; + color?: "Default" | "Dark" | "Light" | "Accent" | "Good" | "Warning" | "Attention"; + wrap?: boolean; + spacing?: "None" | "Small" | "Medium" | "Large" | "ExtraLarge" | "Auto"; + isSubtle?: boolean; +}; + +type ColumnSet = { + type: "ColumnSet"; + separator?: boolean; + spacing?: "None" | "Small" | "Medium" | "Large" | "ExtraLarge" | "Auto"; + columns: unknown[]; +}; + +type Fact = { + title: string; + value: string; +}; + +type FactSet = { + type: "FactSet"; + facts: Fact[]; +}; + +type Action = { + type: string; + title: string; + url?: string; +}; + +type AdaptiveCard = { + type: "AdaptiveCard"; + $schema: "http://adaptivecards.io/schemas/adaptive-card.json"; + version: "1.4"; + body: (TextBlock | ColumnSet | FactSet)[]; + actions?: Action[]; +}; + +type TeamsMessage = { + type: "message"; + attachments: Array<{ + contentType: "application/vnd.microsoft.card.adaptive"; + contentUrl: null; + content: AdaptiveCard; + }>; +}; + export class TeamsProvider implements INotificationProvider { private logger: ILogger; @@ -87,7 +139,7 @@ export class TeamsProvider implements INotificationProvider { /** * Wrap an Adaptive Card in the Teams webhook envelope format */ - private wrapAdaptiveCard(card: object): object { + private wrapAdaptiveCard(card: AdaptiveCard): TeamsMessage { return { type: "message", attachments: [ @@ -103,16 +155,16 @@ export class TeamsProvider implements INotificationProvider { /** * Build an Adaptive Card from NotificationMessage */ - private buildAdaptiveCard(message: NotificationMessage): object { + private buildAdaptiveCard(message: NotificationMessage): AdaptiveCard { const colorMap: Record = { critical: "attention", warning: "warning", success: "good", info: "accent", }; - const color = colorMap[message.severity] || "default"; + const color = (colorMap[message.severity] || "default") as "Default" | "Dark" | "Light" | "Accent" | "Good" | "Warning" | "Attention" | undefined; - const body: any[] = []; + const body: (TextBlock | ColumnSet | FactSet)[] = []; // Header with colored status indicator body.push({ @@ -202,12 +254,12 @@ export class TeamsProvider implements INotificationProvider { }); // Actions (incident link) - const actions: any[] = []; + const actions: Action[] = []; if (message.content.incident) { actions.push({ type: "Action.OpenUrl", title: "View Incident", - url: `${message.clientHost}/infrastructure/${message.monitor.id}`, + url: `${message.clientHost}/incidents/${message.content.incident.id}`, }); } diff --git a/server/src/validation/notificationValidation.ts b/server/src/validation/notificationValidation.ts index 4c66302d9..5aa0b976e 100644 --- a/server/src/validation/notificationValidation.ts +++ b/server/src/validation/notificationValidation.ts @@ -18,7 +18,7 @@ export const createNotificationBodyValidation = z.discriminatedUnion("type", [ z.object({ notificationName: z.string().min(1, "Notification name is required"), type: z.literal("webhook"), - address: z.string().url("Please enter a valid Webhook URL"), + address: z.url({ message: "Please enter a valid Webhook URL" }), homeserverUrl: z.union([z.string(), z.literal("")]).optional(), roomId: z.union([z.string(), z.literal("")]).optional(), accessToken: z.union([z.string(), z.literal("")]).optional(), @@ -27,7 +27,7 @@ export const createNotificationBodyValidation = z.discriminatedUnion("type", [ z.object({ notificationName: z.string().min(1, "Notification name is required"), type: z.literal("slack"), - address: z.string().url("Please enter a valid Webhook URL"), + address: z.url({ message: "Please enter a valid Webhook URL" }), homeserverUrl: z.union([z.string(), z.literal("")]).optional(), roomId: z.union([z.string(), z.literal("")]).optional(), accessToken: z.union([z.string(), z.literal("")]).optional(), @@ -36,7 +36,7 @@ export const createNotificationBodyValidation = z.discriminatedUnion("type", [ z.object({ notificationName: z.string().min(1, "Notification name is required"), type: z.literal("discord"), - address: z.string().url("Please enter a valid Webhook URL"), + address: z.url({ message: "Please enter a valid Webhook URL" }), homeserverUrl: z.union([z.string(), z.literal("")]).optional(), roomId: z.union([z.string(), z.literal("")]).optional(), accessToken: z.union([z.string(), z.literal("")]).optional(), @@ -55,7 +55,7 @@ export const createNotificationBodyValidation = z.discriminatedUnion("type", [ notificationName: z.string().min(1, "Notification name is required"), type: z.literal("matrix"), address: z.union([z.string(), z.literal("")]).optional(), - homeserverUrl: z.string().url("Please enter a valid Homeserver URL"), + homeserverUrl: z.url({ message: "Please enter a valid Homeserver URL" }), roomId: z.string().min(1, "Room ID is required"), accessToken: z.string().min(1, "Access Token is required"), }), @@ -63,10 +63,7 @@ export const createNotificationBodyValidation = z.discriminatedUnion("type", [ z.object({ notificationName: z.string().min(1, "Notification name is required"), type: z.literal("teams"), - address: z.string().url("Please enter a valid Webhook URL"), - homeserverUrl: z.union([z.string(), z.literal("")]).optional(), - roomId: z.union([z.string(), z.literal("")]).optional(), - accessToken: z.union([z.string(), z.literal("")]).optional(), + address: z.url({ message: "Please enter a valid Webhook URL" }), }), ]);