Skip to content

Commit

Permalink
fix: respect timeout override when provided in request options (#617)
Browse files Browse the repository at this point in the history
* fix: respect timeout override when provided in request options

* Ran prettier
  • Loading branch information
AaronDDM authored Jan 22, 2025
1 parent 50be249 commit a68e4ef
Show file tree
Hide file tree
Showing 3 changed files with 71 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## Unreleased
* Remove `createdAt` field from message model.
* Change `latestMessageReceivedDate` & `latestMessageSentDate` to be optional on threads model.
* Fix issue where timeout was not being respected when overriding the timeout in the request options.
* Fix issue where query params with array values were not being transformed into comma-delimited strings

### 7.7.2 / 2024-12-02
Expand Down
8 changes: 6 additions & 2 deletions src/apiClient.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import fetch, { Request, Response } from 'node-fetch';
import { AbortSignal } from 'node-fetch/externals.js';
import { NylasConfig, OverridableNylasConfig } from './config.js';
import {
NylasApiError,
Expand Down Expand Up @@ -146,12 +147,15 @@ export default class APIClient {
private async sendRequest(options: RequestOptionsParams): Promise<Response> {
const req = this.newRequest(options);
const controller: AbortController = new AbortController();
const timeoutDuration = options.overrides?.timeout || this.timeout;
const timeout = setTimeout(() => {
controller.abort();
}, this.timeout);
}, timeoutDuration);

try {
const response = await fetch(req, { signal: controller.signal });
const response = await fetch(req, {
signal: controller.signal as AbortSignal,
});
clearTimeout(timeout);

if (typeof response === 'undefined') {
Expand Down
65 changes: 64 additions & 1 deletion tests/apiClient.spec.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { RequestInfo } from 'node-fetch';
import APIClient, { RequestOptionsParams } from '../src/apiClient';
import { NylasApiError, NylasOAuthError } from '../src/models/error';
import {
NylasApiError,
NylasOAuthError,
NylasSdkTimeoutError,
} from '../src/models/error';
import { SDK_VERSION } from '../src/version';
import { mockedFetch, mockResponse } from './testUtils';

Expand Down Expand Up @@ -309,6 +314,64 @@ describe('APIClient', () => {
})
).rejects.toThrow(new NylasApiError(payload));
});

it('should respect override timeout when provided', async () => {
const overrideTimeout = 2; // 2 second timeout

mockedFetch.mockImplementationOnce((_url: RequestInfo) => {
// Immediately throw an AbortError to simulate a timeout
const error = new Error('The operation was aborted');
error.name = 'AbortError';
return Promise.reject(error);
});

await expect(
client.request({
path: '/test',
method: 'GET',
overrides: { timeout: overrideTimeout },
})
).rejects.toThrow(
new NylasSdkTimeoutError(
'https://api.us.nylas.com/test',
overrideTimeout * 1000
)
);
});

it('should use default timeout when no override provided', async () => {
mockedFetch.mockImplementationOnce((_url: RequestInfo) => {
// Immediately throw an AbortError to simulate a timeout
const error = new Error('The operation was aborted');
error.name = 'AbortError';
return Promise.reject(error);
});

await expect(
client.request({
path: '/test',
method: 'GET',
})
).rejects.toThrow(
new NylasSdkTimeoutError(
'https://api.us.nylas.com/test',
client.timeout
)
);
});

it('should complete request within timeout period', async () => {
const payload = { data: 'test' };
mockedFetch.mockImplementationOnce(() =>
Promise.resolve(mockResponse(JSON.stringify(payload)))
);

const result = await client.request({
path: '/test',
method: 'GET',
});
expect(result).toEqual(payload);
});
});
});
});

0 comments on commit a68e4ef

Please sign in to comment.