Skip to content

Commit 72f568e

Browse files
bscherleinglasser
andauthored
[fix] whenResultIsFinished works on Promise of Array of Promises (#7843)
Paired with @bbeesley. Updates whenResultIsFinished to call callback also on Promise of Array of Promises. Fixes (partially) #5372 and #4667. The test case we're fixing is `'passes result of Promise of Array of Promises to the callback'`, the other tests test existing behaviour. The existing bug prevented us from dynamically setting cache hints in the reference resolver depending on the result of an asynchronous operation. We discovered the bug within an integration test. If you @glasser have an idea how to add an integration test and would like to add it, that would be amazing! --------- Co-authored-by: David Glasser <[email protected]>
1 parent 5704146 commit 72f568e

File tree

3 files changed

+45
-2
lines changed

3 files changed

+45
-2
lines changed

.changeset/long-crabs-reply.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@apollo/server": patch
3+
---
4+
5+
Improves timing of the `willResolveField` end hook on fields which return Promises resolving to Arrays. This makes the use of the `setCacheHint` method more reliable.
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { describe, expect, it, jest } from '@jest/globals';
2+
import { whenResultIsFinished } from '../../utils/schemaInstrumentation';
3+
4+
describe('whenResultIsFinished', () => {
5+
it('passes result of Promise to the callback', async () => {
6+
const expected = 1;
7+
const result = Promise.resolve(expected);
8+
const callback = jest.fn();
9+
whenResultIsFinished(result, callback);
10+
await new Promise((r) => setImmediate(r));
11+
expect(callback).toBeCalledWith(null, expected);
12+
});
13+
it('passes result of Array of Promises to the callback', async () => {
14+
const expected = 1;
15+
const result = [Promise.resolve(expected)];
16+
const callback = jest.fn();
17+
whenResultIsFinished(result, callback);
18+
await new Promise((r) => setImmediate(r));
19+
expect(callback).toBeCalledWith(null, [expected]);
20+
});
21+
it('passes result which is not asynchronous directly to the callback', async () => {
22+
const expected = 1;
23+
const result = expected;
24+
const callback = jest.fn();
25+
whenResultIsFinished(result, callback);
26+
await new Promise((r) => setImmediate(r));
27+
expect(callback).toBeCalledWith(null, expected);
28+
});
29+
it('passes result of Promise of Array of Promises to the callback', async () => {
30+
const expected = 1;
31+
const result = Promise.resolve([Promise.resolve(expected)]);
32+
const callback = jest.fn();
33+
whenResultIsFinished(result, callback);
34+
await new Promise((r) => setImmediate(r));
35+
expect(callback).toBeCalledWith(null, [expected]);
36+
});
37+
});

packages/server/src/utils/schemaInstrumentation.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,14 +106,15 @@ function isPromise(x: any): boolean {
106106

107107
// Given result (which may be a Promise or an array some of whose elements are
108108
// promises) Promises, set up 'callback' to be invoked when result is fully
109-
// resolved.
109+
// resolved. (Unfortunately, this does not perfectly handle every possible
110+
// return value shape, such as arrays of arrays of Promises.)
110111
export function whenResultIsFinished(
111112
result: any,
112113
callback: (err: Error | null, result?: any) => void,
113114
) {
114115
if (isPromise(result)) {
115116
result.then(
116-
(r: any) => callback(null, r),
117+
(r: any) => whenResultIsFinished(r, callback),
117118
(err: Error) => callback(err),
118119
);
119120
} else if (Array.isArray(result)) {

0 commit comments

Comments
 (0)