Skip to content

Commit be3411c

Browse files
authored
merge extensions in incremental result merging (#5398)
* merge extensions in incremental result merging * add changeset * add tests + fix some bugs
1 parent 03b14e6 commit be3411c

File tree

3 files changed

+205
-14
lines changed

3 files changed

+205
-14
lines changed

.changeset/seven-boats-push.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@graphql-tools/utils': patch
3+
---
4+
5+
incremental merge also merges extensions

packages/utils/src/mergeIncrementalResult.ts

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,25 +9,29 @@ export function mergeIncrementalResult({
99
incrementalResult: ExecutionResult;
1010
executionResult: ExecutionResult;
1111
}) {
12-
if (incrementalResult.path) {
13-
const path = ['data', ...incrementalResult.path];
14-
executionResult.data = executionResult.data || {};
15-
if (incrementalResult.items) {
16-
for (const item of incrementalResult.items) {
17-
dset(executionResult, path, item);
18-
}
19-
}
20-
if (incrementalResult.data) {
21-
dset(executionResult, ['data', ...incrementalResult.path], incrementalResult.data);
12+
const path = ['data', ...(incrementalResult.path ?? [])];
13+
14+
if (incrementalResult.items) {
15+
for (const item of incrementalResult.items) {
16+
dset(executionResult, path, item);
17+
// Increment the last path segment (the array index) to merge the next item at the next index
18+
(path[path.length - 1] as number)++;
2219
}
23-
} else if (incrementalResult.data) {
24-
executionResult.data = executionResult.data || {};
25-
Object.assign(executionResult.data, incrementalResult.data);
2620
}
21+
22+
if (incrementalResult.data) {
23+
dset(executionResult, path, incrementalResult.data);
24+
}
25+
2726
if (incrementalResult.errors) {
2827
executionResult.errors = executionResult.errors || [];
29-
(executionResult.errors as GraphQLError[]).push(...executionResult.errors);
28+
(executionResult.errors as GraphQLError[]).push(...incrementalResult.errors);
29+
}
30+
31+
if (incrementalResult.extensions) {
32+
dset(executionResult, 'extensions', incrementalResult.extensions);
3033
}
34+
3135
if (incrementalResult.incremental) {
3236
incrementalResult.incremental.forEach(incrementalSubResult => {
3337
mergeIncrementalResult({
Lines changed: 182 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,182 @@
1+
import { GraphQLError } from 'graphql';
2+
import { mergeIncrementalResult } from '../src/mergeIncrementalResult';
3+
4+
describe('mergeIncrementalResult', () => {
5+
it('should merge data without path', () => {
6+
const executionResult = { data: { user: { name: 'John' } } };
7+
const incrementalResult = { data: { user: { age: 42 } } };
8+
9+
mergeIncrementalResult({ incrementalResult, executionResult });
10+
11+
expect(executionResult).toEqual({ data: { user: { age: 42, name: 'John' } } });
12+
});
13+
14+
it('should deep merge data with basic path', () => {
15+
const executionResult = { data: { user: { name: 'John' } } };
16+
const incrementalResult = { path: [], data: { user: { age: 42 } } };
17+
18+
mergeIncrementalResult({ incrementalResult, executionResult });
19+
20+
expect(executionResult).toEqual({ data: { user: { age: 42, name: 'John' } } });
21+
});
22+
23+
it('should merge data at path', () => {
24+
const executionResult = { data: { user: { name: 'John' } } };
25+
const incrementalResult = { path: ['user'], data: { age: 42 } };
26+
27+
mergeIncrementalResult({ incrementalResult, executionResult });
28+
29+
expect(executionResult).toEqual({ data: { user: { age: 42, name: 'John' } } });
30+
});
31+
32+
it('should push items', () => {
33+
const executionResult = { data: { user: { name: 'John' } } };
34+
const incrementalResult = {
35+
path: ['user', 'comments', 0],
36+
items: ['comment 1', 'comment 2'],
37+
};
38+
39+
mergeIncrementalResult({ incrementalResult, executionResult });
40+
41+
expect(executionResult).toEqual({
42+
data: {
43+
user: {
44+
name: 'John',
45+
comments: ['comment 1', 'comment 2'],
46+
},
47+
},
48+
});
49+
});
50+
51+
it('should push items at path', () => {
52+
const executionResult = { data: { user: { name: 'John', comments: ['comment 1', 'comment 2'] } } };
53+
const incrementalResult = {
54+
path: ['user', 'comments', 2],
55+
items: ['comment 3', 'comment 4'],
56+
};
57+
58+
mergeIncrementalResult({ incrementalResult, executionResult });
59+
60+
expect(executionResult).toEqual({
61+
data: {
62+
user: {
63+
name: 'John',
64+
comments: ['comment 1', 'comment 2', 'comment 3', 'comment 4'],
65+
},
66+
},
67+
});
68+
});
69+
70+
it('should merge items at path', () => {
71+
const executionResult = {
72+
data: {
73+
user: {
74+
name: 'John',
75+
comments: [{ id: 1 }, { id: 2 }],
76+
},
77+
},
78+
};
79+
80+
const incrementalResult = {
81+
path: ['user', 'comments', 0],
82+
items: [{ text: 'comment 1' }, { text: 'comment 2' }],
83+
};
84+
85+
mergeIncrementalResult({ incrementalResult, executionResult });
86+
87+
expect(executionResult).toEqual({
88+
data: {
89+
user: {
90+
name: 'John',
91+
comments: [
92+
{ id: 1, text: 'comment 1' },
93+
{ id: 2, text: 'comment 2' },
94+
],
95+
},
96+
},
97+
});
98+
});
99+
100+
it('should add errors', () => {
101+
const executionResult = { data: { user: { name: 'John' } } };
102+
const incrementalResult = { errors: [new GraphQLError('error 1'), new GraphQLError('error 2')] };
103+
104+
mergeIncrementalResult({ incrementalResult, executionResult });
105+
106+
expect(executionResult).toEqual({
107+
data: { user: { name: 'John' } },
108+
errors: [new GraphQLError('error 1'), new GraphQLError('error 2')],
109+
});
110+
});
111+
112+
it('should keep errors', () => {
113+
const executionResult = { errors: [new GraphQLError('error 1')] };
114+
const incrementalResult = { data: { user: { name: 'John' } }, path: [] };
115+
116+
mergeIncrementalResult({ incrementalResult, executionResult });
117+
118+
expect(executionResult).toEqual({
119+
data: { user: { name: 'John' } },
120+
errors: [new GraphQLError('error 1')],
121+
});
122+
});
123+
124+
it('should merge errors', () => {
125+
const executionResult = { errors: [new GraphQLError('error 1')] };
126+
127+
const incrementalResult = { errors: [new GraphQLError('error 2'), new GraphQLError('error 3')] };
128+
129+
mergeIncrementalResult({ incrementalResult, executionResult });
130+
131+
expect(executionResult).toEqual({
132+
errors: [new GraphQLError('error 1'), new GraphQLError('error 2'), new GraphQLError('error 3')],
133+
});
134+
});
135+
136+
it('should keep extensions', () => {
137+
const exeuctionResult = { data: { user: { name: 'John' } }, extensions: { foo: 'bar' } };
138+
const incrementalResult = { data: { user: { age: 42 } }, path: [] };
139+
140+
mergeIncrementalResult({ incrementalResult, executionResult: exeuctionResult });
141+
142+
expect(exeuctionResult).toEqual({
143+
data: { user: { age: 42, name: 'John' } },
144+
extensions: { foo: 'bar' },
145+
});
146+
});
147+
148+
it('should add extensions', () => {
149+
const exeuctionResult = { data: { user: { name: 'John' } } };
150+
const incrementalResult = { data: { user: { age: 42 } }, path: [], extensions: { ext1: 'ext1' } };
151+
152+
mergeIncrementalResult({ incrementalResult, executionResult: exeuctionResult });
153+
154+
expect(exeuctionResult).toEqual({
155+
data: { user: { age: 42, name: 'John' } },
156+
extensions: { ext1: 'ext1' },
157+
});
158+
});
159+
160+
it('should merge extensions', () => {
161+
const exeuctionResult = { data: { user: { name: 'John' } }, extensions: { ext1: { a: 'a' } } };
162+
const incrementalResult = { data: { user: { age: 42 } }, path: [], extensions: { ext1: { b: 'b' }, ext2: 'ext2' } };
163+
164+
mergeIncrementalResult({ incrementalResult, executionResult: exeuctionResult });
165+
166+
expect(exeuctionResult).toEqual({
167+
data: { user: { age: 42, name: 'John' } },
168+
extensions: { ext1: { a: 'a', b: 'b' }, ext2: 'ext2' },
169+
});
170+
});
171+
172+
it('should let incremental result override previous extensions', () => {
173+
const executionResult = { extensions: { ext1: { a: 'a' } } };
174+
const incrementalResult = { extensions: { ext1: { a: 'b' } } };
175+
176+
mergeIncrementalResult({ incrementalResult, executionResult });
177+
178+
expect(executionResult).toEqual({
179+
extensions: { ext1: { a: 'b' } },
180+
});
181+
});
182+
});

0 commit comments

Comments
 (0)