Skip to content

Commit 3f3d636

Browse files
committed
fix: Let ParallelHandler only run able handlers
1 parent 063e3c9 commit 3f3d636

5 files changed

+47
-16
lines changed

src/HandlerUtil.ts

+15
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,18 @@ Promise<AsyncHandler<TIn, TOut>[]> {
5252

5353
throw new AggregateError(errors, 'No handler can handle the input');
5454
}
55+
56+
/**
57+
* Returns the value of the `handle` call if the handle can handle input.
58+
*
59+
* @param handler - Handler to run the input.
60+
* @param input - Input to handle.
61+
*/
62+
export async function handleIfAble<TIn, TOut>(handler: AsyncHandler<TIn, TOut>, input: TIn): Promise<TOut | void> {
63+
try {
64+
await handler.canHandle(input);
65+
} catch {
66+
return;
67+
}
68+
return handler.handle(input);
69+
}

src/ParallelHandler.ts

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
import { AsyncHandler } from './AsyncHandler';
2+
import { handleIfAble } from './HandlerUtil';
23

34
/**
45
* A composite handler that executes handlers in parallel.
6+
* Handlers that can not handler the input will be ignored.
57
* The `canHandle` check of this handler will always succeed.
68
*/
79
export class ParallelHandler<TIn = void> extends AsyncHandler<TIn> {
@@ -14,7 +16,7 @@ export class ParallelHandler<TIn = void> extends AsyncHandler<TIn> {
1416

1517
public async handle(input: TIn): Promise<void> {
1618
await Promise.all(
17-
this.handlers.map(async(handler): Promise<unknown> => handler.handleSafe(input)),
19+
this.handlers.map(async(handler): Promise<unknown> => handleIfAble(handler, input)),
1820
);
1921
}
2022
}

src/SequenceHandler.ts

+2-4
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,11 @@ export class SequenceHandler<TIn = void, TOut = void> extends AsyncHandler<TIn,
1616
public async handle(input: TIn): Promise<TOut | undefined> {
1717
let result: TOut | undefined;
1818
for (const handler of this.handlers) {
19-
let supported: boolean;
19+
let supported = false;
2020
try {
2121
await handler.canHandle(input);
2222
supported = true;
23-
} catch {
24-
supported = false;
25-
}
23+
} catch {}
2624
if (supported) {
2725
result = await handler.handle(input);
2826
}

test/unit/HandlerUtil.test.ts

+12-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import type { AsyncHandler } from '../../src/AsyncHandler';
2-
import { filterHandlers, findHandler } from '../../src/HandlerUtil';
2+
import { filterHandlers, findHandler, handleIfAble } from '../../src/HandlerUtil';
33
import { getError } from '../TestUtil';
44

55
describe('HandlerUtil', (): void => {
@@ -9,6 +9,7 @@ describe('HandlerUtil', (): void => {
99
beforeEach(async(): Promise<void> => {
1010
handlerTrue = {
1111
canHandle: jest.fn(),
12+
handle: jest.fn().mockResolvedValue(true),
1213
} satisfies Partial<AsyncHandler<any, any>> as any;
1314
handlerFalse = {
1415
canHandle: jest.fn().mockRejectedValue(new Error('test')),
@@ -45,4 +46,14 @@ describe('HandlerUtil', (): void => {
4546
expect(error.errors[1].message).toBe('test');
4647
});
4748
});
49+
50+
describe('#handleIfAble', (): void => {
51+
it('returns undefined if the handler cannot handle the input.', async(): Promise<void> => {
52+
await expect(handleIfAble(handlerFalse, 'a')).resolves.toBeUndefined();
53+
});
54+
55+
it('returns the value if the handler can handle the input.', async(): Promise<void> => {
56+
await expect(handleIfAble(handlerTrue, 'a')).resolves.toBe(true);
57+
});
58+
});
4859
});

test/unit/ParallelHandler.test.ts

+15-10
Original file line numberDiff line numberDiff line change
@@ -8,33 +8,38 @@ describe('ParallelHandler', (): void => {
88
beforeEach(async(): Promise<void> => {
99
handlers = [
1010
{
11-
handleSafe: jest.fn().mockResolvedValue('0'),
11+
canHandle: jest.fn(),
12+
handle: jest.fn().mockResolvedValue('0'),
1213
} satisfies Partial<AsyncHandler<string, string>> as any,
1314
{
14-
handleSafe: jest.fn().mockResolvedValue('1'),
15+
canHandle: jest.fn(),
16+
handle: jest.fn().mockResolvedValue('1'),
1517
} satisfies Partial<AsyncHandler<string, string>> as any,
1618
{
17-
handleSafe: jest.fn().mockResolvedValue('2'),
19+
canHandle: jest.fn(),
20+
handle: jest.fn().mockResolvedValue('2'),
1821
} satisfies Partial<AsyncHandler<string, string>> as any,
1922
];
2023

2124
parallel = new ParallelHandler(handlers);
2225
});
2326

2427
it('can handle all requests.', async(): Promise<void> => {
25-
handlers[0].handleSafe.mockRejectedValueOnce(new Error('error'));
28+
handlers[0].canHandle.mockRejectedValueOnce(new Error('error'));
29+
handlers[1].handle.mockRejectedValueOnce(new Error('error'));
2630
await expect(parallel.canHandle('input')).resolves.toBeUndefined();
2731
});
2832

2933
it('runs all handlers that can handle the input.', async(): Promise<void> => {
34+
handlers[0].canHandle.mockRejectedValueOnce(new Error('error'));
3035
await expect(parallel.handle('abc')).resolves.toBeUndefined();
3136

32-
expect(handlers[0].handleSafe).toHaveBeenCalledTimes(1);
33-
expect(handlers[1].handleSafe).toHaveBeenCalledTimes(1);
34-
expect(handlers[2].handleSafe).toHaveBeenCalledTimes(1);
37+
expect(handlers[0].canHandle).toHaveBeenLastCalledWith('abc');
38+
expect(handlers[1].canHandle).toHaveBeenLastCalledWith('abc');
39+
expect(handlers[2].canHandle).toHaveBeenLastCalledWith('abc');
3540

36-
expect(handlers[0].handleSafe).toHaveBeenCalledWith('abc');
37-
expect(handlers[1].handleSafe).toHaveBeenCalledWith('abc');
38-
expect(handlers[2].handleSafe).toHaveBeenCalledWith('abc');
41+
expect(handlers[0].handle).toHaveBeenCalledTimes(0);
42+
expect(handlers[1].handle).toHaveBeenLastCalledWith('abc');
43+
expect(handlers[2].handle).toHaveBeenLastCalledWith('abc');
3944
});
4045
});

0 commit comments

Comments
 (0)