mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-17 01:56:46 +00:00
fix(core): Add retry mechanism to tools (#16667)
This commit is contained in:
@@ -31,6 +31,7 @@ export {
|
||||
jsonStringify,
|
||||
replaceCircularReferences,
|
||||
sleep,
|
||||
sleepWithAbort,
|
||||
fileTypeFromMimeType,
|
||||
assert,
|
||||
removeCircularRefs,
|
||||
|
||||
@@ -9,6 +9,7 @@ import merge from 'lodash/merge';
|
||||
|
||||
import { ALPHABET } from './constants';
|
||||
import { ApplicationError } from './errors/application.error';
|
||||
import { ExecutionCancelledError } from './errors/execution-cancelled.error';
|
||||
import type { BinaryFileType, IDisplayOptions, INodeProperties, JsonObject } from './interfaces';
|
||||
|
||||
const readStreamClasses = new Set(['ReadStream', 'Readable', 'ReadableStream']);
|
||||
@@ -199,6 +200,23 @@ export const sleep = async (ms: number): Promise<void> =>
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
|
||||
export const sleepWithAbort = async (ms: number, abortSignal?: AbortSignal): Promise<void> =>
|
||||
await new Promise((resolve, reject) => {
|
||||
if (abortSignal?.aborted) {
|
||||
reject(new ExecutionCancelledError(''));
|
||||
return;
|
||||
}
|
||||
|
||||
const timeout = setTimeout(resolve, ms);
|
||||
|
||||
const abortHandler = () => {
|
||||
clearTimeout(timeout);
|
||||
reject(new ExecutionCancelledError(''));
|
||||
};
|
||||
|
||||
abortSignal?.addEventListener('abort', abortHandler, { once: true });
|
||||
});
|
||||
|
||||
export function fileTypeFromMimeType(mimeType: string): BinaryFileType | undefined {
|
||||
if (mimeType.startsWith('application/json')) return 'json';
|
||||
if (mimeType.startsWith('text/html')) return 'html';
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ALPHABET } from '@/constants';
|
||||
import { ApplicationError } from '@/errors/application.error';
|
||||
import { ExecutionCancelledError } from '@/errors/execution-cancelled.error';
|
||||
import {
|
||||
jsonParse,
|
||||
jsonStringify,
|
||||
@@ -11,6 +12,7 @@ import {
|
||||
hasKey,
|
||||
isSafeObjectProperty,
|
||||
setSafeObjectProperty,
|
||||
sleepWithAbort,
|
||||
} from '@/utils';
|
||||
|
||||
describe('isObjectEmpty', () => {
|
||||
@@ -394,3 +396,68 @@ describe('setSafeObjectProperty', () => {
|
||||
expect(obj).toEqual(expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe('sleepWithAbort', () => {
|
||||
it('should resolve after the specified time when not aborted', async () => {
|
||||
const start = Date.now();
|
||||
await sleepWithAbort(100);
|
||||
const end = Date.now();
|
||||
const elapsed = end - start;
|
||||
|
||||
// Allow some tolerance for timing
|
||||
expect(elapsed).toBeGreaterThanOrEqual(90);
|
||||
expect(elapsed).toBeLessThan(200);
|
||||
});
|
||||
|
||||
it('should reject immediately if abort signal is already aborted', async () => {
|
||||
const abortController = new AbortController();
|
||||
abortController.abort();
|
||||
|
||||
await expect(sleepWithAbort(1000, abortController.signal)).rejects.toThrow(
|
||||
ExecutionCancelledError,
|
||||
);
|
||||
});
|
||||
|
||||
it('should reject when abort signal is triggered during sleep', async () => {
|
||||
const abortController = new AbortController();
|
||||
|
||||
// Start the sleep and abort after 50ms
|
||||
setTimeout(() => abortController.abort(), 50);
|
||||
|
||||
const start = Date.now();
|
||||
await expect(sleepWithAbort(1000, abortController.signal)).rejects.toThrow(
|
||||
ExecutionCancelledError,
|
||||
);
|
||||
const end = Date.now();
|
||||
const elapsed = end - start;
|
||||
|
||||
// Should have been aborted after ~50ms, not the full 1000ms
|
||||
expect(elapsed).toBeLessThan(200);
|
||||
});
|
||||
|
||||
it('should work without abort signal', async () => {
|
||||
const start = Date.now();
|
||||
await sleepWithAbort(100, undefined);
|
||||
const end = Date.now();
|
||||
const elapsed = end - start;
|
||||
|
||||
expect(elapsed).toBeGreaterThanOrEqual(90);
|
||||
expect(elapsed).toBeLessThan(200);
|
||||
});
|
||||
|
||||
it('should clean up timeout when aborted during sleep', async () => {
|
||||
const abortController = new AbortController();
|
||||
const clearTimeoutSpy = jest.spyOn(global, 'clearTimeout');
|
||||
|
||||
// Start the sleep and abort after 50ms
|
||||
const sleepPromise = sleepWithAbort(1000, abortController.signal);
|
||||
setTimeout(() => abortController.abort(), 50);
|
||||
|
||||
await expect(sleepPromise).rejects.toThrow(ExecutionCancelledError);
|
||||
|
||||
// clearTimeout should have been called to clean up
|
||||
expect(clearTimeoutSpy).toHaveBeenCalled();
|
||||
|
||||
clearTimeoutSpy.mockRestore();
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user