@apollo/gateway 0.300.0-alpha.2 → 2.0.0-alpha.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (198) hide show
  1. package/LICENSE +95 -0
  2. package/README.md +1 -1
  3. package/dist/__generated__/graphqlTypes.d.ts +130 -0
  4. package/dist/__generated__/graphqlTypes.d.ts.map +1 -0
  5. package/dist/__generated__/graphqlTypes.js +25 -0
  6. package/dist/__generated__/graphqlTypes.js.map +1 -0
  7. package/dist/config.d.ts +104 -0
  8. package/dist/config.d.ts.map +1 -0
  9. package/dist/config.js +47 -0
  10. package/dist/config.js.map +1 -0
  11. package/dist/datasources/LocalGraphQLDataSource.d.ts +3 -3
  12. package/dist/datasources/LocalGraphQLDataSource.d.ts.map +1 -1
  13. package/dist/datasources/LocalGraphQLDataSource.js +5 -5
  14. package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
  15. package/dist/datasources/RemoteGraphQLDataSource.d.ts +6 -4
  16. package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
  17. package/dist/datasources/RemoteGraphQLDataSource.js +60 -17
  18. package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
  19. package/dist/datasources/index.d.ts +1 -1
  20. package/dist/datasources/index.d.ts.map +1 -1
  21. package/dist/datasources/index.js +1 -0
  22. package/dist/datasources/index.js.map +1 -1
  23. package/dist/datasources/parseCacheControlHeader.d.ts +2 -0
  24. package/dist/datasources/parseCacheControlHeader.d.ts.map +1 -0
  25. package/dist/datasources/parseCacheControlHeader.js +16 -0
  26. package/dist/datasources/parseCacheControlHeader.js.map +1 -0
  27. package/dist/datasources/types.d.ts +16 -1
  28. package/dist/datasources/types.d.ts.map +1 -1
  29. package/dist/datasources/types.js +7 -0
  30. package/dist/datasources/types.js.map +1 -1
  31. package/dist/executeQueryPlan.d.ts +2 -1
  32. package/dist/executeQueryPlan.d.ts.map +1 -1
  33. package/dist/executeQueryPlan.js +199 -112
  34. package/dist/executeQueryPlan.js.map +1 -1
  35. package/dist/index.d.ts +62 -80
  36. package/dist/index.d.ts.map +1 -1
  37. package/dist/index.js +543 -234
  38. package/dist/index.js.map +1 -1
  39. package/dist/loadServicesFromRemoteEndpoint.d.ts +9 -9
  40. package/dist/loadServicesFromRemoteEndpoint.d.ts.map +1 -1
  41. package/dist/loadServicesFromRemoteEndpoint.js +13 -8
  42. package/dist/loadServicesFromRemoteEndpoint.js.map +1 -1
  43. package/dist/loadSupergraphSdlFromStorage.d.ts +13 -0
  44. package/dist/loadSupergraphSdlFromStorage.d.ts.map +1 -0
  45. package/dist/loadSupergraphSdlFromStorage.js +101 -0
  46. package/dist/loadSupergraphSdlFromStorage.js.map +1 -0
  47. package/dist/operationContext.d.ts +17 -0
  48. package/dist/operationContext.d.ts.map +1 -0
  49. package/dist/operationContext.js +42 -0
  50. package/dist/operationContext.js.map +1 -0
  51. package/dist/outOfBandReporter.d.ts +15 -0
  52. package/dist/outOfBandReporter.d.ts.map +1 -0
  53. package/dist/outOfBandReporter.js +88 -0
  54. package/dist/outOfBandReporter.js.map +1 -0
  55. package/dist/utilities/array.d.ts +1 -2
  56. package/dist/utilities/array.d.ts.map +1 -1
  57. package/dist/utilities/array.js +7 -14
  58. package/dist/utilities/array.js.map +1 -1
  59. package/dist/utilities/assert.d.ts +2 -0
  60. package/dist/utilities/assert.d.ts.map +1 -0
  61. package/dist/utilities/assert.js +10 -0
  62. package/dist/utilities/assert.js.map +1 -0
  63. package/dist/utilities/cleanErrorOfInaccessibleNames.d.ts +3 -0
  64. package/dist/utilities/cleanErrorOfInaccessibleNames.d.ts.map +1 -0
  65. package/dist/utilities/cleanErrorOfInaccessibleNames.js +27 -0
  66. package/dist/utilities/cleanErrorOfInaccessibleNames.js.map +1 -0
  67. package/dist/utilities/deepMerge.js +2 -2
  68. package/dist/utilities/deepMerge.js.map +1 -1
  69. package/dist/utilities/graphql.d.ts +1 -4
  70. package/dist/utilities/graphql.d.ts.map +1 -1
  71. package/dist/utilities/graphql.js +3 -36
  72. package/dist/utilities/graphql.js.map +1 -1
  73. package/dist/utilities/opentelemetry.d.ts +10 -0
  74. package/dist/utilities/opentelemetry.d.ts.map +1 -0
  75. package/dist/utilities/opentelemetry.js +19 -0
  76. package/dist/utilities/opentelemetry.js.map +1 -0
  77. package/package.json +30 -21
  78. package/src/__generated__/graphqlTypes.ts +140 -0
  79. package/src/__mocks__/apollo-server-env.ts +56 -0
  80. package/src/__mocks__/make-fetch-happen-fetcher.ts +55 -0
  81. package/src/__mocks__/tsconfig.json +7 -0
  82. package/src/__tests__/build-query-plan.feature +40 -311
  83. package/src/__tests__/buildQueryPlan.test.ts +246 -426
  84. package/src/__tests__/executeQueryPlan.test.ts +1691 -194
  85. package/src/__tests__/execution-utils.ts +33 -26
  86. package/src/__tests__/gateway/__snapshots__/opentelemetry.test.ts.snap +195 -0
  87. package/src/__tests__/gateway/buildService.test.ts +16 -19
  88. package/src/__tests__/gateway/composedSdl.test.ts +44 -0
  89. package/src/__tests__/gateway/endToEnd.test.ts +166 -0
  90. package/src/__tests__/gateway/executor.test.ts +49 -43
  91. package/src/__tests__/gateway/lifecycle-hooks.test.ts +58 -29
  92. package/src/__tests__/gateway/opentelemetry.test.ts +123 -0
  93. package/src/__tests__/gateway/queryPlanCache.test.ts +19 -20
  94. package/src/__tests__/gateway/reporting.test.ts +76 -55
  95. package/src/__tests__/integration/abstract-types.test.ts +1086 -22
  96. package/src/__tests__/integration/aliases.test.ts +5 -6
  97. package/src/__tests__/integration/boolean.test.ts +40 -38
  98. package/src/__tests__/integration/complex-key.test.ts +41 -56
  99. package/src/__tests__/integration/configuration.test.ts +321 -0
  100. package/src/__tests__/integration/custom-directives.test.ts +61 -46
  101. package/src/__tests__/integration/fragments.test.ts +8 -2
  102. package/src/__tests__/integration/list-key.test.ts +2 -2
  103. package/src/__tests__/integration/logger.test.ts +2 -2
  104. package/src/__tests__/integration/multiple-key.test.ts +11 -12
  105. package/src/__tests__/integration/mutations.test.ts +8 -5
  106. package/src/__tests__/integration/networkRequests.test.ts +447 -289
  107. package/src/__tests__/integration/nockMocks.ts +95 -66
  108. package/src/__tests__/integration/provides.test.ts +9 -6
  109. package/src/__tests__/integration/requires.test.ts +17 -15
  110. package/src/__tests__/integration/scope.test.ts +557 -0
  111. package/src/__tests__/integration/unions.test.ts +1 -1
  112. package/src/__tests__/integration/value-types.test.ts +35 -32
  113. package/src/__tests__/integration/variables.test.ts +8 -2
  114. package/src/__tests__/loadServicesFromRemoteEndpoint.test.ts +6 -2
  115. package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +694 -0
  116. package/src/__tests__/queryPlanCucumber.test.ts +11 -61
  117. package/src/__tests__/testSetup.ts +1 -4
  118. package/src/__tests__/tsconfig.json +2 -1
  119. package/src/config.ts +225 -0
  120. package/src/core/__tests__/core.test.ts +412 -0
  121. package/src/datasources/LocalGraphQLDataSource.ts +9 -10
  122. package/src/datasources/RemoteGraphQLDataSource.ts +117 -43
  123. package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +11 -4
  124. package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +148 -79
  125. package/src/datasources/__tests__/tsconfig.json +4 -2
  126. package/src/datasources/index.ts +1 -1
  127. package/src/datasources/parseCacheControlHeader.ts +43 -0
  128. package/src/datasources/types.ts +47 -2
  129. package/src/executeQueryPlan.ts +264 -153
  130. package/src/index.ts +925 -480
  131. package/src/loadServicesFromRemoteEndpoint.ts +24 -17
  132. package/src/loadSupergraphSdlFromStorage.ts +140 -0
  133. package/src/make-fetch-happen.d.ts +2 -2
  134. package/src/operationContext.ts +70 -0
  135. package/src/outOfBandReporter.ts +128 -0
  136. package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +104 -0
  137. package/src/utilities/__tests__/tsconfig.json +8 -0
  138. package/src/utilities/array.ts +6 -28
  139. package/src/utilities/assert.ts +14 -0
  140. package/src/utilities/cleanErrorOfInaccessibleNames.ts +29 -0
  141. package/src/utilities/graphql.ts +0 -64
  142. package/src/utilities/opentelemetry.ts +13 -0
  143. package/CHANGELOG.md +0 -226
  144. package/LICENSE.md +0 -20
  145. package/dist/FieldSet.d.ts +0 -18
  146. package/dist/FieldSet.d.ts.map +0 -1
  147. package/dist/FieldSet.js +0 -96
  148. package/dist/FieldSet.js.map +0 -1
  149. package/dist/QueryPlan.d.ts +0 -41
  150. package/dist/QueryPlan.d.ts.map +0 -1
  151. package/dist/QueryPlan.js +0 -15
  152. package/dist/QueryPlan.js.map +0 -1
  153. package/dist/buildQueryPlan.d.ts +0 -44
  154. package/dist/buildQueryPlan.d.ts.map +0 -1
  155. package/dist/buildQueryPlan.js +0 -670
  156. package/dist/buildQueryPlan.js.map +0 -1
  157. package/dist/loadServicesFromStorage.d.ts +0 -21
  158. package/dist/loadServicesFromStorage.d.ts.map +0 -1
  159. package/dist/loadServicesFromStorage.js +0 -64
  160. package/dist/loadServicesFromStorage.js.map +0 -1
  161. package/dist/snapshotSerializers/astSerializer.d.ts +0 -3
  162. package/dist/snapshotSerializers/astSerializer.d.ts.map +0 -1
  163. package/dist/snapshotSerializers/astSerializer.js +0 -14
  164. package/dist/snapshotSerializers/astSerializer.js.map +0 -1
  165. package/dist/snapshotSerializers/index.d.ts +0 -13
  166. package/dist/snapshotSerializers/index.d.ts.map +0 -1
  167. package/dist/snapshotSerializers/index.js +0 -15
  168. package/dist/snapshotSerializers/index.js.map +0 -1
  169. package/dist/snapshotSerializers/queryPlanSerializer.d.ts +0 -3
  170. package/dist/snapshotSerializers/queryPlanSerializer.d.ts.map +0 -1
  171. package/dist/snapshotSerializers/queryPlanSerializer.js +0 -78
  172. package/dist/snapshotSerializers/queryPlanSerializer.js.map +0 -1
  173. package/dist/snapshotSerializers/selectionSetSerializer.d.ts +0 -3
  174. package/dist/snapshotSerializers/selectionSetSerializer.d.ts.map +0 -1
  175. package/dist/snapshotSerializers/selectionSetSerializer.js +0 -12
  176. package/dist/snapshotSerializers/selectionSetSerializer.js.map +0 -1
  177. package/dist/snapshotSerializers/typeSerializer.d.ts +0 -3
  178. package/dist/snapshotSerializers/typeSerializer.d.ts.map +0 -1
  179. package/dist/snapshotSerializers/typeSerializer.js +0 -12
  180. package/dist/snapshotSerializers/typeSerializer.js.map +0 -1
  181. package/dist/utilities/MultiMap.d.ts +0 -4
  182. package/dist/utilities/MultiMap.d.ts.map +0 -1
  183. package/dist/utilities/MultiMap.js +0 -17
  184. package/dist/utilities/MultiMap.js.map +0 -1
  185. package/src/FieldSet.ts +0 -169
  186. package/src/QueryPlan.ts +0 -57
  187. package/src/__tests__/matchers/toCallService.ts +0 -105
  188. package/src/__tests__/matchers/toHaveBeenCalledBefore.ts +0 -40
  189. package/src/__tests__/matchers/toHaveFetched.ts +0 -81
  190. package/src/__tests__/matchers/toMatchAST.ts +0 -64
  191. package/src/buildQueryPlan.ts +0 -1190
  192. package/src/loadServicesFromStorage.ts +0 -170
  193. package/src/snapshotSerializers/astSerializer.ts +0 -21
  194. package/src/snapshotSerializers/index.ts +0 -21
  195. package/src/snapshotSerializers/queryPlanSerializer.ts +0 -144
  196. package/src/snapshotSerializers/selectionSetSerializer.ts +0 -13
  197. package/src/snapshotSerializers/typeSerializer.ts +0 -11
  198. package/src/utilities/MultiMap.ts +0 -11
@@ -1,43 +1,45 @@
1
- import { GraphQLSchema, GraphQLError } from 'graphql';
2
- import gql from 'graphql-tag';
3
- import { buildQueryPlan, buildOperationContext } from '../buildQueryPlan';
4
- import { astSerializer, queryPlanSerializer } from '../snapshotSerializers';
1
+ import { astSerializer, queryPlanSerializer } from 'apollo-federation-integration-testsuite';
5
2
  import { getFederatedTestingSchema } from './execution-utils';
3
+ import { QueryPlan, QueryPlanner } from '@apollo/query-planner';
4
+ import { Schema, parseOperation } from '@apollo/federation-internals';
6
5
 
7
6
  expect.addSnapshotSerializer(astSerializer);
8
7
  expect.addSnapshotSerializer(queryPlanSerializer);
9
8
 
9
+
10
10
  describe('buildQueryPlan', () => {
11
- let schema: GraphQLSchema;
12
- let errors: GraphQLError[];
11
+ let schema: Schema;
12
+ let queryPlanner: QueryPlanner;
13
+
14
+ const buildPlan = (operation: string): QueryPlan => {
15
+ return queryPlanner.buildQueryPlan(parseOperation(schema, operation));
16
+ }
13
17
 
14
18
  beforeEach(() => {
15
- ({ schema, errors } = getFederatedTestingSchema());
16
- expect(errors).toHaveLength(0);
19
+ expect(
20
+ () => ({ schema, queryPlanner } = getFederatedTestingSchema()),
21
+ ).not.toThrow();
17
22
  });
18
23
 
19
24
  it(`should not confuse union types with overlapping field names`, () => {
20
- const query = gql`
25
+ const operationString = `#graphql
21
26
  query {
22
- body {
23
- ... on Image {
24
- attributes {
25
- url
27
+ body {
28
+ ... on Image {
29
+ attributes {
30
+ url
31
+ }
26
32
  }
27
- }
28
- ... on Text {
29
- attributes {
30
- bold
31
- text
33
+ ... on Text {
34
+ attributes {
35
+ bold
36
+ text
37
+ }
32
38
  }
33
39
  }
34
40
  }
35
- }
36
41
  `;
37
-
38
- const queryPlan = buildQueryPlan(
39
- buildOperationContext(schema, query, undefined),
40
- );
42
+ const queryPlan = buildPlan(operationString);
41
43
 
42
44
  expect(queryPlan).toMatchInlineSnapshot(`
43
45
  QueryPlan {
@@ -64,22 +66,25 @@ describe('buildQueryPlan', () => {
64
66
  });
65
67
 
66
68
  it(`should use a single fetch when requesting a root field from one service`, () => {
67
- const query = gql`
69
+ const operationString = `#graphql
68
70
  query {
69
71
  me {
70
- name
72
+ name {
73
+ first
74
+ }
71
75
  }
72
76
  }
73
77
  `;
74
78
 
75
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
76
-
79
+ const queryPlan = buildPlan(operationString);
77
80
  expect(queryPlan).toMatchInlineSnapshot(`
78
81
  QueryPlan {
79
82
  Fetch(service: "accounts") {
80
83
  {
81
84
  me {
82
- name
85
+ name {
86
+ first
87
+ }
83
88
  }
84
89
  }
85
90
  },
@@ -88,10 +93,12 @@ describe('buildQueryPlan', () => {
88
93
  });
89
94
 
90
95
  it(`should use two independent fetches when requesting root fields from two services`, () => {
91
- const query = gql`
96
+ const operationString = `#graphql
92
97
  query {
93
98
  me {
94
- name
99
+ name {
100
+ first
101
+ }
95
102
  }
96
103
  topProducts {
97
104
  name
@@ -99,7 +106,7 @@ describe('buildQueryPlan', () => {
99
106
  }
100
107
  `;
101
108
 
102
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
109
+ const queryPlan = buildPlan(operationString);
103
110
 
104
111
  expect(queryPlan).toMatchInlineSnapshot(`
105
112
  QueryPlan {
@@ -107,7 +114,9 @@ describe('buildQueryPlan', () => {
107
114
  Fetch(service: "accounts") {
108
115
  {
109
116
  me {
110
- name
117
+ name {
118
+ first
119
+ }
111
120
  }
112
121
  }
113
122
  },
@@ -136,8 +145,6 @@ describe('buildQueryPlan', () => {
136
145
  } =>
137
146
  {
138
147
  ... on Book {
139
- __typename
140
- isbn
141
148
  title
142
149
  year
143
150
  }
@@ -149,9 +156,9 @@ describe('buildQueryPlan', () => {
149
156
  {
150
157
  ... on Book {
151
158
  __typename
152
- isbn
153
159
  title
154
160
  year
161
+ isbn
155
162
  }
156
163
  } =>
157
164
  {
@@ -168,7 +175,7 @@ describe('buildQueryPlan', () => {
168
175
  });
169
176
 
170
177
  it(`should use a single fetch when requesting multiple root fields from the same service`, () => {
171
- const query = gql`
178
+ const operationString = `#graphql
172
179
  query {
173
180
  topProducts {
174
181
  name
@@ -179,7 +186,7 @@ describe('buildQueryPlan', () => {
179
186
  }
180
187
  `;
181
188
 
182
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
189
+ const queryPlan = buildPlan(operationString);
183
190
 
184
191
  expect(queryPlan).toMatchInlineSnapshot(`
185
192
  QueryPlan {
@@ -220,8 +227,6 @@ describe('buildQueryPlan', () => {
220
227
  } =>
221
228
  {
222
229
  ... on Book {
223
- __typename
224
- isbn
225
230
  title
226
231
  year
227
232
  }
@@ -233,9 +238,9 @@ describe('buildQueryPlan', () => {
233
238
  {
234
239
  ... on Book {
235
240
  __typename
236
- isbn
237
241
  title
238
242
  year
243
+ isbn
239
244
  }
240
245
  } =>
241
246
  {
@@ -257,8 +262,6 @@ describe('buildQueryPlan', () => {
257
262
  } =>
258
263
  {
259
264
  ... on Book {
260
- __typename
261
- isbn
262
265
  title
263
266
  year
264
267
  }
@@ -270,9 +273,9 @@ describe('buildQueryPlan', () => {
270
273
  {
271
274
  ... on Book {
272
275
  __typename
273
- isbn
274
276
  title
275
277
  year
278
+ isbn
276
279
  }
277
280
  } =>
278
281
  {
@@ -290,7 +293,7 @@ describe('buildQueryPlan', () => {
290
293
  });
291
294
 
292
295
  it(`should use a single fetch when requesting relationship subfields from the same service`, () => {
293
- const query = gql`
296
+ const operationString = `#graphql
294
297
  query {
295
298
  topReviews {
296
299
  body
@@ -303,7 +306,7 @@ describe('buildQueryPlan', () => {
303
306
  }
304
307
  `;
305
308
 
306
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
309
+ const queryPlan = buildPlan(operationString);
307
310
 
308
311
  expect(queryPlan).toMatchInlineSnapshot(`
309
312
  QueryPlan {
@@ -324,7 +327,7 @@ describe('buildQueryPlan', () => {
324
327
  });
325
328
 
326
329
  it(`should use a single fetch when requesting relationship subfields and provided keys from the same service`, () => {
327
- const query = gql`
330
+ const operationString = `#graphql
328
331
  query {
329
332
  topReviews {
330
333
  body
@@ -338,7 +341,7 @@ describe('buildQueryPlan', () => {
338
341
  }
339
342
  `;
340
343
 
341
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
344
+ const queryPlan = buildPlan(operationString);
342
345
 
343
346
  expect(queryPlan).toMatchInlineSnapshot(`
344
347
  QueryPlan {
@@ -361,10 +364,12 @@ describe('buildQueryPlan', () => {
361
364
 
362
365
  describe(`when requesting an extension field from another service`, () => {
363
366
  it(`should add the field's representation requirements to the parent selection set and use a dependent fetch`, () => {
364
- const query = gql`
367
+ const operationString = `#graphql
365
368
  query {
366
369
  me {
367
- name
370
+ name {
371
+ first
372
+ }
368
373
  reviews {
369
374
  body
370
375
  }
@@ -372,7 +377,7 @@ describe('buildQueryPlan', () => {
372
377
  }
373
378
  `;
374
379
 
375
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
380
+ const queryPlan = buildPlan(operationString);
376
381
 
377
382
  expect(queryPlan).toMatchInlineSnapshot(`
378
383
  QueryPlan {
@@ -380,9 +385,11 @@ describe('buildQueryPlan', () => {
380
385
  Fetch(service: "accounts") {
381
386
  {
382
387
  me {
383
- name
384
388
  __typename
385
389
  id
390
+ name {
391
+ first
392
+ }
386
393
  }
387
394
  }
388
395
  },
@@ -410,7 +417,7 @@ describe('buildQueryPlan', () => {
410
417
 
411
418
  describe(`when the parent selection set is empty`, () => {
412
419
  it(`should add the field's requirements to the parent selection set and use a dependent fetch`, () => {
413
- const query = gql`
420
+ const operationString = `#graphql
414
421
  query {
415
422
  me {
416
423
  reviews {
@@ -420,7 +427,7 @@ describe('buildQueryPlan', () => {
420
427
  }
421
428
  `;
422
429
 
423
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
430
+ const queryPlan = buildPlan(operationString);
424
431
 
425
432
  expect(queryPlan).toMatchInlineSnapshot(`
426
433
  QueryPlan {
@@ -458,7 +465,7 @@ describe('buildQueryPlan', () => {
458
465
 
459
466
  // TODO: Ask martijn about the meaning of this test
460
467
  it(`should only add requirements once`, () => {
461
- const query = gql`
468
+ const operationString = `#graphql
462
469
  query {
463
470
  me {
464
471
  reviews {
@@ -469,7 +476,7 @@ describe('buildQueryPlan', () => {
469
476
  }
470
477
  `;
471
478
 
472
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
479
+ const queryPlan = buildPlan(operationString);
473
480
 
474
481
  expect(queryPlan).toMatchInlineSnapshot(`
475
482
  QueryPlan {
@@ -508,18 +515,20 @@ describe('buildQueryPlan', () => {
508
515
 
509
516
  describe(`when requesting a composite field with subfields from another service`, () => {
510
517
  it(`should add key fields to the parent selection set and use a dependent fetch`, () => {
511
- const query = gql`
518
+ const operationString = `#graphql
512
519
  query {
513
520
  topReviews {
514
521
  body
515
522
  author {
516
- name
523
+ name {
524
+ first
525
+ }
517
526
  }
518
527
  }
519
528
  }
520
529
  `;
521
530
 
522
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
531
+ const queryPlan = buildPlan(operationString);
523
532
 
524
533
  expect(queryPlan).toMatchInlineSnapshot(`
525
534
  QueryPlan {
@@ -545,7 +554,9 @@ describe('buildQueryPlan', () => {
545
554
  } =>
546
555
  {
547
556
  ... on User {
548
- name
557
+ name {
558
+ first
559
+ }
549
560
  }
550
561
  }
551
562
  },
@@ -557,7 +568,7 @@ describe('buildQueryPlan', () => {
557
568
 
558
569
  describe(`when requesting a field defined in another service which requires a field in the base service`, () => {
559
570
  it(`should add the field provided by base service in first Fetch`, () => {
560
- const query = gql`
571
+ const operationString = `#graphql
561
572
  query {
562
573
  topCars {
563
574
  retailPrice
@@ -565,7 +576,7 @@ describe('buildQueryPlan', () => {
565
576
  }
566
577
  `;
567
578
 
568
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
579
+ const queryPlan = buildPlan(operationString);
569
580
 
570
581
  expect(queryPlan).toMatchInlineSnapshot(`
571
582
  QueryPlan {
@@ -603,17 +614,19 @@ describe('buildQueryPlan', () => {
603
614
 
604
615
  describe(`when the parent selection set is empty`, () => {
605
616
  it(`should add key fields to the parent selection set and use a dependent fetch`, () => {
606
- const query = gql`
617
+ const operationString = `#graphql
607
618
  query {
608
619
  topReviews {
609
620
  author {
610
- name
621
+ name {
622
+ first
623
+ }
611
624
  }
612
625
  }
613
626
  }
614
627
  `;
615
628
 
616
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
629
+ const queryPlan = buildPlan(operationString);
617
630
 
618
631
  expect(queryPlan).toMatchInlineSnapshot(`
619
632
  QueryPlan {
@@ -638,7 +651,9 @@ describe('buildQueryPlan', () => {
638
651
  } =>
639
652
  {
640
653
  ... on User {
641
- name
654
+ name {
655
+ first
656
+ }
642
657
  }
643
658
  }
644
659
  },
@@ -651,7 +666,7 @@ describe('buildQueryPlan', () => {
651
666
  });
652
667
  describe(`when requesting a relationship field with extension subfields from a different service`, () => {
653
668
  it(`should first fetch the object using a key from the base service and then pass through the requirements`, () => {
654
- const query = gql`
669
+ const operationString = `#graphql
655
670
  query {
656
671
  topReviews {
657
672
  author {
@@ -661,7 +676,7 @@ describe('buildQueryPlan', () => {
661
676
  }
662
677
  `;
663
678
 
664
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
679
+ const queryPlan = buildPlan(operationString);
665
680
 
666
681
  expect(queryPlan).toMatchInlineSnapshot(`
667
682
  QueryPlan {
@@ -702,7 +717,7 @@ describe('buildQueryPlan', () => {
702
717
  // Probably an issue with extending / interfaces in composition. None of the fields from the base Book type
703
718
  // are showing up in the resulting schema.
704
719
  it(`should add __typename when fetching objects of an interface type from a service`, () => {
705
- const query = gql`
720
+ const operationString = `#graphql
706
721
  query {
707
722
  topProducts {
708
723
  price
@@ -710,7 +725,7 @@ describe('buildQueryPlan', () => {
710
725
  }
711
726
  `;
712
727
 
713
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
728
+ const queryPlan = buildPlan(operationString);
714
729
 
715
730
  expect(queryPlan).toMatchInlineSnapshot(`
716
731
  QueryPlan {
@@ -718,25 +733,164 @@ describe('buildQueryPlan', () => {
718
733
  {
719
734
  topProducts {
720
735
  __typename
721
- ... on Book {
722
- price
723
- }
724
- ... on Furniture {
725
- price
726
- }
736
+ price
727
737
  }
728
738
  }
729
739
  },
730
740
  }
731
741
  `);
732
742
  });
743
+
744
+ it(`should not get confused by a fragment spread multiple times`, () => {
745
+ const operationString = `#graphql
746
+ fragment Price on Product {
747
+ price
748
+ }
749
+
750
+ query {
751
+ topProducts {
752
+ __typename
753
+ ... on Book {
754
+ ...Price
755
+ }
756
+ ... on Furniture {
757
+ ...Price
758
+ }
759
+ }
760
+ }
761
+ `;
762
+
763
+ const queryPlan = buildPlan(operationString);
764
+
765
+ expect(queryPlan).toMatchInlineSnapshot(`
766
+ QueryPlan {
767
+ Fetch(service: "product") {
768
+ {
769
+ topProducts {
770
+ __typename
771
+ ... on Book {
772
+ price
773
+ }
774
+ ... on Furniture {
775
+ price
776
+ }
777
+ }
778
+ }
779
+ },
780
+ }
781
+ `);
782
+ });
783
+
784
+ it(`should not get confused by an inline fragment multiple times`, () => {
785
+ const operationString = `#graphql
786
+ query {
787
+ topProducts {
788
+ __typename
789
+ ... on Book {
790
+ ...on Product {
791
+ price
792
+ }
793
+ }
794
+ ... on Furniture {
795
+ ... on Product {
796
+ price
797
+ }
798
+ }
799
+ }
800
+ }
801
+ `;
802
+
803
+ const queryPlan = buildPlan(operationString);
804
+
805
+ expect(queryPlan).toMatchInlineSnapshot(`
806
+ QueryPlan {
807
+ Fetch(service: "product") {
808
+ {
809
+ topProducts {
810
+ __typename
811
+ ... on Book {
812
+ price
813
+ }
814
+ ... on Furniture {
815
+ price
816
+ }
817
+ }
818
+ }
819
+ },
820
+ }
821
+ `);
822
+ });
823
+
824
+ it(`eliminate unecessary type conditions`, () => {
825
+ const operationString = `#graphql
826
+ query {
827
+ body {
828
+ ... on Image {
829
+ ... on NamedObject {
830
+ name
831
+ }
832
+ }
833
+ }
834
+ }
835
+ `;
836
+
837
+ const queryPlan = buildPlan(operationString);
838
+
839
+ expect(queryPlan).toMatchInlineSnapshot(`
840
+ QueryPlan {
841
+ Fetch(service: "documents") {
842
+ {
843
+ body {
844
+ __typename
845
+ ... on Image {
846
+ name
847
+ }
848
+ }
849
+ }
850
+ },
851
+ }
852
+ `);
853
+ });
854
+
855
+ it(`should preserve directives on inline fragments even if the fragment is otherwise useless`, () => {
856
+ const operationString = `#graphql
857
+ query myQuery($b: Boolean!) {
858
+ body {
859
+ ... on Image {
860
+ ... on NamedObject @include(if: $b) {
861
+ name
862
+ }
863
+ }
864
+ }
865
+ }
866
+ `;
867
+
868
+ const queryPlan = buildPlan(operationString);
869
+
870
+ expect(queryPlan).toMatchInlineSnapshot(`
871
+ QueryPlan {
872
+ Fetch(service: "documents") {
873
+ {
874
+ body {
875
+ __typename
876
+ ... on Image {
877
+ ... on NamedObject @include(if: $b) {
878
+ name
879
+ }
880
+ }
881
+ }
882
+ }
883
+ },
884
+ }
885
+ `);
886
+ });
733
887
  });
734
888
 
735
889
  // GraphQLError: Cannot query field "isbn" on type "Book"
736
890
  // Probably an issue with extending / interfaces in composition. None of the fields from the base Book type
737
891
  // are showing up in the resulting schema.
738
892
  it(`should break up when traversing an extension field on an interface type from a service`, () => {
739
- const query = gql`
893
+ const operationString = `#graphql
740
894
  query {
741
895
  topProducts {
742
896
  price
@@ -747,7 +901,7 @@ describe('buildQueryPlan', () => {
747
901
  }
748
902
  `;
749
903
 
750
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
904
+ const queryPlan = buildPlan(operationString);
751
905
 
752
906
  expect(queryPlan).toMatchInlineSnapshot(`
753
907
  QueryPlan {
@@ -756,13 +910,12 @@ describe('buildQueryPlan', () => {
756
910
  {
757
911
  topProducts {
758
912
  __typename
913
+ price
759
914
  ... on Book {
760
- price
761
915
  __typename
762
916
  isbn
763
917
  }
764
918
  ... on Furniture {
765
- price
766
919
  __typename
767
920
  upc
768
921
  }
@@ -801,7 +954,7 @@ describe('buildQueryPlan', () => {
801
954
  });
802
955
 
803
956
  it(`interface fragments should expand into possible types only`, () => {
804
- const query = gql`
957
+ const operationString = `#graphql
805
958
  query {
806
959
  books {
807
960
  ... on Product {
@@ -814,7 +967,7 @@ describe('buildQueryPlan', () => {
814
967
  }
815
968
  `;
816
969
 
817
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
970
+ const queryPlan = buildPlan(operationString);
818
971
 
819
972
  expect(queryPlan).toMatchInlineSnapshot(`
820
973
  QueryPlan {
@@ -851,8 +1004,8 @@ describe('buildQueryPlan', () => {
851
1004
  `);
852
1005
  });
853
1006
 
854
- it(`interface inside interface should expand into possible types only`, () => {
855
- const query = gql`
1007
+ it(`interface inside interface should not type explode if possible`, () => {
1008
+ const operationString = `#graphql
856
1009
  query {
857
1010
  product(upc: "") {
858
1011
  details {
@@ -862,7 +1015,7 @@ describe('buildQueryPlan', () => {
862
1015
  }
863
1016
  `;
864
1017
 
865
- const queryPlan = buildQueryPlan(buildOperationContext(schema, query));
1018
+ const queryPlan = buildPlan(operationString);
866
1019
 
867
1020
  expect(queryPlan).toMatchInlineSnapshot(`
868
1021
  QueryPlan {
@@ -870,15 +1023,9 @@ describe('buildQueryPlan', () => {
870
1023
  {
871
1024
  product(upc: "") {
872
1025
  __typename
873
- ... on Book {
874
- details {
875
- country
876
- }
877
- }
878
- ... on Furniture {
879
- details {
880
- country
881
- }
1026
+ details {
1027
+ __typename
1028
+ country
882
1029
  }
883
1030
  }
884
1031
  }
@@ -887,319 +1034,8 @@ describe('buildQueryPlan', () => {
887
1034
  `);
888
1035
  });
889
1036
 
890
- describe(`experimental compression to downstream services`, () => {
891
- it(`should generate fragments internally to downstream requests`, () => {
892
- const query = gql`
893
- query {
894
- topReviews {
895
- body
896
- author
897
- product {
898
- name
899
- price
900
- details {
901
- country
902
- }
903
- }
904
- }
905
- }
906
- `;
907
-
908
- const queryPlan = buildQueryPlan(
909
- buildOperationContext(schema, query, undefined),
910
- { autoFragmentization: true },
911
- );
912
-
913
- expect(queryPlan).toMatchInlineSnapshot(`
914
- QueryPlan {
915
- Sequence {
916
- Fetch(service: "reviews") {
917
- {
918
- topReviews {
919
- ...__QueryPlanFragment_1__
920
- }
921
- }
922
- fragment __QueryPlanFragment_1__ on Review {
923
- body
924
- author
925
- product {
926
- ...__QueryPlanFragment_0__
927
- }
928
- }
929
- fragment __QueryPlanFragment_0__ on Product {
930
- __typename
931
- ... on Book {
932
- __typename
933
- isbn
934
- }
935
- ... on Furniture {
936
- __typename
937
- upc
938
- }
939
- }
940
- },
941
- Parallel {
942
- Sequence {
943
- Flatten(path: "topReviews.@.product") {
944
- Fetch(service: "books") {
945
- {
946
- ... on Book {
947
- __typename
948
- isbn
949
- }
950
- } =>
951
- {
952
- ... on Book {
953
- __typename
954
- isbn
955
- title
956
- year
957
- }
958
- }
959
- },
960
- },
961
- Flatten(path: "topReviews.@.product") {
962
- Fetch(service: "product") {
963
- {
964
- ... on Book {
965
- __typename
966
- isbn
967
- title
968
- year
969
- }
970
- } =>
971
- {
972
- ... on Book {
973
- name
974
- }
975
- }
976
- },
977
- },
978
- },
979
- Flatten(path: "topReviews.@.product") {
980
- Fetch(service: "product") {
981
- {
982
- ... on Furniture {
983
- __typename
984
- upc
985
- }
986
- ... on Book {
987
- __typename
988
- isbn
989
- }
990
- } =>
991
- {
992
- ... on Furniture {
993
- name
994
- price
995
- details {
996
- country
997
- }
998
- }
999
- ... on Book {
1000
- price
1001
- details {
1002
- country
1003
- }
1004
- }
1005
- }
1006
- },
1007
- },
1008
- },
1009
- },
1010
- }
1011
- `);
1012
- });
1013
-
1014
- it(`shouldn't generate fragments for selection sets of length 2 or less`, () => {
1015
- const query = gql`
1016
- query {
1017
- topReviews {
1018
- body
1019
- author
1020
- }
1021
- }
1022
- `;
1023
-
1024
- const queryPlan = buildQueryPlan(
1025
- buildOperationContext(schema, query, undefined),
1026
- { autoFragmentization: true },
1027
- );
1028
-
1029
- expect(queryPlan).toMatchInlineSnapshot(`
1030
- QueryPlan {
1031
- Fetch(service: "reviews") {
1032
- {
1033
- topReviews {
1034
- body
1035
- author
1036
- }
1037
- }
1038
- },
1039
- }
1040
- `);
1041
- });
1042
-
1043
- it(`should generate fragments for selection sets of length 3 or greater`, () => {
1044
- const query = gql`
1045
- query {
1046
- topReviews {
1047
- id
1048
- body
1049
- author
1050
- }
1051
- }
1052
- `;
1053
-
1054
- const queryPlan = buildQueryPlan(
1055
- buildOperationContext(schema, query, undefined),
1056
- { autoFragmentization: true },
1057
- );
1058
-
1059
- expect(queryPlan).toMatchInlineSnapshot(`
1060
- QueryPlan {
1061
- Fetch(service: "reviews") {
1062
- {
1063
- topReviews {
1064
- ...__QueryPlanFragment_0__
1065
- }
1066
- }
1067
- fragment __QueryPlanFragment_0__ on Review {
1068
- id
1069
- body
1070
- author
1071
- }
1072
- },
1073
- }
1074
- `);
1075
- });
1076
-
1077
- it(`should generate fragments correctly when aliases are used`, () => {
1078
- const query = gql`
1079
- query {
1080
- reviews: topReviews {
1081
- content: body
1082
- author
1083
- product {
1084
- name
1085
- cost: price
1086
- details {
1087
- origin: country
1088
- }
1089
- }
1090
- }
1091
- }
1092
- `;
1093
-
1094
- const queryPlan = buildQueryPlan(
1095
- buildOperationContext(schema, query, undefined),
1096
- { autoFragmentization: true },
1097
- );
1098
-
1099
- expect(queryPlan).toMatchInlineSnapshot(`
1100
- QueryPlan {
1101
- Sequence {
1102
- Fetch(service: "reviews") {
1103
- {
1104
- reviews: topReviews {
1105
- ...__QueryPlanFragment_1__
1106
- }
1107
- }
1108
- fragment __QueryPlanFragment_1__ on Review {
1109
- content: body
1110
- author
1111
- product {
1112
- ...__QueryPlanFragment_0__
1113
- }
1114
- }
1115
- fragment __QueryPlanFragment_0__ on Product {
1116
- __typename
1117
- ... on Book {
1118
- __typename
1119
- isbn
1120
- }
1121
- ... on Furniture {
1122
- __typename
1123
- upc
1124
- }
1125
- }
1126
- },
1127
- Parallel {
1128
- Sequence {
1129
- Flatten(path: "reviews.@.product") {
1130
- Fetch(service: "books") {
1131
- {
1132
- ... on Book {
1133
- __typename
1134
- isbn
1135
- }
1136
- } =>
1137
- {
1138
- ... on Book {
1139
- __typename
1140
- isbn
1141
- title
1142
- year
1143
- }
1144
- }
1145
- },
1146
- },
1147
- Flatten(path: "reviews.@.product") {
1148
- Fetch(service: "product") {
1149
- {
1150
- ... on Book {
1151
- __typename
1152
- isbn
1153
- title
1154
- year
1155
- }
1156
- } =>
1157
- {
1158
- ... on Book {
1159
- name
1160
- }
1161
- }
1162
- },
1163
- },
1164
- },
1165
- Flatten(path: "reviews.@.product") {
1166
- Fetch(service: "product") {
1167
- {
1168
- ... on Furniture {
1169
- __typename
1170
- upc
1171
- }
1172
- ... on Book {
1173
- __typename
1174
- isbn
1175
- }
1176
- } =>
1177
- {
1178
- ... on Furniture {
1179
- name
1180
- cost: price
1181
- details {
1182
- origin: country
1183
- }
1184
- }
1185
- ... on Book {
1186
- cost: price
1187
- details {
1188
- origin: country
1189
- }
1190
- }
1191
- }
1192
- },
1193
- },
1194
- },
1195
- },
1196
- }
1197
- `);
1198
- });
1199
- });
1200
-
1201
1037
  it(`should properly expand nested unions with inline fragments`, () => {
1202
- const query = gql`
1038
+ const operationString = `#graphql
1203
1039
  query {
1204
1040
  body {
1205
1041
  ... on Image {
@@ -1226,9 +1062,7 @@ describe('buildQueryPlan', () => {
1226
1062
  }
1227
1063
  `;
1228
1064
 
1229
- const queryPlan = buildQueryPlan(
1230
- buildOperationContext(schema, query, undefined),
1231
- );
1065
+ const queryPlan = buildPlan(operationString);
1232
1066
 
1233
1067
  expect(queryPlan).toMatchInlineSnapshot(`
1234
1068
  QueryPlan {
@@ -1255,16 +1089,9 @@ describe('buildQueryPlan', () => {
1255
1089
 
1256
1090
  describe('deduplicates fields / selections regardless of adjacency and type condition nesting', () => {
1257
1091
  it('for inline fragments', () => {
1258
- const query = gql`
1092
+ const operationString = `#graphql
1259
1093
  query {
1260
1094
  body {
1261
- ... on Image {
1262
- ... on Text {
1263
- attributes {
1264
- bold
1265
- }
1266
- }
1267
- }
1268
1095
  ... on Body {
1269
1096
  ... on Text {
1270
1097
  attributes {
@@ -1283,9 +1110,7 @@ describe('buildQueryPlan', () => {
1283
1110
  }
1284
1111
  `;
1285
1112
 
1286
- const queryPlan = buildQueryPlan(
1287
- buildOperationContext(schema, query, undefined),
1288
- );
1113
+ const queryPlan = buildPlan(operationString);
1289
1114
 
1290
1115
  expect(queryPlan).toMatchInlineSnapshot(`
1291
1116
  QueryPlan {
@@ -1307,7 +1132,7 @@ describe('buildQueryPlan', () => {
1307
1132
  });
1308
1133
 
1309
1134
  it('for named fragment spreads', () => {
1310
- const query = gql`
1135
+ const operationString = `#graphql
1311
1136
  fragment TextFragment on Text {
1312
1137
  attributes {
1313
1138
  bold
@@ -1317,9 +1142,6 @@ describe('buildQueryPlan', () => {
1317
1142
 
1318
1143
  query {
1319
1144
  body {
1320
- ... on Image {
1321
- ...TextFragment
1322
- }
1323
1145
  ... on Body {
1324
1146
  ...TextFragment
1325
1147
  }
@@ -1328,9 +1150,7 @@ describe('buildQueryPlan', () => {
1328
1150
  }
1329
1151
  `;
1330
1152
 
1331
- const queryPlan = buildQueryPlan(
1332
- buildOperationContext(schema, query, undefined),
1333
- );
1153
+ const queryPlan = buildPlan(operationString);
1334
1154
 
1335
1155
  expect(queryPlan).toMatchInlineSnapshot(`
1336
1156
  QueryPlan {