0
Workspace or Schedule error handler Failure
One script reply has been approved by the moderators Verified

Error handler script sending an error message to a Microsoft Teams channel. It can be used for both a global workspace error handler and specific schedules error handlers

NOTE: this script uses the Microsoft Teams workspace settings resource (EE only)

Created by alex308 175 days ago Viewed 15091 times
0
Submitted by alex308 Bun
Verified 175 days ago
1
import dayjs from "dayjs";
2
import * as wmill from "windmill-client";
3

4
function formatError(error: any) {
5
  if (error.stack && error.name && error.message) {
6
    return `*${error.name}: ${error.message}*\`\`\`\n${error.stack}\n\`\`\``;
7
  } else {
8
    return `\n${JSON.stringify(error, null, 2)}\n`;
9
  }
10
}
11

12
function getTriggerInfo(baseUrl: string, workspace_id: string, trigger_path?: string) {
13
  if (trigger_path) {
14
    const triggerType = trigger_path.split("/")[0];
15
    const prettyTriggerType = triggerType.charAt(0).toUpperCase() + triggerType.replace("_", " ").slice(1);
16
    const triggerTypePath = triggerType === "http_trigger" ? "routes" : triggerType + "s";
17
    const triggerPath = trigger_path.replace(`${triggerType}/`, "");
18
    const url = `${baseUrl}/${triggerTypePath}?workspace=${encodeURIComponent(workspace_id)}\#${encodeURIComponent(
19
      triggerPath
20
    )}`;
21
    return {
22
      prettyTriggerType,
23
      triggerPath,
24
      url,
25
    };
26
  }
27
}
28

29
export async function main(
30
  workspace_id: string,
31
  job_id: string, // The UUID of the job that errored
32
  path: string, // The path of the script or flow that errored
33
  is_flow: boolean, // Whether the runnable is a flow
34
  error: object, // The error details
35
  started_at: string, // The start datetime of the latest job that failed
36
  channel: string,
37
  schedule_path?: string, // The path of the schedule
38
  trigger_path?: string, // The path of the trigger that errored in the format <trigger_type>/<trigger_path>
39
  failed_times?: number // Minimum number of times the schedule failed before calling the error handler
40
) {
41
  const baseUrl = process.env["WM_BASE_URL"];
42
  const scheduleUrl =
43
    baseUrl +
44
    "/runs?schedule_path=" +
45
    encodeURIComponent(schedule_path) +
46
    "&workspace=" +
47
    encodeURIComponent(workspace_id);
48
  const scriptOrFlowRunUrl =
49
    baseUrl + (is_flow ? "/flows/get/" : "/scripts/get/") + path + "?workspace=" + encodeURIComponent(workspace_id);
50
  const jobRunUrl = baseUrl + "/run/" + job_id + "?workspace=" + encodeURIComponent(workspace_id);
51
  const triggerInfo = getTriggerInfo(baseUrl, workspace_id, trigger_path);
52

53
  let mdText = "";
54
  if (schedule_path) {
55
    mdText = `*Schedule [${schedule_path}](${scheduleUrl}) failed*${
56
      failed_times > 1 ? " " + failed_times + " times in a row" : ""
57
    }:\n- Run [${job_id}](${jobRunUrl}) of ${is_flow ? "flow" : "script"}: [${path}](${scriptOrFlowRunUrl})`;
58
  } else if (triggerInfo) {
59
    mdText = `*${triggerInfo.prettyTriggerType} [${triggerInfo.triggerPath}](${
60
      triggerInfo.url
61
    }) failed*\n- Run [${job_id}](${jobRunUrl}) of ${is_flow ? "flow" : "script"}: [${path}](${scriptOrFlowRunUrl})`;
62
  } else {
63
    mdText = `*Run [${job_id}](${jobRunUrl}) of ${
64
      is_flow ? "flow" : "script"
65
    } [${path}](${scriptOrFlowRunUrl}) failed*:`;
66
  }
67

68
  const lastFailureText = `${(failed_times ?? 1) > 1 ? "Last failed" : "Failed"} job started at: ${dayjs(
69
    started_at
70
  ).format("DD.MM.YYYY HH:mm (Z)")}\n`;
71

72
  await wmill.TeamsService.sendMessageToConversation({
73
    requestBody: {
74
      conversation_id: channel,
75
      success: true,
76
      text: mdText,
77
      card_block: {
78
        type: "message",
79
        attachments: [
80
          {
81
            contentType: "application/vnd.microsoft.card.adaptive",
82
            content: {
83
              type: "AdaptiveCard",
84
              body: [
85
                {
86
                  type: "TextBlock",
87
                  size: "Medium",
88
                  weight: "Bolder",
89
                  text: "Error Handler",
90
                },
91
                {
92
                  type: "ColumnSet",
93
                  columns: [
94
                    {
95
                      type: "Column",
96
                      items: [
97
                        {
98
                          type: "Icon",
99
                          name: "Warning",
100
                          size: "Medium",
101
                          style: "Filled",
102
                          color: "Attention",
103
                        },
104
                      ],
105
                      width: "auto",
106
                    },
107
                    {
108
                      type: "Column",
109
                      items: [
110
                        {
111
                          type: "TextBlock",
112
                          text: mdText,
113
                          wrap: true,
114
                        },
115
                        {
116
                          type: "TextBlock",
117
                          text: lastFailureText,
118
                          wrap: true,
119
                        },
120
                        {
121
                          type: "CodeBlock",
122
                          codeSnippet: formatError(error),
123
                          language: "JavaScript",
124
                        },
125
                      ],
126
                      width: "stretch",
127
                    },
128
                  ],
129
                },
130
              ],
131
              $schema: "http://adaptivecards.io/schemas/adaptive-card.json",
132
              version: "1.6",
133
            },
134
          },
135
        ],
136
      },
137
    },
138
  });
139
}
140