diff --git a/src/model/ApiError.ts b/src/model/ApiError.ts index 47e541bd2..8b547985a 100644 --- a/src/model/ApiError.ts +++ b/src/model/ApiError.ts @@ -31,19 +31,21 @@ export class ApiError { request: Request constructor(axiosError) { + const response = axiosError.response || {}; + const request = axiosError.request || {}; - this.statusCode = axiosError.response.status; - this.body = axiosError.response.data; - this.headers = axiosError.response.headers; + this.statusCode = response.status || 0; + this.body = response.data ?? axiosError.message; + this.headers = response.headers || {}; this.request = { url: { - protocol: axiosError.request.protocol, - port: axiosError.request.agent?.defaultPort || axiosError.request.socket?.localPort, - host: axiosError.request.host, - path: axiosError.request.path, + protocol: request.protocol, + port: request.agent?.defaultPort || request.socket?.localPort, + host: request.host, + path: request.path, }, - headers: axiosError.request.getHeaders(), - method: axiosError.request.method + headers: typeof request.getHeaders === 'function' ? request.getHeaders() : {}, + method: request.method } } diff --git a/src/test/apiError.spec.ts b/src/test/apiError.spec.ts new file mode 100644 index 000000000..2e1ff9f98 --- /dev/null +++ b/src/test/apiError.spec.ts @@ -0,0 +1,91 @@ +import { ApiError } from '../model/ApiError'; + +describe('ApiError', () => { + it('handles axios errors without a response', () => { + const apiError = new ApiError({ + message: 'Network Error', + request: { + protocol: 'https:', + agent: { + defaultPort: 443, + }, + host: 'api.xero.com', + path: '/api.xro/2.0/Invoices', + getHeaders: () => ({ + authorization: 'Bearer token', + }), + method: 'GET', + }, + }); + + expect(apiError.generateError()).toEqual({ + response: { + statusCode: 0, + body: 'Network Error', + headers: {}, + request: { + url: { + protocol: 'https:', + port: 443, + host: 'api.xero.com', + path: '/api.xro/2.0/Invoices', + }, + headers: { + authorization: 'Bearer token', + }, + method: 'GET', + }, + }, + body: 'Network Error', + }); + }); + + it('preserves axios response details when available', () => { + const apiError = new ApiError({ + response: { + status: 401, + data: { + error: 'invalid_token', + }, + headers: { + 'content-type': 'application/json', + }, + }, + request: { + protocol: 'https:', + socket: { + localPort: 443, + }, + host: 'api.xero.com', + path: '/api.xro/2.0/Invoices', + getHeaders: () => ({}), + method: 'GET', + }, + }); + + expect(apiError.generateError()).toEqual({ + response: { + statusCode: 401, + body: { + error: 'invalid_token', + }, + headers: { + 'content-type': 'application/json', + }, + request: { + url: { + protocol: 'https:', + port: 443, + host: 'api.xero.com', + path: '/api.xro/2.0/Invoices', + }, + headers: {}, + method: 'GET', + }, + }, + body: { + error: 'invalid_token', + }, + }); + }); +});