From cf7421df87d899a3a12d3197fe6d49894deb923c Mon Sep 17 00:00:00 2001 From: Victor Santos Date: Thu, 23 Oct 2025 14:56:52 -0300 Subject: [PATCH] feat(northflank): enhance error handling for Northflank API responses --- .../northflank/northflank-sync-fns.ts | 46 ++++++++++++++++++- 1 file changed, 44 insertions(+), 2 deletions(-) diff --git a/backend/src/services/secret-sync/northflank/northflank-sync-fns.ts b/backend/src/services/secret-sync/northflank/northflank-sync-fns.ts index bfe845c5e..396aa9ac8 100644 --- a/backend/src/services/secret-sync/northflank/northflank-sync-fns.ts +++ b/backend/src/services/secret-sync/northflank/northflank-sync-fns.ts @@ -1,3 +1,5 @@ +import { AxiosError } from "axios"; + import { request } from "@app/lib/config/request"; import { matchesSchema } from "@app/services/secret-sync/secret-sync-fns"; import { TSecretMap } from "@app/services/secret-sync/secret-sync-types"; @@ -7,6 +9,46 @@ import { TNorthflankSyncWithCredentials } from "./northflank-sync-types"; const NORTHFLANK_API_URL = "https://api.northflank.com"; +const buildNorthflankAPIErrorMessage = (error: unknown): string => { + let errorMessage = "Northflank API returned an error."; + + if (error && typeof error === "object" && "response" in error) { + const axiosError = error as AxiosError; + + if (axiosError.response?.data) { + // This is the shape of the error response from the Northflank API + const responseData = axiosError.response.data as { + error?: { message?: string; details?: Record }; + message?: string; + }; + const errorParts = []; + + if (responseData.error?.message) { + errorParts.push(responseData.error.message); + } else if (responseData.message) { + errorParts.push(responseData.message); + } + + if (responseData.error?.details) { + const { details } = responseData.error; + + // Flatten the details object into a string + Object.entries(details).forEach(([field, fieldErrors]) => { + if (Array.isArray(fieldErrors)) { + fieldErrors.forEach((fieldError) => errorParts.push(`${field}: ${fieldError}`)); + } else { + errorParts.push(`${field}: ${String(fieldErrors)}`); + } + }); + } + + errorMessage += ` ${errorParts.join(". ")}`; + } + } + + return errorMessage; +}; + const getNorthflankSecrets = async (secretSync: TNorthflankSyncWithCredentials): Promise> => { const { destinationConfig: { projectId, secretGroupId }, @@ -39,7 +81,7 @@ const getNorthflankSecrets = async (secretSync: TNorthflankSyncWithCredentials): } catch (error: unknown) { throw new SecretSyncError({ error, - message: "Failed to fetch Northflank secrets" + message: `Failed to fetch Northflank secrets. ${buildNorthflankAPIErrorMessage(error)}` }); } }; @@ -73,7 +115,7 @@ const updateNorthflankSecrets = async ( } catch (error: unknown) { throw new SecretSyncError({ error, - message: "Failed to update Northflank secrets" + message: `Failed to update Northflank secrets. ${buildNorthflankAPIErrorMessage(error)}` }); } };