Skip to content

Commit f0f27a9

Browse files
authored
Merge 00a285b into fed92f9
2 parents fed92f9 + 00a285b commit f0f27a9

7 files changed

Lines changed: 216 additions & 80 deletions

File tree

.changeset/violet-cougars-greet.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'graphiql': patch
3+
---
4+
5+
Fixes issue where with IncrementalDelivery directives objects wouldn't deep-merge.
Lines changed: 162 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -1,74 +1,170 @@
1-
const testStreamQuery = /* GraphQL */ `
2-
query StreamQuery($delay: Int) {
3-
streamable(delay: $delay) @stream(initialCount: 2) {
4-
text
5-
}
6-
}
7-
`;
1+
describe('IncrementalDelivery support via fetcher', () => {
2+
describe('When operation contains @stream', () => {
3+
const testStreamQuery = /* GraphQL */ `
4+
query StreamQuery($delay: Int) {
5+
streamable(delay: $delay) @stream(initialCount: 2) {
6+
text
7+
}
8+
}
9+
`;
810

9-
const mockStreamSuccess = {
10-
data: {
11-
streamable: [
12-
{
13-
text: 'Hi',
14-
},
15-
{
16-
text: '你好',
17-
},
18-
{
19-
text: 'Hola',
20-
},
21-
{
22-
text: 'أهلاً',
23-
},
24-
{
25-
text: 'Bonjour',
26-
},
27-
{
28-
text: 'سلام',
29-
},
30-
{
31-
text: '안녕',
32-
},
33-
{
34-
text: 'Ciao',
35-
},
36-
{
37-
text: 'हेलो',
38-
},
39-
{
40-
text: 'Здорово',
11+
const mockStreamSuccess = {
12+
data: {
13+
streamable: [
14+
{
15+
text: 'Hi',
16+
},
17+
{
18+
text: '你好',
19+
},
20+
{
21+
text: 'Hola',
22+
},
23+
{
24+
text: 'أهلاً',
25+
},
26+
{
27+
text: 'Bonjour',
28+
},
29+
{
30+
text: 'سلام',
31+
},
32+
{
33+
text: '안녕',
34+
},
35+
{
36+
text: 'Ciao',
37+
},
38+
{
39+
text: 'हेलो',
40+
},
41+
{
42+
text: 'Здорово',
43+
},
44+
],
4145
},
42-
],
43-
},
44-
hasNext: false,
45-
};
46+
hasNext: false,
47+
};
4648

47-
const testDeferQuery = /* GraphQL */ `
48-
query DeferQuery($delay: Int) {
49-
streamable(delay: $delay) @stream(initialCount: 2) {
50-
text
51-
}
52-
}
53-
`;
49+
it('Expects slower streams to resolve in several increments, and the payloads to patch properly', () => {
50+
const delay = 100;
51+
const timeout = mockStreamSuccess.data.streamable.length * (delay * 1.5);
5452

55-
describe('IncrementalDelivery support via fetcher', () => {
56-
it('Expects slower streams to resolve in several increments, and the payloads to patch properly', () => {
57-
const delay = 100;
58-
const timeout = mockStreamSuccess.data.streamable.length * (delay * 1.5);
53+
cy.visit(`/?query=${testStreamQuery}`);
54+
cy.assertQueryResult(
55+
{ query: testStreamQuery, variables: { delay } },
56+
mockStreamSuccess,
57+
timeout,
58+
);
59+
});
5960

60-
cy.visit(`/?query=${testStreamQuery}`);
61-
cy.assertQueryResult(
62-
{ query: testStreamQuery, variables: { delay } },
63-
mockStreamSuccess,
64-
timeout,
65-
);
61+
it('Expects a quick stream to resolve in a single increment', () => {
62+
cy.visit(`/?query=${testStreamQuery}`);
63+
cy.assertQueryResult(
64+
{ query: testStreamQuery, variables: { delay: 0 } },
65+
mockStreamSuccess,
66+
);
67+
});
6668
});
67-
it('Expects a quick stream to resolve in a single increment', () => {
68-
cy.visit(`/?query=${testStreamQuery}`);
69-
cy.assertQueryResult(
70-
{ query: testStreamQuery, variables: { delay: 0 } },
71-
mockStreamSuccess,
72-
);
69+
70+
describe('When operating with @defer', () => {
71+
it('Excepts to see a slow response but path properly', () => {
72+
const delay = 1000;
73+
const timeout = delay * 1.5;
74+
75+
const testQuery = /* GraphQL */ `
76+
query DeferQuery($delay: Int) {
77+
deferrable {
78+
normalString
79+
... @defer {
80+
deferredString(delay: $delay)
81+
}
82+
}
83+
}
84+
`;
85+
86+
cy.visit(`/?query=${testQuery}`);
87+
cy.assertQueryResult(
88+
{ query: testQuery, variables: { delay } },
89+
{
90+
data: {
91+
deferrable: {
92+
normalString: 'Nice',
93+
deferredString:
94+
'Oops, this took 1 seconds longer than I thought it would!',
95+
},
96+
},
97+
hasNext: false,
98+
},
99+
timeout,
100+
);
101+
});
102+
103+
it('Expects to merge types when members arrive at different times', () => {
104+
/*
105+
This tests that;
106+
1. user ({name}) => { name }
107+
2. user ({age}) => { name, age }
108+
3. user.friends.0 ({name}) => { name, age, friends: [{name}] } <- can sometimes happen before 4, due the the promise race
109+
4. user.friends.0 ({age}) => { name, age, friends: [{name, age}] }
110+
111+
This shows us that we can deep merge defers, deep merge streams, and also deep merge defers inside streams
112+
*/
113+
114+
const delay = 1000;
115+
const timeout = 4 /* friends */ * (delay * 1.5);
116+
117+
const testQuery = /* GraphQL */ `
118+
query DeferQuery($delay: Int) {
119+
person {
120+
name
121+
... @defer {
122+
age(delay: $delay)
123+
}
124+
friends @stream(initialCount: 0) {
125+
... @defer {
126+
name
127+
}
128+
... @defer {
129+
age(delay: $delay)
130+
}
131+
}
132+
}
133+
}
134+
`;
135+
136+
cy.visit(`/?query=${testQuery}`);
137+
cy.assertQueryResult(
138+
{ query: testQuery, variables: { delay } },
139+
{
140+
data: {
141+
person: {
142+
name: 'Mark',
143+
friends: [
144+
{
145+
name: 'James',
146+
age: 1000,
147+
},
148+
{
149+
name: 'Mary',
150+
age: 1000,
151+
},
152+
{
153+
name: 'John',
154+
age: 1000,
155+
},
156+
{
157+
name: 'Patrica',
158+
age: 1000,
159+
},
160+
],
161+
age: 1000,
162+
},
163+
},
164+
hasNext: false,
165+
},
166+
timeout,
167+
);
168+
});
73169
});
74170
});

packages/graphiql/cypress/support/commands.ts

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,8 @@ declare namespace Cypress {
1717
variablesString?: string;
1818
};
1919
type MockResult =
20-
| {
21-
data: any;
22-
}
20+
| { data: any }
21+
| { data: any; hasNext?: boolean }
2322
| { error: any[] };
2423
interface Chainable<Subject = any> {
2524
/**

packages/graphiql/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,10 +47,10 @@
4747
"codemirror-graphql": "^1.0.0",
4848
"copy-to-clipboard": "^3.2.0",
4949
"entities": "^2.0.0",
50+
"dset": "^3.1.0",
5051
"graphql-language-service": "^3.1.2",
5152
"markdown-it": "^10.0.0",
52-
"@graphiql/toolkit": "^0.1.0",
53-
"dset": "^3.0.0"
53+
"@graphiql/toolkit": "^0.1.0"
5454
},
5555
"peerDependencies": {
5656
"graphql": "^14.0.0 || ^15.0.0",

packages/graphiql/src/components/GraphiQL.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,6 @@ import {
2626
} from 'graphql';
2727
import copyToClipboard from 'copy-to-clipboard';
2828
import { getFragmentDependenciesForAST } from 'graphql-language-service-utils';
29-
import { dset } from 'dset';
3029

3130
import { ExecuteButton } from './ExecuteButton';
3231
import { ImagePreview } from './ImagePreview';
@@ -53,6 +52,7 @@ import {
5352
introspectionQueryName,
5453
introspectionQuerySansSubscriptions,
5554
} from '../utility/introspectionQueries';
55+
import { dset } from 'dset/merge';
5656

5757
import type {
5858
Fetcher,
@@ -1113,7 +1113,8 @@ export class GraphiQL extends React.Component<GraphiQLProps, GraphiQLState> {
11131113
`Expected part to contain a data property, but got ${part}`,
11141114
);
11151115
}
1116-
dset(payload.data, part.path, part.data);
1116+
1117+
dset(payload.data, path, data);
11171118
} else if (data) {
11181119
// If there is no path, we don't know what to do with the payload,
11191120
// so we just set it.

packages/graphiql/test/schema.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,37 @@ const DeferrableObject = new GraphQLObjectType({
165165
},
166166
});
167167

168+
const Person = new GraphQLObjectType({
169+
name: 'Person',
170+
fields: () => ({
171+
name: {
172+
type: GraphQLString,
173+
resolve: obj => obj.name,
174+
},
175+
age: {
176+
args: {
177+
delay: delayArgument(600),
178+
},
179+
type: GraphQLInt,
180+
resolve: async function lazilyReturnValue(_value, args) {
181+
const seconds = args.delay / 1000;
182+
await sleep(args.delay);
183+
return Math.ceil(args.delay);
184+
},
185+
},
186+
friends: {
187+
type: new GraphQLList(Person),
188+
async *resolve(_value, args) {
189+
const names = ['James', 'Mary', 'John', 'Patrica']; // Top 4 names https://www.ssa.gov/oact/babynames/decades/century.html
190+
for (const name of names) {
191+
await sleep(100);
192+
yield { name };
193+
}
194+
},
195+
},
196+
}),
197+
});
198+
168199
const sleep = async timeout => new Promise(res => setTimeout(res, timeout));
169200

170201
const TestType = new GraphQLObjectType({
@@ -206,6 +237,10 @@ const TestType = new GraphQLObjectType({
206237
}
207238
},
208239
},
240+
person: {
241+
type: Person,
242+
resolve: () => ({ name: 'Mark' }),
243+
},
209244
longDescriptionType: {
210245
type: TestType,
211246
description:

yarn.lock

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9031,10 +9031,10 @@ dotenv@^6.2.0:
90319031
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-6.2.0.tgz#941c0410535d942c8becf28d3f357dbd9d476064"
90329032
integrity sha512-HygQCKUBSFl8wKQZBSemMywRWcEDNidvNbjGVyZu3nbZ8qq9ubiPoGLMdRDpfSrpkkm9BXYFkpKxxFX38o/76w==
90339033

9034-
dset@^3.0.0:
9035-
version "3.0.0"
9036-
resolved "https://registry.yarnpkg.com/dset/-/dset-3.0.0.tgz#b49ef4a6a092c2c5328618eca2ccf6885fafb431"
9037-
integrity sha512-pp0B9VgLwMem6bfSDJujcXa41swmXkhWICL1jwC7WbD/NaxXPCXO0Z1sOrVshIQaD4D/pi5lDS7NCt6qIytWaA==
9034+
dset@^3.1.0:
9035+
version "3.1.0"
9036+
resolved "https://registry.yarnpkg.com/dset/-/dset-3.1.0.tgz#23feb6df93816ea452566308b1374d6e869b0d7b"
9037+
integrity sha512-7xTQ5DzyE59Nn+7ZgXDXjKAGSGmXZHqttMVVz1r4QNfmGpyj+cm2YtI3II0c/+4zS4a9yq2mBhgdeq2QnpcYlw==
90389038

90399039
duplexer2@~0.1.4:
90409040
version "0.1.4"
@@ -10990,13 +10990,13 @@ grapheme-splitter@^1.0.4:
1099010990
integrity sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==
1099110991

1099210992
"graphiql@file:packages/graphiql":
10993-
version "1.4.0-rc.1"
10993+
version "1.4.0"
1099410994
dependencies:
1099510995
"@graphiql/toolkit" "^0.1.0"
1099610996
codemirror "^5.54.0"
10997-
codemirror-graphql "^0.15.2"
10997+
codemirror-graphql "^1.0.0"
1099810998
copy-to-clipboard "^3.2.0"
10999-
dset "^3.0.0"
10999+
dset "^3.1.0"
1100011000
entities "^2.0.0"
1100111001
graphql-language-service "^3.1.2"
1100211002
markdown-it "^10.0.0"

0 commit comments

Comments
 (0)