@apollo/gateway 2.0.1 → 2.0.2-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 (94) hide show
  1. package/dist/config.d.ts +2 -2
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/datasources/RemoteGraphQLDataSource.d.ts +6 -5
  4. package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
  5. package/dist/datasources/RemoteGraphQLDataSource.js +18 -13
  6. package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
  7. package/dist/executeQueryPlan.d.ts.map +1 -1
  8. package/dist/executeQueryPlan.js +2 -2
  9. package/dist/executeQueryPlan.js.map +1 -1
  10. package/dist/index.d.ts +0 -4
  11. package/dist/index.d.ts.map +1 -1
  12. package/dist/index.js +7 -25
  13. package/dist/index.js.map +1 -1
  14. package/dist/schema-helper/addExtensions.d.ts +3 -0
  15. package/dist/schema-helper/addExtensions.d.ts.map +1 -0
  16. package/dist/schema-helper/addExtensions.js +20 -0
  17. package/dist/schema-helper/addExtensions.js.map +1 -0
  18. package/dist/supergraphManagers/UplinkFetcher/index.d.ts +2 -2
  19. package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -1
  20. package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -1
  21. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts +3 -3
  22. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -1
  23. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js +14 -13
  24. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -1
  25. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts +6 -5
  26. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +1 -1
  27. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js +3 -3
  28. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +1 -1
  29. package/dist/typings/graphql.d.ts +34 -0
  30. package/dist/typings/graphql.d.ts.map +1 -0
  31. package/dist/{schema-helper/resolverMap.js → typings/graphql.js} +1 -1
  32. package/dist/typings/graphql.js.map +1 -0
  33. package/package.json +10 -8
  34. package/src/__tests__/executeQueryPlan.test.ts +4 -1
  35. package/src/__tests__/execution-utils.ts +2 -2
  36. package/src/__tests__/gateway/buildService.test.ts +81 -83
  37. package/src/__tests__/gateway/endToEnd.test.ts +1 -1
  38. package/src/__tests__/gateway/executor.test.ts +20 -17
  39. package/src/__tests__/gateway/extensions.test.ts +37 -0
  40. package/src/__tests__/gateway/lifecycle-hooks.test.ts +1 -1
  41. package/src/__tests__/gateway/opentelemetry.test.ts +3 -7
  42. package/src/__tests__/gateway/reporting.test.ts +1 -1
  43. package/src/__tests__/gateway/supergraphSdl.test.ts +7 -12
  44. package/src/config.ts +2 -2
  45. package/src/datasources/RemoteGraphQLDataSource.ts +34 -18
  46. package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
  47. package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +106 -140
  48. package/src/executeQueryPlan.ts +5 -1
  49. package/src/index.ts +15 -32
  50. package/src/schema-helper/__tests__/addExtensions.test.ts +11 -0
  51. package/src/schema-helper/addExtensions.ts +17 -0
  52. package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +0 -6
  53. package/src/supergraphManagers/UplinkFetcher/__tests__/loadSupergraphSdlFromStorage.test.ts +70 -74
  54. package/src/supergraphManagers/UplinkFetcher/index.ts +2 -2
  55. package/src/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts +23 -17
  56. package/src/supergraphManagers/UplinkFetcher/outOfBandReporter.ts +9 -7
  57. package/src/typings/graphql.ts +43 -0
  58. package/dist/cache.d.ts +0 -18
  59. package/dist/cache.d.ts.map +0 -1
  60. package/dist/cache.js +0 -46
  61. package/dist/cache.js.map +0 -1
  62. package/dist/schema-helper/addResolversToSchema.d.ts +0 -4
  63. package/dist/schema-helper/addResolversToSchema.d.ts.map +0 -1
  64. package/dist/schema-helper/addResolversToSchema.js +0 -62
  65. package/dist/schema-helper/addResolversToSchema.js.map +0 -1
  66. package/dist/schema-helper/error.d.ts +0 -6
  67. package/dist/schema-helper/error.d.ts.map +0 -1
  68. package/dist/schema-helper/error.js +0 -14
  69. package/dist/schema-helper/error.js.map +0 -1
  70. package/dist/schema-helper/index.d.ts +0 -4
  71. package/dist/schema-helper/index.d.ts.map +0 -1
  72. package/dist/schema-helper/index.js +0 -20
  73. package/dist/schema-helper/index.js.map +0 -1
  74. package/dist/schema-helper/resolverMap.d.ts +0 -16
  75. package/dist/schema-helper/resolverMap.d.ts.map +0 -1
  76. package/dist/schema-helper/resolverMap.js.map +0 -1
  77. package/dist/utilities/createHash.d.ts +0 -2
  78. package/dist/utilities/createHash.d.ts.map +0 -1
  79. package/dist/utilities/createHash.js +0 -15
  80. package/dist/utilities/createHash.js.map +0 -1
  81. package/dist/utilities/isNodeLike.d.ts +0 -3
  82. package/dist/utilities/isNodeLike.d.ts.map +0 -1
  83. package/dist/utilities/isNodeLike.js +0 -8
  84. package/dist/utilities/isNodeLike.js.map +0 -1
  85. package/src/__mocks__/apollo-server-env.ts +0 -56
  86. package/src/__mocks__/make-fetch-happen-fetcher.ts +0 -57
  87. package/src/cache.ts +0 -66
  88. package/src/make-fetch-happen.d.ts +0 -59
  89. package/src/schema-helper/addResolversToSchema.ts +0 -83
  90. package/src/schema-helper/error.ts +0 -11
  91. package/src/schema-helper/index.ts +0 -3
  92. package/src/schema-helper/resolverMap.ts +0 -23
  93. package/src/utilities/createHash.ts +0 -10
  94. package/src/utilities/isNodeLike.ts +0 -11
@@ -1,6 +1,3 @@
1
- import { fetch as customFetcher } from '../../__mocks__/apollo-server-env';
2
- import { fetch } from '../../__mocks__/make-fetch-happen-fetcher';
3
-
4
1
  import {
5
2
  ApolloError,
6
3
  AuthenticationError,
@@ -8,13 +5,18 @@ import {
8
5
  } from 'apollo-server-errors';
9
6
 
10
7
  import { RemoteGraphQLDataSource } from '../RemoteGraphQLDataSource';
11
- import { Headers } from 'apollo-server-env';
8
+ import { Response, Headers } from 'node-fetch';
12
9
  import { GraphQLRequestContext } from 'apollo-server-types';
13
10
  import { GraphQLDataSourceRequestKind } from '../types';
11
+ import { nockBeforeEach, nockAfterEach } from '../../__tests__/nockAssertions';
12
+ import nock from 'nock';
14
13
 
15
- beforeEach(() => {
16
- fetch.mockReset();
17
- });
14
+ beforeEach(nockBeforeEach);
15
+ afterEach(nockAfterEach);
16
+
17
+ const replyHeaders = {
18
+ 'content-type': 'application/json',
19
+ };
18
20
 
19
21
  // Right now, none of these tests care what's on incomingRequestContext, so we
20
22
  // pass this fake one in.
@@ -32,7 +34,9 @@ describe('constructing requests', () => {
32
34
  apq: false,
33
35
  });
34
36
 
35
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
37
+ nock('https://api.example.com')
38
+ .post('/foo', { query: '{ me { name } }' })
39
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
36
40
 
37
41
  const { data } = await DataSource.process({
38
42
  ...defaultProcessOptions,
@@ -40,10 +44,6 @@ describe('constructing requests', () => {
40
44
  });
41
45
 
42
46
  expect(data).toEqual({ me: 'james' });
43
- expect(fetch).toBeCalledTimes(1);
44
- expect(fetch).toHaveFetched('https://api.example.com/foo', {
45
- body: { query: '{ me { name } }' },
46
- });
47
47
  });
48
48
 
49
49
  it('passes variables', async () => {
@@ -52,7 +52,9 @@ describe('constructing requests', () => {
52
52
  apq: false,
53
53
  });
54
54
 
55
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
55
+ nock('https://api.example.com')
56
+ .post('/foo', { query: '{ me { name } }', variables: { id: '1' } })
57
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
56
58
 
57
59
  const { data } = await DataSource.process({
58
60
  ...defaultProcessOptions,
@@ -63,10 +65,6 @@ describe('constructing requests', () => {
63
65
  });
64
66
 
65
67
  expect(data).toEqual({ me: 'james' });
66
- expect(fetch).toBeCalledTimes(1);
67
- expect(fetch).toHaveFetched('https://api.example.com/foo', {
68
- body: { query: '{ me { name } }', variables: { id: '1' } },
69
- });
70
68
  });
71
69
  });
72
70
 
@@ -101,28 +99,18 @@ describe('constructing requests', () => {
101
99
  apq: true,
102
100
  });
103
101
 
104
- fetch.mockJSONResponseOnce(apqNotFoundResponse);
105
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
106
-
107
- const { data } = await DataSource.process({
108
- ...defaultProcessOptions,
109
- request: { query },
110
- });
111
-
112
- expect(data).toEqual({ me: 'james' });
113
- expect(fetch).toBeCalledTimes(2);
114
- expect(fetch).toHaveFetchedNth(1, 'https://api.example.com/foo', {
115
- body: {
102
+ nock('https://api.example.com')
103
+ .post('/foo', {
116
104
  extensions: {
117
105
  persistedQuery: {
118
106
  version: 1,
119
107
  sha256Hash,
120
108
  },
121
109
  },
122
- },
123
- });
124
- expect(fetch).toHaveFetchedNth(2, 'https://api.example.com/foo', {
125
- body: {
110
+ })
111
+ .reply(200, apqNotFoundResponse, replyHeaders);
112
+ nock('https://api.example.com')
113
+ .post('/foo', {
126
114
  query,
127
115
  extensions: {
128
116
  persistedQuery: {
@@ -130,8 +118,15 @@ describe('constructing requests', () => {
130
118
  sha256Hash,
131
119
  },
132
120
  },
133
- },
121
+ })
122
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
123
+
124
+ const { data } = await DataSource.process({
125
+ ...defaultProcessOptions,
126
+ request: { query },
134
127
  });
128
+
129
+ expect(data).toEqual({ me: 'james' });
135
130
  });
136
131
 
137
132
  it('passes variables', async () => {
@@ -140,21 +135,8 @@ describe('constructing requests', () => {
140
135
  apq: true,
141
136
  });
142
137
 
143
- fetch.mockJSONResponseOnce(apqNotFoundResponse);
144
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
145
-
146
- const { data } = await DataSource.process({
147
- ...defaultProcessOptions,
148
- request: {
149
- query,
150
- variables: { id: '1' },
151
- },
152
- });
153
-
154
- expect(data).toEqual({ me: 'james' });
155
- expect(fetch).toBeCalledTimes(2);
156
- expect(fetch).toHaveFetchedNth(1, 'https://api.example.com/foo', {
157
- body: {
138
+ nock('https://api.example.com')
139
+ .post('/foo', {
158
140
  variables: { id: '1' },
159
141
  extensions: {
160
142
  persistedQuery: {
@@ -162,10 +144,10 @@ describe('constructing requests', () => {
162
144
  sha256Hash,
163
145
  },
164
146
  },
165
- },
166
- });
167
- expect(fetch).toHaveFetchedNth(2, 'https://api.example.com/foo', {
168
- body: {
147
+ })
148
+ .reply(200, apqNotFoundResponse, replyHeaders);
149
+ nock('https://api.example.com')
150
+ .post('/foo', {
169
151
  query,
170
152
  variables: { id: '1' },
171
153
  extensions: {
@@ -174,8 +156,18 @@ describe('constructing requests', () => {
174
156
  sha256Hash,
175
157
  },
176
158
  },
159
+ })
160
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
161
+
162
+ const { data } = await DataSource.process({
163
+ ...defaultProcessOptions,
164
+ request: {
165
+ query,
166
+ variables: { id: '1' },
177
167
  },
178
168
  });
169
+
170
+ expect(data).toEqual({ me: 'james' });
179
171
  });
180
172
  });
181
173
 
@@ -186,25 +178,23 @@ describe('constructing requests', () => {
186
178
  apq: true,
187
179
  });
188
180
 
189
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
190
-
191
- const { data } = await DataSource.process({
192
- ...defaultProcessOptions,
193
- request: { query },
194
- });
195
-
196
- expect(data).toEqual({ me: 'james' });
197
- expect(fetch).toBeCalledTimes(1);
198
- expect(fetch).toHaveFetched('https://api.example.com/foo', {
199
- body: {
181
+ nock('https://api.example.com')
182
+ .post('/foo', {
200
183
  extensions: {
201
184
  persistedQuery: {
202
185
  version: 1,
203
186
  sha256Hash,
204
187
  },
205
188
  },
206
- },
189
+ })
190
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
191
+
192
+ const { data } = await DataSource.process({
193
+ ...defaultProcessOptions,
194
+ request: { query },
207
195
  });
196
+
197
+ expect(data).toEqual({ me: 'james' });
208
198
  });
209
199
 
210
200
  it('passes variables', async () => {
@@ -213,7 +203,17 @@ describe('constructing requests', () => {
213
203
  apq: true,
214
204
  });
215
205
 
216
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
206
+ nock('https://api.example.com')
207
+ .post('/foo', {
208
+ variables: { id: '1' },
209
+ extensions: {
210
+ persistedQuery: {
211
+ version: 1,
212
+ sha256Hash,
213
+ },
214
+ },
215
+ })
216
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
217
217
 
218
218
  const { data } = await DataSource.process({
219
219
  ...defaultProcessOptions,
@@ -224,52 +224,20 @@ describe('constructing requests', () => {
224
224
  });
225
225
 
226
226
  expect(data).toEqual({ me: 'james' });
227
- expect(fetch).toBeCalledTimes(1);
228
- expect(fetch).toHaveFetched('https://api.example.com/foo', {
229
- body: {
230
- variables: { id: '1' },
231
- extensions: {
232
- persistedQuery: {
233
- version: 1,
234
- sha256Hash,
235
- },
236
- },
237
- },
238
- });
239
227
  });
240
228
  });
241
229
  });
242
230
  });
243
231
 
244
232
  describe('fetcher', () => {
245
- it('uses a custom provided `fetcher`', async () => {
246
- const injectedFetch = fetch.mockJSONResponseOnce({
247
- data: { injected: true },
248
- });
249
- const DataSource = new RemoteGraphQLDataSource({
250
- url: 'https://api.example.com/foo',
251
- fetcher: injectedFetch,
252
- });
253
-
254
- const { data } = await DataSource.process({
255
- ...defaultProcessOptions,
256
- request: {
257
- query: '{ me { name } }',
258
- variables: { id: '1' },
259
- },
260
- });
261
-
262
- expect(injectedFetch).toHaveBeenCalled();
263
- expect(data).toEqual({ injected: true });
264
- });
265
-
266
- it('supports a custom fetcher, like `node-fetch`', async () => {
267
- const injectedFetch = customFetcher.mockJSONResponseOnce({
268
- data: { me: 'james' },
269
- });
233
+ it('supports a custom fetcher', async () => {
270
234
  const DataSource = new RemoteGraphQLDataSource({
271
235
  url: 'https://api.example.com/foo',
272
- fetcher: injectedFetch,
236
+ fetcher: async () =>
237
+ new Response(JSON.stringify({ data: { me: 'james' } }), {
238
+ status: 200,
239
+ headers: { 'content-type': 'application/json' },
240
+ }),
273
241
  });
274
242
 
275
243
  const { data } = await DataSource.process({
@@ -280,7 +248,6 @@ describe('fetcher', () => {
280
248
  },
281
249
  });
282
250
 
283
- expect(injectedFetch).toHaveBeenCalled();
284
251
  expect(data).toEqual({ me: 'james' });
285
252
  });
286
253
  });
@@ -294,7 +261,9 @@ describe('willSendRequest', () => {
294
261
  },
295
262
  });
296
263
 
297
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
264
+ nock('https://api.example.com')
265
+ .post('/foo', { query: '{ me { name } }', variables: { id: '2' } })
266
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
298
267
 
299
268
  const { data } = await DataSource.process({
300
269
  ...defaultProcessOptions,
@@ -305,12 +274,6 @@ describe('willSendRequest', () => {
305
274
  });
306
275
 
307
276
  expect(data).toEqual({ me: 'james' });
308
- expect(fetch).toHaveFetched('https://api.example.com/foo', {
309
- body: {
310
- query: '{ me { name } }',
311
- variables: { id: '2' },
312
- },
313
- });
314
277
  });
315
278
 
316
279
  it('accepts context', async () => {
@@ -326,7 +289,11 @@ describe('willSendRequest', () => {
326
289
  },
327
290
  });
328
291
 
329
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
292
+ nock('https://api.example.com', {
293
+ reqheaders: { 'x-user-id': '1234' },
294
+ })
295
+ .post('/foo', { query: '{ me { name } }', variables: { id: '1' } })
296
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
330
297
 
331
298
  const { data } = await DataSource.process({
332
299
  ...defaultProcessOptions,
@@ -338,15 +305,6 @@ describe('willSendRequest', () => {
338
305
  });
339
306
 
340
307
  expect(data).toEqual({ me: 'james' });
341
- expect(fetch).toHaveFetched('https://api.example.com/foo', {
342
- body: {
343
- query: '{ me { name } }',
344
- variables: { id: '1' },
345
- },
346
- headers: {
347
- 'x-user-id': '1234',
348
- },
349
- });
350
308
  });
351
309
  });
352
310
 
@@ -379,7 +337,9 @@ describe('didReceiveResponse', () => {
379
337
 
380
338
  const DataSource = new MyDataSource();
381
339
 
382
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
340
+ nock('https://api.example.com')
341
+ .post('/foo', { query: '{ me { name } }', variables: { id: '1' } })
342
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
383
343
 
384
344
  const context: MyContext = { surrogateKeys: [] };
385
345
  await DataSource.process({
@@ -418,7 +378,9 @@ describe('didReceiveResponse', () => {
418
378
  const DataSource = new MyDataSource();
419
379
  const spyDidReceiveResponse = jest.spyOn(DataSource, 'didReceiveResponse');
420
380
 
421
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
381
+ nock('https://api.example.com')
382
+ .post('/foo', { query: '{ me { name } }', variables: { id: '1' } })
383
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
422
384
 
423
385
  await DataSource.process({
424
386
  ...defaultProcessOptions,
@@ -452,7 +414,9 @@ describe('didReceiveResponse', () => {
452
414
  const DataSource = new MyDataSource();
453
415
  const spyDidReceiveResponse = jest.spyOn(DataSource, 'didReceiveResponse');
454
416
 
455
- fetch.mockJSONResponseOnce({ data: { me: 'james' } });
417
+ nock('https://api.example.com')
418
+ .post('/foo')
419
+ .reply(200, { data: { me: 'james' } }, replyHeaders);
456
420
 
457
421
  await DataSource.process({
458
422
  ...defaultProcessOptions,
@@ -483,7 +447,7 @@ describe('didEncounterError', () => {
483
447
 
484
448
  const DataSource = new MyDataSource();
485
449
 
486
- fetch.mockResponseOnce('Invalid token', undefined, 401);
450
+ nock('https://api.example.com').post('/foo').reply(401, 'Invalid token');
487
451
 
488
452
  const context: MyContext = { timingData: [] };
489
453
  const result = DataSource.process({
@@ -510,7 +474,7 @@ describe('error handling', () => {
510
474
  url: 'https://api.example.com/foo',
511
475
  });
512
476
 
513
- fetch.mockResponseOnce('Invalid token', undefined, 401);
477
+ nock('https://api.example.com').post('/foo').reply(401, 'Invalid token');
514
478
 
515
479
  const result = DataSource.process({
516
480
  ...defaultProcessOptions,
@@ -533,7 +497,7 @@ describe('error handling', () => {
533
497
  url: 'https://api.example.com/foo',
534
498
  });
535
499
 
536
- fetch.mockResponseOnce('No access', undefined, 403);
500
+ nock('https://api.example.com').post('/foo').reply(403, 'No access');
537
501
 
538
502
  const result = DataSource.process({
539
503
  ...defaultProcessOptions,
@@ -556,7 +520,7 @@ describe('error handling', () => {
556
520
  url: 'https://api.example.com/foo',
557
521
  });
558
522
 
559
- fetch.mockResponseOnce('Oops', undefined, 500);
523
+ nock('https://api.example.com').post('/foo').reply(500, 'Oops');
560
524
 
561
525
  const result = DataSource.process({
562
526
  ...defaultProcessOptions,
@@ -578,17 +542,19 @@ describe('error handling', () => {
578
542
  url: 'https://api.example.com/foo',
579
543
  });
580
544
 
581
- fetch.mockResponseOnce(
582
- JSON.stringify({
583
- errors: [
584
- {
585
- message: 'Houston, we have a problem.',
586
- },
587
- ],
588
- }),
589
- { 'Content-Type': 'application/json' },
590
- 500,
591
- );
545
+ nock('https://api.example.com')
546
+ .post('/foo')
547
+ .reply(
548
+ 500,
549
+ {
550
+ errors: [
551
+ {
552
+ message: 'Houston, we have a problem.',
553
+ },
554
+ ],
555
+ },
556
+ { 'Content-Type': 'application/json' },
557
+ );
592
558
 
593
559
  const result = DataSource.process({
594
560
  ...defaultProcessOptions,
@@ -2,7 +2,7 @@ import {
2
2
  GraphQLExecutionResult,
3
3
  GraphQLRequestContext,
4
4
  } from 'apollo-server-types';
5
- import { Headers } from 'apollo-server-env';
5
+ import { Headers } from 'node-fetch';
6
6
  import {
7
7
  execute,
8
8
  GraphQLError,
@@ -380,6 +380,10 @@ async function executeFetch<TContext>(
380
380
  ): Promise<ResultMap | void | null> {
381
381
  // We declare this as 'any' because it is missing url and method, which
382
382
  // GraphQLRequest.http is supposed to have if it exists.
383
+ // (This is admittedly kinda weird, since we currently do pass url and
384
+ // method to `process` from the SDL fetching call site, but presumably
385
+ // existing implementation of the interface don't try to look for these
386
+ // fields. RemoteGraphQLDataSource just overwrites them.)
383
387
  let http: any;
384
388
 
385
389
  // If we're capturing a trace for Studio, then save the operation text to
package/src/index.ts CHANGED
@@ -4,6 +4,7 @@ import {
4
4
  GraphQLExecutionResult,
5
5
  GraphQLRequestContextExecutionDidStart,
6
6
  } from 'apollo-server-types';
7
+ import { createHash } from '@apollo/utils.createhash';
7
8
  import type { Logger } from '@apollo/utils.logger';
8
9
  import { InMemoryLRUCache } from 'apollo-server-caching';
9
10
  import {
@@ -26,8 +27,6 @@ import {
26
27
  import { RemoteGraphQLDataSource } from './datasources/RemoteGraphQLDataSource';
27
28
  import { getVariableValues } from 'graphql/execution/values';
28
29
  import fetcher from 'make-fetch-happen';
29
- import { HttpRequestCache } from './cache';
30
- import { fetch } from 'apollo-server-env';
31
30
  import {
32
31
  QueryPlanner,
33
32
  QueryPlan,
@@ -53,14 +52,20 @@ import {
53
52
  } from './config';
54
53
  import { SpanStatusCode } from '@opentelemetry/api';
55
54
  import { OpenTelemetrySpanNames, tracer } from './utilities/opentelemetry';
56
- import { createHash } from './utilities/createHash';
55
+ import { addExtensions } from './schema-helper/addExtensions';
57
56
  import {
58
57
  IntrospectAndCompose,
59
58
  UplinkFetcher,
60
59
  LegacyFetcher,
61
60
  LocalCompose,
62
61
  } from './supergraphManagers';
63
- import { buildSupergraphSchema, operationFromDocument, Schema, ServiceDefinition } from '@apollo/federation-internals';
62
+ import {
63
+ buildSupergraphSchema,
64
+ operationFromDocument,
65
+ Schema,
66
+ ServiceDefinition,
67
+ } from '@apollo/federation-internals';
68
+ import { Fetcher } from '@apollo/utils.fetcher';
64
69
 
65
70
  type DataSourceMap = {
66
71
  [serviceName: string]: { url?: string; dataSource: GraphQLDataSource };
@@ -75,30 +80,6 @@ type WarnedStates = {
75
80
  remoteWithLocalConfig?: boolean;
76
81
  };
77
82
 
78
- export function getDefaultFetcher() {
79
- const { name, version } = require('../package.json');
80
- return fetcher.defaults({
81
- cacheManager: new HttpRequestCache(),
82
- // All headers should be lower-cased here, as `make-fetch-happen`
83
- // treats differently cased headers as unique (unlike the `Headers` object).
84
- // @see: https://git.io/JvRUa
85
- headers: {
86
- 'apollographql-client-name': name,
87
- 'apollographql-client-version': version,
88
- 'user-agent': `${name}/${version}`,
89
- 'content-type': 'application/json',
90
- },
91
- retry: {
92
- retries: 5,
93
- // The default factor: expected attempts at 0, 1, 3, 7, 15, and 31 seconds elapsed
94
- factor: 2,
95
- // 1 second
96
- minTimeout: 1000,
97
- randomize: true,
98
- },
99
- });
100
- }
101
-
102
83
  export const HEALTH_CHECK_QUERY =
103
84
  'query __ApolloServiceHealthCheck__ { __typename }';
104
85
  export const SERVICE_DEFINITION_QUERY =
@@ -164,7 +145,7 @@ export class ApolloGateway implements GraphQLService {
164
145
  private warnedStates: WarnedStates = Object.create(null);
165
146
  private queryPlanner?: QueryPlanner;
166
147
  private supergraphSdl?: string;
167
- private fetcher: typeof fetch;
148
+ private fetcher: Fetcher;
168
149
  private compositionId?: string;
169
150
  private state: GatewayState;
170
151
 
@@ -192,7 +173,7 @@ export class ApolloGateway implements GraphQLService {
192
173
  this.queryPlanStore = this.initQueryPlanStore(
193
174
  config?.experimental_approximateQueryPlanStoreMiB,
194
175
  );
195
- this.fetcher = config?.fetcher || getDefaultFetcher();
176
+ this.fetcher = config?.fetcher || fetcher;
196
177
 
197
178
  // set up experimental observability callbacks and config settings
198
179
  this.experimental_didResolveQueryPlan =
@@ -426,6 +407,8 @@ export class ApolloGateway implements GraphQLService {
426
407
  }`,
427
408
  );
428
409
 
410
+ addExtensions(this.schema!);
411
+
429
412
  return {
430
413
  schema: this.schema!,
431
414
  executor: this.executor,
@@ -635,8 +618,8 @@ export class ApolloGateway implements GraphQLService {
635
618
  ): void {
636
619
  if (this.queryPlanStore) this.queryPlanStore.flush();
637
620
  this.apiSchema = coreSchema.toAPISchema();
638
- this.schema = wrapSchemaWithAliasResolver(
639
- this.apiSchema.toGraphQLJSSchema(),
621
+ this.schema = addExtensions(
622
+ wrapSchemaWithAliasResolver(this.apiSchema.toGraphQLJSSchema()),
640
623
  );
641
624
  this.queryPlanner = new QueryPlanner(coreSchema);
642
625
 
@@ -0,0 +1,11 @@
1
+ import { buildSchema } from 'graphql';
2
+ import { addExtensions } from '../addExtensions';
3
+ const { version } = require('../../../package.json');
4
+
5
+ describe('addExtensions', () => {
6
+ it('adds gateway extensions to a schema', async () => {
7
+ const schema = buildSchema('type Query { hello: String }');
8
+ expect(schema.extensions).toEqual({});
9
+ expect(addExtensions(schema).extensions).toEqual({ apollo: { gateway: { version: version } } });
10
+ });
11
+ });
@@ -0,0 +1,17 @@
1
+ import { GraphQLSchema } from 'graphql';
2
+ const { version } = require('../../package.json');
3
+
4
+ export function addExtensions(schema: GraphQLSchema): GraphQLSchema {
5
+ schema.extensions = {
6
+ ...schema.extensions,
7
+ apollo: {
8
+ ...schema.extensions.apollo,
9
+ gateway: {
10
+ ...schema.extensions.apollo?.gateway,
11
+ version,
12
+ }
13
+ },
14
+ };
15
+
16
+ return schema;
17
+ }
@@ -119,11 +119,6 @@ describe('IntrospectAndCompose', () => {
119
119
 
120
120
  // TODO: useFakeTimers (though I'm struggling to get this to work as expected)
121
121
  it("doesn't call `update` when there's no change to the supergraph", async () => {
122
- const fetcher =
123
- jest.requireActual<typeof import('apollo-server-env')>(
124
- 'apollo-server-env',
125
- ).fetch;
126
-
127
122
  // mock for initial load and a few polls against an unchanging schema
128
123
  mockAllServicesSdlQuerySuccess();
129
124
  mockAllServicesSdlQuerySuccess();
@@ -144,7 +139,6 @@ describe('IntrospectAndCompose', () => {
144
139
  getDataSource({ url }) {
145
140
  return new RemoteGraphQLDataSource({
146
141
  url,
147
- fetcher,
148
142
  });
149
143
  },
150
144
  });