Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 70 additions & 3 deletions integrations/zoho-sales-iq-hitl/src/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,72 @@ const getZohoAuthUrl = (region: string): string => zohoAuthUrls.get(region) ?? '
// Function to get the Zoho SalesIQ Server URL
const getZohoSalesIQUrl = (region: string): string => zohoSalesIQUrls.get(region) ?? 'https://salesiq.zoho.com' // Default to US if region not found

/**
* Extracts error message from various error response structures
* Handles different API error formats including:
* - response.data.message
* - response.data.error
* - response.data.error_description
* - response.data.error_message
* - response.data.errors (array)
* Falls back to axiosError.message if no response data is available
*/
const extractErrorMessage = (axiosError: AxiosError): string => {
const data = axiosError.response?.data as Record<string, unknown> | undefined

if (!data) {
return axiosError.message
}

// Check for common error message fields
if (typeof data.message === 'string' && data.message) {
return data.message
}

if (typeof data.error === 'string' && data.error) {
return data.error
}

if (typeof data.error_description === 'string' && data.error_description) {
return data.error_description
}

if (typeof data.error_message === 'string' && data.error_message) {
return data.error_message
}

// Handle error arrays
if (Array.isArray(data.errors) && data.errors.length > 0) {
const errorMessages: string[] = []

for (const errorItem of data.errors) {
if (typeof errorItem === 'string') {
errorMessages.push(errorItem)
} else if (typeof errorItem === 'object' && errorItem !== null) {
const errorObj = errorItem as Record<string, unknown>
// Check for the same error field names as at the top level
if (typeof errorObj.message === 'string' && errorObj.message) {
errorMessages.push(errorObj.message)
} else if (typeof errorObj.error === 'string' && errorObj.error) {
errorMessages.push(errorObj.error)
} else if (typeof errorObj.error_description === 'string' && errorObj.error_description) {
errorMessages.push(errorObj.error_description)
} else if (typeof errorObj.error_message === 'string' && errorObj.error_message) {
errorMessages.push(errorObj.error_message)
}
}
}

if (errorMessages.length > 0) {
return errorMessages.join('; ')
}
}

// If data exists but no recognizable error field, fall back to axiosError.message
// This ensures we don't lose the default error message from axios
return axiosError.message
}

export class ZohoApi {
private refreshToken: string
private clientId: string
Expand Down Expand Up @@ -135,7 +201,7 @@ export class ZohoApi {
}
} catch (error) {
if (axios.isAxiosError(error)) {
const axiosError = error as AxiosError<{ message?: string }>
const axiosError: AxiosError = error
logger.forBot().error(axiosError.response)

if (axiosError.response?.status === 401 || axiosError.response?.status === 400) {
Expand All @@ -145,7 +211,7 @@ export class ZohoApi {
return this.makeHitlRequest(endpoint, method, data, params)
}

const errorMessage = axiosError.response?.data?.message ?? axiosError.message
const errorMessage = extractErrorMessage(axiosError)
logger.forBot().error(`Error in ${method} ${endpoint}:`, axiosError.response?.data ?? axiosError.message)

return {
Expand Down Expand Up @@ -200,7 +266,8 @@ export class ZohoApi {
})
} catch (error) {
if (axios.isAxiosError(error)) {
logger.forBot().error('Error refreshing access token:', error.response?.data ?? error.message)
const errorMessage = extractErrorMessage(error)
logger.forBot().error('Error refreshing access token:', errorMessage)
} else {
logger
.forBot()
Expand Down
Loading