Skip to content

Commit a0ad53c

Browse files
robrichardyaacovCR
authored andcommitted
deduplicate non-leaf fields
# Conflicts: # src/execution/execute.ts
1 parent 7bd79f0 commit a0ad53c

File tree

4 files changed

+610
-266
lines changed

4 files changed

+610
-266
lines changed

src/execution/__tests__/defer-test.ts

Lines changed: 199 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ const deeperObject = new GraphQLObjectType({
5353
const nestedObject = new GraphQLObjectType({
5454
fields: {
5555
deeperObject: { type: deeperObject, resolve: () => ({}) },
56+
name: { type: GraphQLString, resolve: () => 'foo' },
5657
},
5758
name: 'NestedObject',
5859
});
@@ -129,13 +130,29 @@ const heroType = new GraphQLObjectType({
129130
type: new GraphQLList(friendType),
130131
resolve: () => friends,
131132
},
133+
emptyFriends: {
134+
type: new GraphQLList(friendType),
135+
resolve: () => [],
136+
},
132137
asyncFriends: {
133138
type: new GraphQLList(friendType),
134139
async *resolve() {
135140
yield await Promise.resolve(friends[0]);
136141
},
137142
},
143+
asyncEmptyFriends: {
144+
type: new GraphQLList(friendType),
145+
// eslint-disable-next-line require-yield
146+
async *resolve() {
147+
await resolveOnNextTick();
148+
},
149+
},
138150
nestedObject: { type: nestedObject, resolve: () => ({}) },
151+
promiseNestedObject: {
152+
type: nestedObject,
153+
resolve: () => Promise.resolve({}),
154+
},
155+
nullNestedObject: { type: nestedObject, resolve: () => null },
139156
anotherNestedObject: { type: anotherNestedObject, resolve: () => ({}) },
140157
},
141158
name: 'Hero',
@@ -395,26 +412,14 @@ describe('Execute: defer directive', () => {
395412
}
396413
`);
397414
const result = await complete(document);
398-
expectJSON(result).toDeepEqual([
399-
{
400-
data: {
401-
hero: {
402-
id: '1',
403-
name: 'Luke',
404-
},
415+
expectJSON(result).toDeepEqual({
416+
data: {
417+
hero: {
418+
id: '1',
419+
name: 'Luke',
405420
},
406-
hasNext: true,
407421
},
408-
{
409-
incremental: [
410-
{
411-
data: {},
412-
path: ['hero'],
413-
},
414-
],
415-
hasNext: false,
416-
},
417-
]);
422+
});
418423
});
419424
it('Can defer a fragment that is also not deferred, non-deferred fragment is first', async () => {
420425
const document = parse(`
@@ -430,26 +435,14 @@ describe('Execute: defer directive', () => {
430435
}
431436
`);
432437
const result = await complete(document);
433-
expectJSON(result).toDeepEqual([
434-
{
435-
data: {
436-
hero: {
437-
id: '1',
438-
name: 'Luke',
439-
},
438+
expectJSON(result).toDeepEqual({
439+
data: {
440+
hero: {
441+
id: '1',
442+
name: 'Luke',
440443
},
441-
hasNext: true,
442-
},
443-
{
444-
incremental: [
445-
{
446-
data: {},
447-
path: ['hero'],
448-
},
449-
],
450-
hasNext: false,
451444
},
452-
]);
445+
});
453446
});
454447

455448
it('Can defer an inline fragment', async () => {
@@ -578,9 +571,6 @@ describe('Execute: defer directive', () => {
578571
bar: 'bar',
579572
},
580573
},
581-
anotherNestedObject: {
582-
deeperObject: {},
583-
},
584574
},
585575
path: ['hero'],
586576
},
@@ -772,14 +762,6 @@ describe('Execute: defer directive', () => {
772762
},
773763
path: ['hero', 'nestedObject', 'deeperObject'],
774764
},
775-
{
776-
data: {
777-
nestedObject: {
778-
deeperObject: {},
779-
},
780-
},
781-
path: ['hero'],
782-
},
783765
],
784766
hasNext: false,
785767
},
@@ -860,6 +842,176 @@ describe('Execute: defer directive', () => {
860842
]);
861843
});
862844

845+
it('Can deduplicate list fields', async () => {
846+
const document = parse(`
847+
query {
848+
hero {
849+
friends {
850+
name
851+
}
852+
... @defer {
853+
friends {
854+
name
855+
}
856+
}
857+
}
858+
}
859+
`);
860+
const result = await complete(document);
861+
expectJSON(result).toDeepEqual({
862+
data: {
863+
hero: {
864+
friends: [{ name: 'Han' }, { name: 'Leia' }, { name: 'C-3PO' }],
865+
},
866+
},
867+
});
868+
});
869+
870+
it('Can deduplicate async iterable list fields', async () => {
871+
const document = parse(`
872+
query {
873+
hero {
874+
asyncFriends {
875+
name
876+
}
877+
... @defer {
878+
asyncFriends {
879+
name
880+
}
881+
}
882+
}
883+
}
884+
`);
885+
const result = await complete(document);
886+
expectJSON(result).toDeepEqual({
887+
data: { hero: { asyncFriends: [{ name: 'Han' }] } },
888+
});
889+
});
890+
891+
it('Can deduplicate empty async iterable list fields', async () => {
892+
const document = parse(`
893+
query {
894+
hero {
895+
asyncEmptyFriends {
896+
name
897+
}
898+
... @defer {
899+
asyncEmptyFriends {
900+
name
901+
}
902+
}
903+
}
904+
}
905+
`);
906+
const result = await complete(document);
907+
expectJSON(result).toDeepEqual({
908+
data: { hero: { asyncEmptyFriends: [] } },
909+
});
910+
});
911+
912+
it("Doesn't deduplicate list fields with non-overlapping fields", async () => {
913+
const document = parse(`
914+
query {
915+
hero {
916+
friends {
917+
name
918+
}
919+
... @defer {
920+
friends {
921+
name
922+
id
923+
}
924+
}
925+
}
926+
}
927+
`);
928+
const result = await complete(document);
929+
expectJSON(result).toDeepEqual([
930+
{
931+
data: {
932+
hero: {
933+
friends: [{ name: 'Han' }, { name: 'Leia' }, { name: 'C-3PO' }],
934+
},
935+
},
936+
hasNext: true,
937+
},
938+
{
939+
incremental: [
940+
{
941+
data: {
942+
friends: [{ id: '2' }, { id: '3' }, { id: '4' }],
943+
},
944+
path: ['hero'],
945+
},
946+
],
947+
hasNext: false,
948+
},
949+
]);
950+
});
951+
952+
it('Can deduplicate list fields that return empty lists', async () => {
953+
const document = parse(`
954+
query {
955+
hero {
956+
emptyFriends {
957+
name
958+
}
959+
... @defer {
960+
emptyFriends {
961+
name
962+
}
963+
}
964+
}
965+
}
966+
`);
967+
const result = await complete(document);
968+
expectJSON(result).toDeepEqual({
969+
data: { hero: { emptyFriends: [] } },
970+
});
971+
});
972+
973+
it('Can deduplicate null object fields', async () => {
974+
const document = parse(`
975+
query {
976+
hero {
977+
nullNestedObject {
978+
name
979+
}
980+
... @defer {
981+
nullNestedObject {
982+
name
983+
}
984+
}
985+
}
986+
}
987+
`);
988+
const result = await complete(document);
989+
expectJSON(result).toDeepEqual({
990+
data: { hero: { nullNestedObject: null } },
991+
});
992+
});
993+
994+
it('Can deduplicate promise object fields', async () => {
995+
const document = parse(`
996+
query {
997+
hero {
998+
promiseNestedObject {
999+
name
1000+
}
1001+
... @defer {
1002+
promiseNestedObject {
1003+
name
1004+
}
1005+
}
1006+
}
1007+
}
1008+
`);
1009+
const result = await complete(document);
1010+
expectJSON(result).toDeepEqual({
1011+
data: { hero: { promiseNestedObject: { name: 'foo' } } },
1012+
});
1013+
});
1014+
8631015
it('Handles errors thrown in deferred fragments', async () => {
8641016
const document = parse(`
8651017
query HeroNameQuery {

src/execution/__tests__/stream-test.ts

Lines changed: 0 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -527,11 +527,6 @@ describe('Execute: stream directive', () => {
527527
},
528528
],
529529
},
530-
],
531-
hasNext: true,
532-
},
533-
{
534-
incremental: [
535530
{
536531
items: [{ name: 'Leia', id: '3' }],
537532
path: ['friendList', 2],
@@ -980,11 +975,6 @@ describe('Execute: stream directive', () => {
980975
},
981976
],
982977
},
983-
],
984-
hasNext: true,
985-
},
986-
{
987-
incremental: [
988978
{
989979
items: [{ nonNullName: 'Han' }],
990980
path: ['friendList', 2],

0 commit comments

Comments
 (0)