From 31653efd40b976dfa52032a101f30c0dbf515ec5 Mon Sep 17 00:00:00 2001 From: Aubin Date: Fri, 20 Dec 2024 15:57:34 +0100 Subject: [PATCH] improve error handling --- .../src/connectors/zendesk/lib/errors.ts | 9 +++++ .../src/connectors/zendesk/lib/zendesk_api.ts | 34 +++++++++++++------ 2 files changed, 32 insertions(+), 11 deletions(-) diff --git a/connectors/src/connectors/zendesk/lib/errors.ts b/connectors/src/connectors/zendesk/lib/errors.ts index d80a9bc2d8eb..6bb17d7bf404 100644 --- a/connectors/src/connectors/zendesk/lib/errors.ts +++ b/connectors/src/connectors/zendesk/lib/errors.ts @@ -46,6 +46,15 @@ export function isZendeskExpiredCursorError( ); } +/** + * Catches 404 errors that were already caught in fetchFromZendeskWithRetries and rethrown as ZendeskApiErrors. + * The idea is that we only try/catch the part where we call the API, without wrapping any of our code and from then + * only certain functions can actually handle 404 by returning a null. + */ +export function isZendeskNotFoundError(err: unknown): boolean { + return err instanceof ZendeskApiError && err.status === 404; +} + export function isNodeZendeskEpipeError(err: unknown): err is NodeZendeskError { return ( typeof err === "object" && diff --git a/connectors/src/connectors/zendesk/lib/zendesk_api.ts b/connectors/src/connectors/zendesk/lib/zendesk_api.ts index f7b043c4fc4b..7dba68f1851f 100644 --- a/connectors/src/connectors/zendesk/lib/zendesk_api.ts +++ b/connectors/src/connectors/zendesk/lib/zendesk_api.ts @@ -9,7 +9,10 @@ import type { ZendeskFetchedTicket, ZendeskFetchedUser, } from "@connectors/@types/node-zendesk"; -import { ZendeskApiError } from "@connectors/connectors/zendesk/lib/errors"; +import { + isZendeskNotFoundError, + ZendeskApiError, +} from "@connectors/connectors/zendesk/lib/errors"; import { setTimeoutAsync } from "@connectors/lib/async_utils"; import logger from "@connectors/logger/logger"; import type { ZendeskCategoryResource } from "@connectors/resources/zendesk_resources"; @@ -118,6 +121,7 @@ async function handleZendeskRateLimit(response: Response): Promise { /** * Runs a GET request to the Zendesk API with a maximum number of retries before throwing. + * TODO(2024-12-20): add some basic io-ts validation here (pass a codec as argument and decode with it) */ async function fetchFromZendeskWithRetries({ url, @@ -155,9 +159,6 @@ async function fetchFromZendeskWithRetries({ try { response = await rawResponse.json(); } catch (e) { - if (rawResponse.status === 404) { - return null; - } throw new ZendeskApiError( "Error parsing Zendesk API response", rawResponse.status, @@ -165,9 +166,6 @@ async function fetchFromZendeskWithRetries({ ); } if (!rawResponse.ok) { - if (rawResponse.status === 404) { - return null; - } throw new ZendeskApiError( "Zendesk API error.", rawResponse.status, @@ -191,8 +189,15 @@ export async function fetchZendeskBrand({ brandId: number; }): Promise { const url = `https://${subdomain}.zendesk.com/api/v2/brands/${brandId}`; - const response = await fetchFromZendeskWithRetries({ url, accessToken }); - return response?.brand ?? null; + try { + const response = await fetchFromZendeskWithRetries({ url, accessToken }); + return response?.brand ?? null; + } catch (e) { + if (isZendeskNotFoundError(e)) { + return null; + } + throw e; + } } /** @@ -208,8 +213,15 @@ export async function fetchZendeskArticle({ articleId: number; }): Promise { const url = `https://${brandSubdomain}.zendesk.com/api/v2/help_center/articles/${articleId}`; - const response = await fetchFromZendeskWithRetries({ url, accessToken }); - return response?.article ?? null; + try { + const response = await fetchFromZendeskWithRetries({ url, accessToken }); + return response?.article ?? null; + } catch (e) { + if (isZendeskNotFoundError(e)) { + return null; + } + throw e; + } } /**