From b03ae708ff1d71c6182a69eded254cbe5faaa1c3 Mon Sep 17 00:00:00 2001
From: HenryNguyen5 <6404866+HenryNguyen5@users.noreply.github.com>
Date: Tue, 10 Sep 2024 15:53:21 -0700
Subject: [PATCH] Add better error handling

---
 .github/scripts/jira/axios.ts                 | 97 +++++++++++++++++++
 .../scripts/jira/create-jira-traceability.ts  | 26 +++--
 .github/scripts/jira/enforce-jira-issue.ts    |  4 +-
 .github/scripts/jira/lib.ts                   | 18 +++-
 .github/scripts/jira/package.json             |  3 +
 .github/scripts/jira/pnpm-lock.yaml           | 11 ++-
 .github/scripts/jira/tsconfig.json            |  2 +-
 7 files changed, 145 insertions(+), 16 deletions(-)
 create mode 100644 .github/scripts/jira/axios.ts

diff --git a/.github/scripts/jira/axios.ts b/.github/scripts/jira/axios.ts
new file mode 100644
index 00000000000..3a912797713
--- /dev/null
+++ b/.github/scripts/jira/axios.ts
@@ -0,0 +1,97 @@
+import {
+  AxiosRequestConfig,
+  AxiosResponse,
+  AxiosError,
+  InternalAxiosRequestConfig,
+} from "axios";
+import { Readable } from "stream";
+
+interface AxiosErrorFormat<Data = any> {
+  config: Pick<AxiosRequestConfig, (typeof CONFIG_KEYS)[number]>;
+  code?: string;
+  response: Partial<Pick<AxiosResponse<Data>, (typeof RESPONSE_KEYS)[number]>>;
+  isAxiosError: boolean;
+}
+
+interface AxiosErrorFormatError<Data = any>
+  extends Error,
+    AxiosErrorFormat<Data> {}
+
+export function formatAxiosError<Data = any>(
+  origErr: AxiosError<Data>
+): AxiosErrorFormatError<Data> {
+  const { message, name, stack, code, config, response, isAxiosError } =
+    origErr;
+
+  const err: AxiosErrorFormatError = {
+    ...new Error(message),
+    name,
+    stack,
+    code,
+    isAxiosError,
+    config: {},
+    response: {},
+  };
+
+  for (const k of CONFIG_KEYS) {
+    if (config?.[k] === undefined) {
+      continue;
+    }
+
+    err.config[k] = formatValue(config[k], k);
+  }
+
+  for (const k of RESPONSE_KEYS) {
+    if (response?.[k] === undefined) {
+      continue;
+    }
+
+    err.response[k] = formatValue(response[k], k);
+  }
+
+  return err as any;
+}
+
+const CONFIG_KEYS: (keyof InternalAxiosRequestConfig)[] = [
+  "url",
+  "method",
+  "baseURL",
+  "params",
+  "data",
+  "timeout",
+  "timeoutErrorMessage",
+  "withCredentials",
+  "auth",
+  "responseType",
+  "xsrfCookieName",
+  "xsrfHeaderName",
+  "maxContentLength",
+  "maxBodyLength",
+  "maxRedirects",
+  "socketPath",
+  "proxy",
+  "decompress",
+] as const;
+
+const RESPONSE_KEYS: (keyof AxiosResponse)[] = [
+  "data",
+  "status",
+  "statusText",
+] as const;
+
+function formatValue(
+  value: any,
+  key: (typeof CONFIG_KEYS)[number] | (typeof RESPONSE_KEYS)[number]
+): any {
+  if (key !== "data") {
+    return value;
+  }
+
+  if (process.env.BROWSER !== "true") {
+    if (value instanceof Readable) {
+      return "[Readable]";
+    }
+  }
+
+  return value;
+}
diff --git a/.github/scripts/jira/create-jira-traceability.ts b/.github/scripts/jira/create-jira-traceability.ts
index b151c9d5eab..cda038a7cc9 100644
--- a/.github/scripts/jira/create-jira-traceability.ts
+++ b/.github/scripts/jira/create-jira-traceability.ts
@@ -5,6 +5,7 @@ import {
   generateIssueLabel,
   generateJiraIssuesLink,
   getJiraEnvVars,
+  handleError,
 } from "./lib";
 import * as core from "@actions/core";
 
@@ -191,15 +192,24 @@ async function main() {
 
   const client = createJiraClient();
   const label = generateIssueLabel(product, baseRef, headRef);
-  await addTraceabillityToJiraIssues(
-    client,
-    jiraIssueNumbers,
-    label,
-    artifactUrl
-  );
+  try {
+    await addTraceabillityToJiraIssues(
+      client,
+      jiraIssueNumbers,
+      label,
+      artifactUrl
+    );
+  } catch (e) {
+    handleError(e);
 
-  const { jiraHost } = getJiraEnvVars()
-  core.summary.addLink("Jira Issues", generateJiraIssuesLink(`${jiraHost}/issues/`, label));
+    process.exit(1);
+  }
+
+  const { jiraHost } = getJiraEnvVars();
+  core.summary.addLink(
+    "Jira Issues",
+    generateJiraIssuesLink(`${jiraHost}/issues/`, label)
+  );
   core.summary.write();
 }
 main();
diff --git a/.github/scripts/jira/enforce-jira-issue.ts b/.github/scripts/jira/enforce-jira-issue.ts
index 9d8c6e490ee..153f397e021 100644
--- a/.github/scripts/jira/enforce-jira-issue.ts
+++ b/.github/scripts/jira/enforce-jira-issue.ts
@@ -1,6 +1,6 @@
 import * as core from "@actions/core";
 import jira from "jira.js";
-import { createJiraClient, getGitTopLevel, parseIssueNumberFrom } from "./lib";
+import { createJiraClient, getGitTopLevel, handleError, parseIssueNumberFrom } from "./lib";
 import { promises as fs } from "fs";
 import { join } from "path";
 
@@ -36,7 +36,7 @@ async function doesIssueExist(
 
     return true;
   } catch (e) {
-    core.debug(e as any);
+    handleError(e)
     return false;
   }
 }
diff --git a/.github/scripts/jira/lib.ts b/.github/scripts/jira/lib.ts
index 0d0983f5c3e..8be295ab14f 100644
--- a/.github/scripts/jira/lib.ts
+++ b/.github/scripts/jira/lib.ts
@@ -4,7 +4,8 @@ import * as jira from "jira.js";
 import { exec } from "child_process";
 import { promisify } from "util";
 import { join } from "path";
-
+import { isAxiosError } from "axios";
+import { formatAxiosError } from "./axios";
 export function generateJiraIssuesLink(baseUrl: string, label: string) {
   // https://smartcontract-it.atlassian.net/issues/?jql=labels%20%3D%20%22review-artifacts-automation-base%3A8d818ea265ff08887e61ace4f83364a3ee149ef0-head%3A3c45b71f3610de28f429cef0163936eaa448e63c%22
   const jqlQuery = `labels = "${label}"`;
@@ -129,3 +130,18 @@ export function createJiraClient() {
     },
   });
 }
+
+export function handleError(e: unknown) {
+  if (e instanceof Error) {
+    if (isAxiosError(e)) {
+      core.error(formatAxiosError(e));
+    } else if (isAxiosError(e.cause)) {
+      core.error(formatAxiosError(e.cause));
+    } else {
+      core.error(e);
+    }
+  } else {
+    core.error(JSON.stringify(e));
+  }
+  core.setFailed("Error adding traceability to Jira issues");
+}
diff --git a/.github/scripts/jira/package.json b/.github/scripts/jira/package.json
index 94a805314af..6081e489818 100644
--- a/.github/scripts/jira/package.json
+++ b/.github/scripts/jira/package.json
@@ -27,5 +27,8 @@
     "@types/node": "^20.14.10",
     "typescript": "^5.5.3",
     "vitest": "^2.0.3"
+  },
+  "peerDependencies": {
+    "axios": "^1.7.7"
   }
 }
diff --git a/.github/scripts/jira/pnpm-lock.yaml b/.github/scripts/jira/pnpm-lock.yaml
index 4deeef7f339..a52fa9dd0c8 100644
--- a/.github/scripts/jira/pnpm-lock.yaml
+++ b/.github/scripts/jira/pnpm-lock.yaml
@@ -11,6 +11,9 @@ importers:
       '@actions/core':
         specifier: ^1.10.1
         version: 1.10.1
+      axios:
+        specifier: ^1.7.7
+        version: 1.7.7
       jira.js:
         specifier: ^4.0.1
         version: 4.0.1
@@ -311,8 +314,8 @@ packages:
   asynckit@0.4.0:
     resolution: {integrity: sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==}
 
-  axios@1.7.2:
-    resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==}
+  axios@1.7.7:
+    resolution: {integrity: sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==}
 
   cac@6.7.14:
     resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==}
@@ -811,7 +814,7 @@ snapshots:
 
   asynckit@0.4.0: {}
 
-  axios@1.7.2:
+  axios@1.7.7:
     dependencies:
       follow-redirects: 1.15.6
       form-data: 4.0.0
@@ -918,7 +921,7 @@ snapshots:
 
   jira.js@4.0.1:
     dependencies:
-      axios: 1.7.2
+      axios: 1.7.7
       form-data: 4.0.0
       tslib: 2.6.3
     transitivePeerDependencies:
diff --git a/.github/scripts/jira/tsconfig.json b/.github/scripts/jira/tsconfig.json
index 746f76774b0..3e3216e6e05 100644
--- a/.github/scripts/jira/tsconfig.json
+++ b/.github/scripts/jira/tsconfig.json
@@ -11,7 +11,7 @@
     // "disableReferencedProjectLoad": true,             /* Reduce the number of projects loaded automatically by TypeScript. */
 
     /* Language and Environment */
-    "target": "ES2020",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
+    "target": "ES2022",                                  /* Set the JavaScript language version for emitted JavaScript and include compatible library declarations. */
     // "lib": [],                                        /* Specify a set of bundled library declaration files that describe the target runtime environment. */
     // "jsx": "preserve",                                /* Specify what JSX code is generated. */
     // "experimentalDecorators": true,                   /* Enable experimental support for legacy experimental decorators. */