@apollo/gateway 2.0.0-alpha.1 → 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 (61) hide show
  1. package/README.md +1 -1
  2. package/dist/__generated__/graphqlTypes.d.ts +13 -11
  3. package/dist/__generated__/graphqlTypes.d.ts.map +1 -1
  4. package/dist/__generated__/graphqlTypes.js.map +1 -1
  5. package/dist/config.d.ts +2 -9
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +6 -17
  8. package/dist/config.js.map +1 -1
  9. package/dist/datasources/types.d.ts +1 -1
  10. package/dist/datasources/types.d.ts.map +1 -1
  11. package/dist/executeQueryPlan.d.ts.map +1 -1
  12. package/dist/executeQueryPlan.js +4 -4
  13. package/dist/executeQueryPlan.js.map +1 -1
  14. package/dist/index.d.ts +1 -2
  15. package/dist/index.d.ts.map +1 -1
  16. package/dist/index.js +14 -28
  17. package/dist/index.js.map +1 -1
  18. package/dist/loadSupergraphSdlFromStorage.d.ts +4 -3
  19. package/dist/loadSupergraphSdlFromStorage.d.ts.map +1 -1
  20. package/dist/loadSupergraphSdlFromStorage.js +7 -3
  21. package/dist/loadSupergraphSdlFromStorage.js.map +1 -1
  22. package/dist/operationContext.js +0 -1
  23. package/dist/operationContext.js.map +1 -1
  24. package/dist/utilities/array.js +1 -1
  25. package/dist/utilities/array.js.map +1 -1
  26. package/package.json +8 -9
  27. package/src/__generated__/graphqlTypes.ts +13 -11
  28. package/src/__tests__/buildQueryPlan.test.ts +1 -1
  29. package/src/__tests__/executeQueryPlan.test.ts +572 -76
  30. package/src/__tests__/execution-utils.ts +2 -4
  31. package/src/__tests__/gateway/composedSdl.test.ts +1 -1
  32. package/src/__tests__/gateway/executor.test.ts +2 -0
  33. package/src/__tests__/gateway/lifecycle-hooks.test.ts +8 -4
  34. package/src/__tests__/gateway/opentelemetry.test.ts +1 -0
  35. package/src/__tests__/gateway/queryPlanCache.test.ts +3 -0
  36. package/src/__tests__/integration/aliases.test.ts +1 -0
  37. package/src/__tests__/integration/configuration.test.ts +3 -21
  38. package/src/__tests__/integration/logger.test.ts +1 -1
  39. package/src/__tests__/integration/networkRequests.test.ts +53 -28
  40. package/src/__tests__/integration/nockMocks.ts +33 -5
  41. package/src/__tests__/loadServicesFromRemoteEndpoint.test.ts +2 -2
  42. package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +36 -6
  43. package/src/config.ts +8 -43
  44. package/src/core/__tests__/core.test.ts +6 -6
  45. package/src/datasources/types.ts +1 -1
  46. package/src/executeQueryPlan.ts +4 -7
  47. package/src/index.ts +22 -50
  48. package/src/loadServicesFromRemoteEndpoint.ts +1 -1
  49. package/src/loadSupergraphSdlFromStorage.ts +7 -4
  50. package/src/make-fetch-happen.d.ts +1 -1
  51. package/src/operationContext.ts +2 -2
  52. package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +1 -1
  53. package/src/utilities/array.ts +1 -1
  54. package/CHANGELOG.md +0 -452
  55. package/dist/legacyLoadServicesFromStorage.d.ts +0 -20
  56. package/dist/legacyLoadServicesFromStorage.d.ts.map +0 -1
  57. package/dist/legacyLoadServicesFromStorage.js +0 -62
  58. package/dist/legacyLoadServicesFromStorage.js.map +0 -1
  59. package/src/__tests__/integration/legacyNetworkRequests.test.ts +0 -279
  60. package/src/__tests__/integration/legacyNockMocks.ts +0 -113
  61. package/src/legacyLoadServicesFromStorage.ts +0 -170
@@ -4,9 +4,6 @@ import {
4
4
  GraphQLResolverMap,
5
5
  } from 'apollo-graphql';
6
6
  import { GraphQLRequest, GraphQLExecutionResult, Logger } from 'apollo-server-types';
7
- import {
8
- ServiceDefinition,
9
- } from '@apollo/federation';
10
7
  import { buildSubgraphSchema } from '@apollo/subgraph';
11
8
  import {
12
9
  executeQueryPlan,
@@ -20,7 +17,7 @@ import { queryPlanSerializer, astSerializer } from 'apollo-federation-integratio
20
17
  import gql from 'graphql-tag';
21
18
  import { fixtures } from 'apollo-federation-integration-testsuite';
22
19
  import { composeServices } from '@apollo/composition';
23
- import { buildSchema, operationFromDocument } from '@apollo/federation-internals';
20
+ import { buildSchema, operationFromDocument, ServiceDefinition } from '@apollo/federation-internals';
24
21
 
25
22
  const prettyFormat = require('pretty-format');
26
23
 
@@ -67,6 +64,7 @@ export async function execute(
67
64
  const result = await executeQueryPlan(
68
65
  queryPlan,
69
66
  serviceMap,
67
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
70
68
  // @ts-ignore
71
69
  {
72
70
  cache: undefined as any,
@@ -35,7 +35,7 @@ describe('Using supergraphSdl configuration', () => {
35
35
  `);
36
36
 
37
37
  const [url, request] = fetch.mock.calls[0];
38
- expect(url).toEqual('https://accounts.api.com');
38
+ expect(url).toEqual('https://accounts.api.com.invalid');
39
39
  expect(request?.body).toEqual(
40
40
  JSON.stringify({ query: '{me{username}}', variables: {} }),
41
41
  );
@@ -38,6 +38,7 @@ describe('ApolloGateway executor', () => {
38
38
  }
39
39
  `;
40
40
 
41
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
41
42
  // @ts-ignore
42
43
  const { errors } = await executor({
43
44
  source,
@@ -73,6 +74,7 @@ describe('ApolloGateway executor', () => {
73
74
  }
74
75
  `;
75
76
 
77
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
76
78
  // @ts-ignore
77
79
  const { errors, data } = await executor({
78
80
  source,
@@ -14,6 +14,8 @@ import {
14
14
  } from 'apollo-federation-integration-testsuite';
15
15
  import { Logger } from 'apollo-server-types';
16
16
 
17
+ type GenericFunction = (...args: unknown[]) => unknown;
18
+
17
19
  // The order of this was specified to preserve existing test coverage. Typically
18
20
  // we would just import and use the `fixtures` array.
19
21
  const serviceDefinitions = [
@@ -115,7 +117,7 @@ describe('lifecycle hooks', () => {
115
117
  const callbackArgs = experimental_didFailComposition.mock.calls[0][0];
116
118
  expect(callbackArgs.serviceList).toHaveLength(2);
117
119
  expect(callbackArgs.errors[0]).toMatchInlineSnapshot(
118
- `[GraphQLError: Field "T.a" has incompatible types accross subgraphs: it has type "Int" in subgraph "S1" but type "String" in subgraph "S2"]`,
120
+ `[GraphQLError: Field "T.a" has incompatible types across subgraphs: it has type "Int" in subgraph "S1" but type "String" in subgraph "S2"]`,
119
121
  );
120
122
  expect(callbackArgs.compositionMetadata.id).toEqual('abc');
121
123
  expect(experimental_didFailComposition).toBeCalled();
@@ -167,11 +169,12 @@ describe('lifecycle hooks', () => {
167
169
  experimental_didUpdateComposition: mockDidUpdate,
168
170
  logger,
169
171
  });
172
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
170
173
  // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
171
174
  gateway.experimental_pollInterval = 100;
172
175
 
173
- let resolve1: Function;
174
- let resolve2: Function;
176
+ let resolve1: GenericFunction;
177
+ let resolve2: GenericFunction;
175
178
  const schemaChangeBlocker1 = new Promise(res => (resolve1 = res));
176
179
  const schemaChangeBlocker2 = new Promise(res => (resolve2 = res));
177
180
 
@@ -251,7 +254,7 @@ describe('lifecycle hooks', () => {
251
254
  logger,
252
255
  });
253
256
 
254
- let resolve: Function;
257
+ let resolve: GenericFunction;
255
258
  const schemaChangeBlocker = new Promise(res => (resolve = res));
256
259
  const schemaChangeCallback = jest.fn(() => resolve());
257
260
 
@@ -280,6 +283,7 @@ describe('lifecycle hooks', () => {
280
283
  { book(isbn: "0262510871") { year } }
281
284
  `;
282
285
 
286
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
283
287
  // @ts-ignore
284
288
  await executor({
285
289
  source,
@@ -39,6 +39,7 @@ describe('opentelemetry', () => {
39
39
  const gateway = new ApolloGateway({
40
40
  localServiceList: fixtures,
41
41
  buildService: service => {
42
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
42
43
  // @ts-ignore
43
44
  return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
44
45
  },
@@ -12,6 +12,7 @@ it('caches the query plan for a request', async () => {
12
12
  const gateway = new ApolloGateway({
13
13
  localServiceList: fixtures,
14
14
  buildService: service => {
15
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
15
16
  // @ts-ignore
16
17
  return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
17
18
  },
@@ -68,6 +69,7 @@ it('supports multiple operations and operationName', async () => {
68
69
  const gateway = new ApolloGateway({
69
70
  localServiceList: fixtures,
70
71
  buildService: service => {
72
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
71
73
  // @ts-ignore
72
74
  return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
73
75
  },
@@ -173,6 +175,7 @@ it('does not corrupt cached queryplan data across requests', async () => {
173
175
  const gateway = new ApolloGateway({
174
176
  localServiceList: [serviceA, serviceB],
175
177
  buildService: service => {
178
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
176
179
  // @ts-ignore
177
180
  return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
178
181
  },
@@ -144,6 +144,7 @@ it('supports aliases when using ApolloServer', async () => {
144
144
  const gateway = new ApolloGateway({
145
145
  localServiceList: fixtures,
146
146
  buildService: service => {
147
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
147
148
  // @ts-ignore
148
149
  return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
149
150
  },
@@ -70,9 +70,10 @@ describe('gateway configuration warnings', () => {
70
70
 
71
71
  it('warns when both manual update configurations are provided', async () => {
72
72
  gateway = new ApolloGateway({
73
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
73
74
  // @ts-ignore
74
- async experimental_updateSupergraphSdl() {},
75
- async experimental_updateServiceDefinitions() {},
75
+ experimental_updateSupergraphSdl: async () => undefined,
76
+ experimental_updateServiceDefinitions: async () => undefined,
76
77
  logger,
77
78
  });
78
79
 
@@ -110,7 +111,6 @@ describe('gateway configuration warnings', () => {
110
111
 
111
112
  gateway = new ApolloGateway({
112
113
  logger,
113
- // TODO(trevor:cloudconfig): remove
114
114
  schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
115
115
  });
116
116
 
@@ -300,7 +300,6 @@ describe('gateway config / env behavior', () => {
300
300
  });
301
301
  });
302
302
 
303
- // TODO(trevor:cloudconfig): this behavior will be updated
304
303
  describe('schema config delivery endpoint configuration', () => {
305
304
  it('A code config overrides the env variable', async () => {
306
305
  cleanUp = mockedEnv({
@@ -318,22 +317,5 @@ describe('gateway config / env behavior', () => {
318
317
 
319
318
  gateway = null;
320
319
  });
321
-
322
- it('A code config set to `null` takes precedence over an existing env variable', async () => {
323
- cleanUp = mockedEnv({
324
- APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT: 'env-config',
325
- });
326
-
327
- gateway = new ApolloGateway({
328
- logger,
329
- schemaConfigDeliveryEndpoint: null,
330
- });
331
-
332
- expect(gateway['schemaConfigDeliveryEndpoint']).toEqual(
333
- null,
334
- );
335
-
336
- gateway = null;
337
- });
338
320
  });
339
321
  });
@@ -18,7 +18,7 @@ async function triggerKnownDebugMessage(logger: Logger) {
18
18
  // message outside of the constructor, but it seemed worth testing
19
19
  // the compatibility with `ApolloGateway` itself rather than generically.
20
20
  // The error does not matter, so it is caught and ignored.
21
- await new ApolloGateway({ logger }).load().catch(_e => {});
21
+ await new ApolloGateway({ logger }).load().catch(_e => undefined);
22
22
  }
23
23
 
24
24
  describe("logger", () => {
@@ -13,6 +13,8 @@ import {
13
13
  mockSupergraphSdlRequest,
14
14
  mockApolloConfig,
15
15
  mockCloudConfigUrl,
16
+ mockSupergraphSdlRequestIfAfter,
17
+ mockSupergraphSdlRequestSuccessIfAfter,
16
18
  } from './nockMocks';
17
19
  import {
18
20
  accounts,
@@ -25,6 +27,7 @@ import {
25
27
  } from 'apollo-federation-integration-testsuite';
26
28
  import { getTestingSupergraphSdl } from '../execution-utils';
27
29
 
30
+ type GenericFunction = (...args: unknown[]) => unknown;
28
31
  export interface MockService {
29
32
  name: string;
30
33
  url: string;
@@ -98,7 +101,6 @@ it('Queries remote endpoints for their SDLs', async () => {
98
101
  expect(gateway.schema!.getType('User')!.description).toBe('This is my User');
99
102
  });
100
103
 
101
- // TODO(trevor:cloudconfig): Remove all usages of the experimental config option
102
104
  it('Fetches Supergraph SDL from remote storage', async () => {
103
105
  mockSupergraphSdlRequestSuccess();
104
106
 
@@ -112,7 +114,6 @@ it('Fetches Supergraph SDL from remote storage', async () => {
112
114
  expect(gateway.schema?.getType('User')).toBeTruthy();
113
115
  });
114
116
 
115
- // TODO(trevor:cloudconfig): This test should evolve to demonstrate overriding the default in the future
116
117
  it('Fetches Supergraph SDL from remote storage using a configured env variable', async () => {
117
118
  cleanUp = mockedEnv({
118
119
  APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT: mockCloudConfigUrl,
@@ -130,15 +131,19 @@ it('Fetches Supergraph SDL from remote storage using a configured env variable',
130
131
 
131
132
  it('Updates Supergraph SDL from remote storage', async () => {
132
133
  mockSupergraphSdlRequestSuccess();
133
- mockSupergraphSdlRequestSuccess(getTestingSupergraphSdl(fixturesWithUpdate), 'updatedId-5678');
134
+ mockSupergraphSdlRequestSuccessIfAfter(
135
+ 'originalId-1234',
136
+ 'updatedId-5678',
137
+ getTestingSupergraphSdl(fixturesWithUpdate),
138
+ );
134
139
 
135
140
  // This test is only interested in the second time the gateway notifies of an
136
141
  // update, since the first happens on load.
137
- let secondUpdateResolve: Function;
142
+ let secondUpdateResolve: GenericFunction;
138
143
  const secondUpdate = new Promise((res) => (secondUpdateResolve = res));
139
144
  const schemaChangeCallback = jest
140
145
  .fn()
141
- .mockImplementationOnce(() => {})
146
+ .mockImplementationOnce(() => undefined)
142
147
  .mockImplementationOnce(() => {
143
148
  secondUpdateResolve();
144
149
  });
@@ -147,9 +152,10 @@ it('Updates Supergraph SDL from remote storage', async () => {
147
152
  logger,
148
153
  schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
149
154
  });
155
+ // eslint-disable-next-line
150
156
  // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
151
157
  gateway.experimental_pollInterval = 100;
152
- gateway.onSchemaChange(schemaChangeCallback);
158
+ gateway.onSchemaLoadOrUpdate(schemaChangeCallback);
153
159
 
154
160
  await gateway.load(mockApolloConfig);
155
161
  expect(gateway['compositionId']).toMatchInlineSnapshot(`"originalId-1234"`);
@@ -183,10 +189,10 @@ describe('Supergraph SDL update failures', () => {
183
189
 
184
190
  it('Handles arbitrary fetch failures (non 200 response)', async () => {
185
191
  mockSupergraphSdlRequestSuccess();
186
- mockSupergraphSdlRequest().reply(500);
192
+ mockSupergraphSdlRequestIfAfter('originalId-1234').reply(500);
187
193
 
188
194
  // Spy on logger.error so we can just await once it's been called
189
- let errorLogged: Function;
195
+ let errorLogged: GenericFunction;
190
196
  const errorLoggedPromise = new Promise((r) => (errorLogged = r));
191
197
  logger.error = jest.fn(() => errorLogged());
192
198
 
@@ -195,6 +201,7 @@ describe('Supergraph SDL update failures', () => {
195
201
  schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
196
202
  });
197
203
 
204
+ // eslint-disable-next-line
198
205
  // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
199
206
  gateway.experimental_pollInterval = 100;
200
207
 
@@ -208,7 +215,7 @@ describe('Supergraph SDL update failures', () => {
208
215
 
209
216
  it('Handles GraphQL errors', async () => {
210
217
  mockSupergraphSdlRequestSuccess();
211
- mockSupergraphSdlRequest().reply(200, {
218
+ mockSupergraphSdlRequest('originalId-1234').reply(200, {
212
219
  errors: [
213
220
  {
214
221
  message: 'Cannot query field "fail" on type "Query".',
@@ -219,7 +226,7 @@ describe('Supergraph SDL update failures', () => {
219
226
  });
220
227
 
221
228
  // Spy on logger.error so we can just await once it's been called
222
- let errorLogged: Function;
229
+ let errorLogged: GenericFunction;
223
230
  const errorLoggedPromise = new Promise((r) => (errorLogged = r));
224
231
  logger.error = jest.fn(() => errorLogged());
225
232
 
@@ -227,6 +234,7 @@ describe('Supergraph SDL update failures', () => {
227
234
  logger,
228
235
  schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
229
236
  });
237
+ // eslint-disable-next-line
230
238
  // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
231
239
  gateway.experimental_pollInterval = 100;
232
240
 
@@ -242,7 +250,7 @@ describe('Supergraph SDL update failures', () => {
242
250
 
243
251
  it("Doesn't update and logs on receiving unparseable Supergraph SDL", async () => {
244
252
  mockSupergraphSdlRequestSuccess();
245
- mockSupergraphSdlRequest().reply(
253
+ mockSupergraphSdlRequestIfAfter('originalId-1234').reply(
246
254
  200,
247
255
  JSON.stringify({
248
256
  data: {
@@ -256,7 +264,7 @@ describe('Supergraph SDL update failures', () => {
256
264
  );
257
265
 
258
266
  // Spy on logger.error so we can just await once it's been called
259
- let errorLogged: Function;
267
+ let errorLogged: GenericFunction;
260
268
  const errorLoggedPromise = new Promise((r) => (errorLogged = r));
261
269
  logger.error = jest.fn(() => errorLogged());
262
270
 
@@ -264,6 +272,7 @@ describe('Supergraph SDL update failures', () => {
264
272
  logger,
265
273
  schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
266
274
  });
275
+ // eslint-disable-next-line
267
276
  // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
268
277
  gateway.experimental_pollInterval = 100;
269
278
 
@@ -294,6 +303,7 @@ describe('Supergraph SDL update failures', () => {
294
303
  logger,
295
304
  schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
296
305
  });
306
+ // eslint-disable-next-line
297
307
  // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
298
308
  gateway.experimental_pollInterval = 100;
299
309
 
@@ -314,12 +324,16 @@ describe('Supergraph SDL update failures', () => {
314
324
  it('Rollsback to a previous schema when triggered', async () => {
315
325
  // Init
316
326
  mockSupergraphSdlRequestSuccess();
317
- mockSupergraphSdlRequestSuccess(getTestingSupergraphSdl(fixturesWithUpdate), 'updatedId-5678');
318
- mockSupergraphSdlRequestSuccess();
327
+ mockSupergraphSdlRequestSuccessIfAfter(
328
+ 'originalId-1234',
329
+ 'updatedId-5678',
330
+ getTestingSupergraphSdl(fixturesWithUpdate),
331
+ );
332
+ mockSupergraphSdlRequestSuccessIfAfter('updatedId-5678');
319
333
 
320
- let firstResolve: Function;
321
- let secondResolve: Function;
322
- let thirdResolve: Function;
334
+ let firstResolve: GenericFunction;
335
+ let secondResolve: GenericFunction;
336
+ let thirdResolve: GenericFunction;
323
337
  const firstSchemaChangeBlocker = new Promise((res) => (firstResolve = res));
324
338
  const secondSchemaChangeBlocker = new Promise((res) => (secondResolve = res));
325
339
  const thirdSchemaChangeBlocker = new Promise((res) => (thirdResolve = res));
@@ -334,6 +348,7 @@ it('Rollsback to a previous schema when triggered', async () => {
334
348
  logger,
335
349
  schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
336
350
  });
351
+ // eslint-disable-next-line
337
352
  // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
338
353
  gateway.experimental_pollInterval = 100;
339
354
 
@@ -386,10 +401,11 @@ describe('Downstream service health checks', () => {
386
401
  // [accounts] Account -> A @key selects id, but Account.id could not be found"
387
402
  // `);
388
403
  // Instead we'll just use the regular snapshot matcher...
404
+ let err;
389
405
  try {
390
406
  await gateway.load(mockApolloConfig);
391
407
  } catch (e) {
392
- var err = e;
408
+ err = e;
393
409
  }
394
410
 
395
411
  // TODO: smell that we should be awaiting something else
@@ -418,6 +434,7 @@ describe('Downstream service health checks', () => {
418
434
  logger,
419
435
  schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
420
436
  });
437
+ // eslint-disable-next-line
421
438
  // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
422
439
  gateway.experimental_pollInterval = 100;
423
440
 
@@ -450,10 +467,11 @@ describe('Downstream service health checks', () => {
450
467
  // [accounts] Account -> A @key selects id, but Account.id could not be found"
451
468
  // `);
452
469
  // Instead we'll just use the regular snapshot matcher...
470
+ let err;
453
471
  try {
454
472
  await gateway.load(mockApolloConfig);
455
473
  } catch (e) {
456
- var err = e;
474
+ err = e;
457
475
  }
458
476
 
459
477
  // TODO: smell that we should be awaiting something else
@@ -480,14 +498,15 @@ describe('Downstream service health checks', () => {
480
498
  mockAllServicesHealthCheckSuccess();
481
499
 
482
500
  // Update
483
- mockSupergraphSdlRequestSuccess(
484
- getTestingSupergraphSdl(fixturesWithUpdate),
485
- 'updatedId-5678',
501
+ mockSupergraphSdlRequestSuccessIfAfter(
502
+ 'originalId-1234',
503
+ 'updatedId-5678',
504
+ getTestingSupergraphSdl(fixturesWithUpdate),
486
505
  );
487
506
  mockAllServicesHealthCheckSuccess();
488
507
 
489
- let resolve1: Function;
490
- let resolve2: Function;
508
+ let resolve1: GenericFunction;
509
+ let resolve2: GenericFunction;
491
510
  const schemaChangeBlocker1 = new Promise((res) => (resolve1 = res));
492
511
  const schemaChangeBlocker2 = new Promise((res) => (resolve2 = res));
493
512
  const onChange = jest
@@ -500,6 +519,7 @@ describe('Downstream service health checks', () => {
500
519
  logger,
501
520
  schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
502
521
  });
522
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
503
523
  // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
504
524
  gateway.experimental_pollInterval = 100;
505
525
 
@@ -523,9 +543,10 @@ describe('Downstream service health checks', () => {
523
543
  mockAllServicesHealthCheckSuccess();
524
544
 
525
545
  // Update (with one health check failure)
526
- mockSupergraphSdlRequestSuccess(
546
+ mockSupergraphSdlRequestSuccessIfAfter(
547
+ 'originalId-1234',
548
+ 'updatedId-5678',
527
549
  getTestingSupergraphSdl(fixturesWithUpdate),
528
- 'updatedId-5678',
529
550
  );
530
551
  mockServiceHealthCheck(accounts).reply(500);
531
552
  mockServiceHealthCheckSuccess(books);
@@ -534,7 +555,7 @@ describe('Downstream service health checks', () => {
534
555
  mockServiceHealthCheckSuccess(reviews);
535
556
  mockServiceHealthCheckSuccess(documents);
536
557
 
537
- let resolve: Function;
558
+ let resolve: GenericFunction;
538
559
  const schemaChangeBlocker = new Promise((res) => (resolve = res));
539
560
 
540
561
  gateway = new ApolloGateway({
@@ -542,9 +563,11 @@ describe('Downstream service health checks', () => {
542
563
  logger,
543
564
  schemaConfigDeliveryEndpoint: mockCloudConfigUrl,
544
565
  });
566
+ // eslint-disable-next-line
545
567
  // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
546
568
  gateway.experimental_pollInterval = 100;
547
569
 
570
+ // eslint-disable-next-line
548
571
  // @ts-ignore for testing purposes, we'll call the original `updateSchema`
549
572
  // function from our mock. The first call should mimic original behavior,
550
573
  // but the second call needs to handle the PromiseRejection. Typically for tests
@@ -568,10 +591,11 @@ describe('Downstream service health checks', () => {
568
591
  // [accounts]: 500: Internal Server Error"
569
592
  // `);
570
593
  // Instead we'll just use the regular snapshot matcher...
594
+ let err;
571
595
  try {
572
596
  await original.apply(gateway);
573
597
  } catch (e) {
574
- var err = e;
598
+ err = e;
575
599
  }
576
600
 
577
601
  expect(err.message).toMatchInlineSnapshot(`
@@ -582,6 +606,7 @@ describe('Downstream service health checks', () => {
582
606
  resolve();
583
607
  });
584
608
 
609
+ // eslint-disable-next-line
585
610
  // @ts-ignore for testing purposes, replace the `updateSchema`
586
611
  // function on the gateway with our mock
587
612
  gateway.updateSchema = mockUpdateSchema;
@@ -70,21 +70,30 @@ export const mockCloudConfigUrl =
70
70
  export const mockOutOfBandReporterUrl =
71
71
  'https://example.outofbandreporter.com/monitoring/';
72
72
 
73
- export function mockSupergraphSdlRequest() {
73
+ export function mockSupergraphSdlRequestIfAfter(ifAfter: string | null) {
74
74
  return gatewayNock(mockCloudConfigUrl).post('/', {
75
75
  query: SUPERGRAPH_SDL_QUERY,
76
76
  variables: {
77
77
  ref: graphRef,
78
78
  apiKey: apiKey,
79
+ ifAfterId: ifAfter,
79
80
  },
80
81
  });
81
82
  }
82
83
 
83
- export function mockSupergraphSdlRequestSuccess(
84
- supergraphSdl = getTestingSupergraphSdl(),
85
- id = 'originalId-1234',
84
+ export function mockSupergraphSdlRequest(ifAfter: string | null = null) {
85
+ return mockSupergraphSdlRequestIfAfter(ifAfter);
86
+ }
87
+
88
+ export function mockSupergraphSdlRequestSuccessIfAfter(
89
+ ifAfter: string | null = null,
90
+ id: string = 'originalId-1234',
91
+ supergraphSdl: string = getTestingSupergraphSdl(),
86
92
  ) {
87
- return mockSupergraphSdlRequest().reply(
93
+ if (supergraphSdl == null) {
94
+ supergraphSdl = getTestingSupergraphSdl();
95
+ }
96
+ return mockSupergraphSdlRequestIfAfter(ifAfter).reply(
88
97
  200,
89
98
  JSON.stringify({
90
99
  data: {
@@ -98,6 +107,25 @@ export function mockSupergraphSdlRequestSuccess(
98
107
  );
99
108
  }
100
109
 
110
+ export function mockSupergraphSdlRequestIfAfterUnchanged(
111
+ ifAfter: string | null = null,
112
+ ) {
113
+ return mockSupergraphSdlRequestIfAfter(ifAfter).reply(
114
+ 200,
115
+ JSON.stringify({
116
+ data: {
117
+ routerConfig: {
118
+ __typename: 'Unchanged',
119
+ },
120
+ },
121
+ }),
122
+ );
123
+ }
124
+
125
+ export function mockSupergraphSdlRequestSuccess() {
126
+ return mockSupergraphSdlRequestSuccessIfAfter(null);
127
+ }
128
+
101
129
  export function mockOutOfBandReportRequest() {
102
130
  return gatewayNock(mockOutOfBandReporterUrl).post('/', () => true);
103
131
  }
@@ -19,7 +19,7 @@ describe('getServiceDefinitionsFromRemoteEndpoint', () => {
19
19
 
20
20
  it('throws when the downstream service returns errors', async () => {
21
21
  const serviceSdlCache = new Map<string, string>();
22
- const host = 'http://host-which-better-not-resolve';
22
+ const host = 'http://host-which-better-not-resolve.invalid';
23
23
  const url = host + '/graphql';
24
24
 
25
25
  const dataSource = new RemoteGraphQLDataSource({ url });
@@ -34,7 +34,7 @@ describe('getServiceDefinitionsFromRemoteEndpoint', () => {
34
34
  getServiceIntrospectionHeaders: async () => ({}),
35
35
  }),
36
36
  ).rejects.toThrowError(
37
- /^Couldn't load service definitions for "test" at http:\/\/host-which-better-not-resolve\/graphql: request to http:\/\/host-which-better-not-resolve\/graphql failed, reason: getaddrinfo (ENOTFOUND|EAI_AGAIN)/,
37
+ /^Couldn't load service definitions for "test" at http:\/\/host-which-better-not-resolve.invalid\/graphql: request to http:\/\/host-which-better-not-resolve.invalid\/graphql failed, reason: getaddrinfo (ENOTFOUND|EAI_AGAIN)/,
38
38
  );
39
39
  });
40
40
  });