@apollo/gateway 2.0.0-alpha.3 → 2.0.0-alpha.4

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 (116) hide show
  1. package/dist/config.d.ts +41 -15
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +28 -18
  4. package/dist/config.js.map +1 -1
  5. package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
  6. package/dist/executeQueryPlan.d.ts.map +1 -1
  7. package/dist/executeQueryPlan.js +1 -1
  8. package/dist/executeQueryPlan.js.map +1 -1
  9. package/dist/index.d.ts +36 -24
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +197 -295
  12. package/dist/index.js.map +1 -1
  13. package/dist/schema-helper/addResolversToSchema.d.ts +4 -0
  14. package/dist/schema-helper/addResolversToSchema.d.ts.map +1 -0
  15. package/dist/schema-helper/addResolversToSchema.js +62 -0
  16. package/dist/schema-helper/addResolversToSchema.js.map +1 -0
  17. package/dist/schema-helper/error.d.ts +6 -0
  18. package/dist/schema-helper/error.d.ts.map +1 -0
  19. package/dist/schema-helper/error.js +14 -0
  20. package/dist/schema-helper/error.js.map +1 -0
  21. package/dist/schema-helper/index.d.ts +4 -0
  22. package/dist/schema-helper/index.d.ts.map +1 -0
  23. package/dist/schema-helper/index.js +16 -0
  24. package/dist/schema-helper/index.js.map +1 -0
  25. package/dist/schema-helper/resolverMap.d.ts +16 -0
  26. package/dist/schema-helper/resolverMap.d.ts.map +1 -0
  27. package/dist/schema-helper/resolverMap.js +3 -0
  28. package/dist/schema-helper/resolverMap.js.map +1 -0
  29. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +31 -0
  30. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -0
  31. package/dist/supergraphManagers/IntrospectAndCompose/index.js +112 -0
  32. package/dist/supergraphManagers/IntrospectAndCompose/index.js.map +1 -0
  33. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts +12 -0
  34. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -0
  35. package/dist/{loadServicesFromRemoteEndpoint.js → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js} +6 -6
  36. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -0
  37. package/dist/supergraphManagers/LegacyFetcher/index.d.ts +33 -0
  38. package/dist/supergraphManagers/LegacyFetcher/index.d.ts.map +1 -0
  39. package/dist/supergraphManagers/LegacyFetcher/index.js +149 -0
  40. package/dist/supergraphManagers/LegacyFetcher/index.js.map +1 -0
  41. package/dist/supergraphManagers/LocalCompose/index.d.ts +19 -0
  42. package/dist/supergraphManagers/LocalCompose/index.d.ts.map +1 -0
  43. package/dist/supergraphManagers/LocalCompose/index.js +55 -0
  44. package/dist/supergraphManagers/LocalCompose/index.js.map +1 -0
  45. package/dist/supergraphManagers/UplinkFetcher/index.d.ts +32 -0
  46. package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -0
  47. package/dist/supergraphManagers/UplinkFetcher/index.js +96 -0
  48. package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -0
  49. package/dist/{loadSupergraphSdlFromStorage.d.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts} +1 -1
  50. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -0
  51. package/dist/{loadSupergraphSdlFromStorage.js → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js} +1 -1
  52. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -0
  53. package/dist/{outOfBandReporter.d.ts → supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts} +0 -0
  54. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +1 -0
  55. package/dist/{outOfBandReporter.js → supergraphManagers/UplinkFetcher/outOfBandReporter.js} +2 -2
  56. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +1 -0
  57. package/dist/supergraphManagers/index.d.ts +5 -0
  58. package/dist/supergraphManagers/index.d.ts.map +1 -0
  59. package/dist/supergraphManagers/index.js +12 -0
  60. package/dist/supergraphManagers/index.js.map +1 -0
  61. package/dist/utilities/createHash.d.ts +2 -0
  62. package/dist/utilities/createHash.d.ts.map +1 -0
  63. package/dist/utilities/createHash.js +15 -0
  64. package/dist/utilities/createHash.js.map +1 -0
  65. package/dist/utilities/isNodeLike.d.ts +3 -0
  66. package/dist/utilities/isNodeLike.d.ts.map +1 -0
  67. package/dist/utilities/isNodeLike.js +8 -0
  68. package/dist/utilities/isNodeLike.js.map +1 -0
  69. package/package.json +8 -7
  70. package/src/__tests__/executeQueryPlan.test.ts +1 -1
  71. package/src/__tests__/execution-utils.ts +3 -3
  72. package/src/__tests__/gateway/buildService.test.ts +2 -2
  73. package/src/__tests__/gateway/endToEnd.test.ts +1 -1
  74. package/src/__tests__/gateway/lifecycle-hooks.test.ts +59 -125
  75. package/src/__tests__/gateway/opentelemetry.test.ts +8 -4
  76. package/src/__tests__/gateway/queryPlanCache.test.ts +25 -12
  77. package/src/__tests__/gateway/reporting.test.ts +34 -8
  78. package/src/__tests__/gateway/supergraphSdl.test.ts +397 -0
  79. package/src/__tests__/integration/aliases.test.ts +9 -4
  80. package/src/__tests__/integration/configuration.test.ts +109 -12
  81. package/src/__tests__/integration/logger.test.ts +1 -1
  82. package/src/__tests__/integration/networkRequests.test.ts +81 -131
  83. package/src/__tests__/integration/nockMocks.ts +15 -8
  84. package/src/config.ts +148 -39
  85. package/src/datasources/LocalGraphQLDataSource.ts +1 -1
  86. package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
  87. package/src/executeQueryPlan.ts +3 -1
  88. package/src/index.ts +322 -466
  89. package/src/schema-helper/addResolversToSchema.ts +83 -0
  90. package/src/schema-helper/error.ts +11 -0
  91. package/src/schema-helper/index.ts +3 -0
  92. package/src/schema-helper/resolverMap.ts +23 -0
  93. package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +370 -0
  94. package/src/{__tests__ → supergraphManagers/IntrospectAndCompose/__tests__}/loadServicesFromRemoteEndpoint.test.ts +5 -5
  95. package/src/supergraphManagers/IntrospectAndCompose/__tests__/tsconfig.json +8 -0
  96. package/src/supergraphManagers/IntrospectAndCompose/index.ts +160 -0
  97. package/src/{loadServicesFromRemoteEndpoint.ts → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts} +6 -6
  98. package/src/supergraphManagers/LegacyFetcher/index.ts +226 -0
  99. package/src/supergraphManagers/LocalCompose/index.ts +79 -0
  100. package/src/{__tests__ → supergraphManagers/UplinkFetcher/__tests__}/loadSupergraphSdlFromStorage.test.ts +4 -4
  101. package/src/supergraphManagers/UplinkFetcher/__tests__/tsconfig.json +8 -0
  102. package/src/supergraphManagers/UplinkFetcher/index.ts +128 -0
  103. package/src/{loadSupergraphSdlFromStorage.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts} +3 -3
  104. package/src/{outOfBandReporter.ts → supergraphManagers/UplinkFetcher/outOfBandReporter.ts} +2 -2
  105. package/src/supergraphManagers/index.ts +4 -0
  106. package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +12 -9
  107. package/src/utilities/createHash.ts +10 -0
  108. package/src/utilities/isNodeLike.ts +11 -0
  109. package/dist/loadServicesFromRemoteEndpoint.d.ts +0 -13
  110. package/dist/loadServicesFromRemoteEndpoint.d.ts.map +0 -1
  111. package/dist/loadServicesFromRemoteEndpoint.js.map +0 -1
  112. package/dist/loadSupergraphSdlFromStorage.d.ts.map +0 -1
  113. package/dist/loadSupergraphSdlFromStorage.js.map +0 -1
  114. package/dist/outOfBandReporter.d.ts.map +0 -1
  115. package/dist/outOfBandReporter.js.map +0 -1
  116. package/src/__tests__/gateway/composedSdl.test.ts +0 -44
@@ -1,5 +1,5 @@
1
1
  import gql from 'graphql-tag';
2
- import { DocumentNode, GraphQLObjectType, GraphQLSchema } from 'graphql';
2
+ import { GraphQLObjectType, GraphQLSchema } from 'graphql';
3
3
  import mockedEnv from 'mocked-env';
4
4
  import { Logger } from 'apollo-server-types';
5
5
  import { ApolloGateway } from '../..';
@@ -19,6 +19,7 @@ import {
19
19
  accounts,
20
20
  books,
21
21
  documents,
22
+ Fixture,
22
23
  fixturesWithUpdate,
23
24
  inventory,
24
25
  product,
@@ -26,15 +27,9 @@ import {
26
27
  } from 'apollo-federation-integration-testsuite';
27
28
  import { getTestingSupergraphSdl } from '../execution-utils';
28
29
  import { nockAfterEach, nockBeforeEach } from '../nockAssertions';
30
+ import resolvable from '@josephg/resolvable';
29
31
 
30
- type GenericFunction = (...args: unknown[]) => unknown;
31
- export interface MockService {
32
- name: string;
33
- url: string;
34
- typeDefs: DocumentNode;
35
- }
36
-
37
- const simpleService: MockService = {
32
+ const simpleService: Fixture = {
38
33
  name: 'accounts',
39
34
  url: 'http://localhost:4001',
40
35
  typeDefs: gql`
@@ -138,29 +133,39 @@ it('Updates Supergraph SDL from remote storage', async () => {
138
133
 
139
134
  // This test is only interested in the second time the gateway notifies of an
140
135
  // update, since the first happens on load.
141
- let secondUpdateResolve: GenericFunction;
142
- const secondUpdate = new Promise((res) => (secondUpdateResolve = res));
143
- const schemaChangeCallback = jest
144
- .fn()
145
- .mockImplementationOnce(() => undefined)
146
- .mockImplementationOnce(() => {
147
- secondUpdateResolve();
148
- });
136
+ const secondUpdate = resolvable();
149
137
 
150
138
  gateway = new ApolloGateway({
151
139
  logger,
152
140
  uplinkEndpoints: [mockCloudConfigUrl1],
153
141
  });
154
- // eslint-disable-next-line
155
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
156
- gateway.experimental_pollInterval = 100;
157
- gateway.onSchemaLoadOrUpdate(schemaChangeCallback);
142
+ // for testing purposes, a short pollInterval is ideal so we'll override here
143
+ gateway['pollIntervalInMs'] = 100;
144
+
145
+ const schemas: GraphQLSchema[] = [];
146
+ gateway.onSchemaLoadOrUpdate(({ apiSchema }) => {
147
+ schemas.push(apiSchema);
148
+ });
149
+ gateway.onSchemaLoadOrUpdate(
150
+ jest
151
+ .fn()
152
+ .mockImplementationOnce(() => {})
153
+ .mockImplementationOnce(() => secondUpdate.resolve()),
154
+ );
158
155
 
159
156
  await gateway.load(mockApolloConfig);
160
- expect(gateway['compositionId']).toMatchInlineSnapshot(`"originalId-1234"`);
161
157
 
162
158
  await secondUpdate;
163
- expect(gateway['compositionId']).toMatchInlineSnapshot(`"updatedId-5678"`);
159
+
160
+ // First schema has no 'review' field on the 'Query' type
161
+ expect(
162
+ (schemas[0].getType('Query') as GraphQLObjectType).getFields()['review'],
163
+ ).toBeFalsy();
164
+
165
+ // Updated schema adds 'review' field on the 'Query' type
166
+ expect(
167
+ (schemas[1].getType('Query') as GraphQLObjectType).getFields()['review'],
168
+ ).toBeTruthy();
164
169
  });
165
170
 
166
171
  describe('Supergraph SDL update failures', () => {
@@ -170,7 +175,7 @@ describe('Supergraph SDL update failures', () => {
170
175
  gateway = new ApolloGateway({
171
176
  logger,
172
177
  uplinkEndpoints: [mockCloudConfigUrl1],
173
- uplinkMaxRetries: 0
178
+ uplinkMaxRetries: 0,
174
179
  });
175
180
 
176
181
  await expect(
@@ -192,25 +197,23 @@ describe('Supergraph SDL update failures', () => {
192
197
  mockSupergraphSdlRequestIfAfter('originalId-1234').reply(500);
193
198
 
194
199
  // Spy on logger.error so we can just await once it's been called
195
- let errorLogged: GenericFunction;
196
- const errorLoggedPromise = new Promise((r) => (errorLogged = r));
197
- logger.error = jest.fn(() => errorLogged());
200
+ const errorLoggedPromise = resolvable();
201
+ logger.error = jest.fn(() => errorLoggedPromise.resolve());
198
202
 
199
203
  gateway = new ApolloGateway({
200
204
  logger,
201
205
  uplinkEndpoints: [mockCloudConfigUrl1],
202
- uplinkMaxRetries: 0
206
+ uplinkMaxRetries: 0,
203
207
  });
204
208
 
205
- // eslint-disable-next-line
206
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
207
- gateway.experimental_pollInterval = 100;
209
+ // for testing purposes, a short pollInterval is ideal so we'll override here
210
+ gateway['pollIntervalInMs'] = 100;
208
211
 
209
212
  await gateway.load(mockApolloConfig);
210
213
  await errorLoggedPromise;
211
214
 
212
215
  expect(logger.error).toHaveBeenCalledWith(
213
- 'An error occurred while fetching your schema from Apollo: 500 Internal Server Error',
216
+ 'UplinkFetcher failed to update supergraph with the following error: An error occurred while fetching your schema from Apollo: 500 Internal Server Error',
214
217
  );
215
218
  });
216
219
 
@@ -227,26 +230,22 @@ describe('Supergraph SDL update failures', () => {
227
230
  });
228
231
 
229
232
  // Spy on logger.error so we can just await once it's been called
230
- let errorLogged: GenericFunction;
231
- const errorLoggedPromise = new Promise((r) => (errorLogged = r));
232
- logger.error = jest.fn(() => errorLogged());
233
+ const errorLoggedPromise = resolvable();
234
+ logger.error = jest.fn(() => errorLoggedPromise.resolve());
233
235
 
234
236
  gateway = new ApolloGateway({
235
237
  logger,
236
238
  uplinkEndpoints: [mockCloudConfigUrl1],
237
- uplinkMaxRetries: 0
239
+ uplinkMaxRetries: 0,
238
240
  });
239
- // eslint-disable-next-line
240
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
241
- gateway.experimental_pollInterval = 100;
241
+ // for testing purposes, a short pollInterval is ideal so we'll override here
242
+ gateway['pollIntervalInMs'] = 100;
242
243
 
243
244
  await gateway.load(mockApolloConfig);
244
245
  await errorLoggedPromise;
245
246
 
246
247
  expect(logger.error).toHaveBeenCalledWith(
247
- 'An error occurred while fetching your schema from Apollo: ' +
248
- '\n' +
249
- 'Cannot query field "fail" on type "Query".',
248
+ `UplinkFetcher failed to update supergraph with the following error: An error occurred while fetching your schema from Apollo: \nCannot query field "fail" on type "Query".`,
250
249
  );
251
250
  });
252
251
 
@@ -266,23 +265,21 @@ describe('Supergraph SDL update failures', () => {
266
265
  );
267
266
 
268
267
  // Spy on logger.error so we can just await once it's been called
269
- let errorLogged: GenericFunction;
270
- const errorLoggedPromise = new Promise((r) => (errorLogged = r));
271
- logger.error = jest.fn(() => errorLogged());
268
+ const errorLoggedPromise = resolvable();
269
+ logger.error = jest.fn(() => errorLoggedPromise.resolve());
272
270
 
273
271
  gateway = new ApolloGateway({
274
272
  logger,
275
273
  uplinkEndpoints: [mockCloudConfigUrl1],
276
274
  });
277
- // eslint-disable-next-line
278
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
279
- gateway.experimental_pollInterval = 100;
275
+ // for testing purposes, a short pollInterval is ideal so we'll override here
276
+ gateway['pollIntervalInMs'] = 100;
280
277
 
281
278
  await gateway.load(mockApolloConfig);
282
279
  await errorLoggedPromise;
283
280
 
284
281
  expect(logger.error).toHaveBeenCalledWith(
285
- 'Syntax Error: Unexpected Name "Syntax".',
282
+ 'UplinkFetcher failed to update supergraph with the following error: Syntax Error: Unexpected Name "Syntax".',
286
283
  );
287
284
  expect(gateway.schema).toBeTruthy();
288
285
  });
@@ -305,9 +302,8 @@ describe('Supergraph SDL update failures', () => {
305
302
  logger,
306
303
  uplinkEndpoints: [mockCloudConfigUrl1],
307
304
  });
308
- // eslint-disable-next-line
309
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
310
- gateway.experimental_pollInterval = 100;
305
+ // for testing purposes, a short pollInterval is ideal so we'll override here
306
+ gateway['pollIntervalInMs'] = 100;
311
307
 
312
308
  await expect(
313
309
  gateway.load(mockApolloConfig),
@@ -333,26 +329,22 @@ it('Rollsback to a previous schema when triggered', async () => {
333
329
  );
334
330
  mockSupergraphSdlRequestSuccessIfAfter('updatedId-5678');
335
331
 
336
- let firstResolve: GenericFunction;
337
- let secondResolve: GenericFunction;
338
- let thirdResolve: GenericFunction;
339
- const firstSchemaChangeBlocker = new Promise((res) => (firstResolve = res));
340
- const secondSchemaChangeBlocker = new Promise((res) => (secondResolve = res));
341
- const thirdSchemaChangeBlocker = new Promise((res) => (thirdResolve = res));
332
+ const firstSchemaChangeBlocker = resolvable();
333
+ const secondSchemaChangeBlocker = resolvable();
334
+ const thirdSchemaChangeBlocker = resolvable();
342
335
 
343
336
  const onChange = jest
344
337
  .fn()
345
- .mockImplementationOnce(() => firstResolve())
346
- .mockImplementationOnce(() => secondResolve())
347
- .mockImplementationOnce(() => thirdResolve());
338
+ .mockImplementationOnce(() => firstSchemaChangeBlocker.resolve())
339
+ .mockImplementationOnce(() => secondSchemaChangeBlocker.resolve())
340
+ .mockImplementationOnce(() => thirdSchemaChangeBlocker.resolve());
348
341
 
349
342
  gateway = new ApolloGateway({
350
343
  logger,
351
344
  uplinkEndpoints: [mockCloudConfigUrl1],
352
345
  });
353
- // eslint-disable-next-line
354
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
355
- gateway.experimental_pollInterval = 100;
346
+ // for testing purposes, a short pollInterval is ideal so we'll override here
347
+ gateway['pollIntervalInMs'] = 100;
356
348
 
357
349
  gateway.onSchemaChange(onChange);
358
350
  await gateway.load(mockApolloConfig);
@@ -410,9 +402,8 @@ describe('Downstream service health checks', () => {
410
402
  err = e;
411
403
  }
412
404
 
413
- // TODO: smell that we should be awaiting something else
414
405
  expect(err.message).toMatchInlineSnapshot(`
415
- "The gateway did not update its schema due to failed service health checks. The gateway will continue to operate with the previous schema and reattempt updates. The following error occurred during the health check:
406
+ "The gateway subgraphs health check failed. Updating to the provided \`supergraphSdl\` will likely result in future request failures to subgraphs. The following error occurred during the health check:
416
407
  [accounts]: 500: Internal Server Error"
417
408
  `);
418
409
 
@@ -436,9 +427,8 @@ describe('Downstream service health checks', () => {
436
427
  logger,
437
428
  uplinkEndpoints: [mockCloudConfigUrl1],
438
429
  });
439
- // eslint-disable-next-line
440
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
441
- gateway.experimental_pollInterval = 100;
430
+ // for testing purposes, a short pollInterval is ideal so we'll override here
431
+ gateway['pollIntervalInMs'] = 100;
442
432
 
443
433
  await gateway.load(mockApolloConfig);
444
434
  await gateway.stop();
@@ -478,7 +468,7 @@ describe('Downstream service health checks', () => {
478
468
 
479
469
  // TODO: smell that we should be awaiting something else
480
470
  expect(err.message).toMatchInlineSnapshot(`
481
- "The gateway did not update its schema due to failed service health checks. The gateway will continue to operate with the previous schema and reattempt updates. The following error occurred during the health check:
471
+ "The gateway subgraphs health check failed. Updating to the provided \`supergraphSdl\` will likely result in future request failures to subgraphs. The following error occurred during the health check:
482
472
  [accounts]: 500: Internal Server Error"
483
473
  `);
484
474
 
@@ -507,23 +497,20 @@ describe('Downstream service health checks', () => {
507
497
  );
508
498
  mockAllServicesHealthCheckSuccess();
509
499
 
510
- let resolve1: GenericFunction;
511
- let resolve2: GenericFunction;
512
- const schemaChangeBlocker1 = new Promise((res) => (resolve1 = res));
513
- const schemaChangeBlocker2 = new Promise((res) => (resolve2 = res));
500
+ const schemaChangeBlocker1 = resolvable();
501
+ const schemaChangeBlocker2 = resolvable();
514
502
  const onChange = jest
515
503
  .fn()
516
- .mockImplementationOnce(() => resolve1())
517
- .mockImplementationOnce(() => resolve2());
504
+ .mockImplementationOnce(() => schemaChangeBlocker1.resolve())
505
+ .mockImplementationOnce(() => schemaChangeBlocker2.resolve());
518
506
 
519
507
  gateway = new ApolloGateway({
520
508
  serviceHealthCheck: true,
521
509
  logger,
522
510
  uplinkEndpoints: [mockCloudConfigUrl1],
523
511
  });
524
- // eslint-disable-next-line @typescript-eslint/ban-ts-comment
525
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
526
- gateway.experimental_pollInterval = 100;
512
+ // for testing purposes, a short pollInterval is ideal so we'll override here
513
+ gateway['pollIntervalInMs'] = 100;
527
514
 
528
515
  gateway.onSchemaChange(onChange);
529
516
  await gateway.load(mockApolloConfig);
@@ -541,6 +528,10 @@ describe('Downstream service health checks', () => {
541
528
  });
542
529
 
543
530
  it('Preserves original schema when health check fails', async () => {
531
+ const errorLoggedPromise = resolvable();
532
+ const errorSpy = jest.fn(() => errorLoggedPromise.resolve());
533
+ logger.error = errorSpy;
534
+
544
535
  mockSupergraphSdlRequestSuccess();
545
536
  mockAllServicesHealthCheckSuccess();
546
537
 
@@ -557,61 +548,16 @@ describe('Downstream service health checks', () => {
557
548
  mockServiceHealthCheckSuccess(reviews);
558
549
  mockServiceHealthCheckSuccess(documents);
559
550
 
560
- let resolve: GenericFunction;
561
- const schemaChangeBlocker = new Promise((res) => (resolve = res));
562
-
563
551
  gateway = new ApolloGateway({
564
552
  serviceHealthCheck: true,
565
553
  logger,
566
554
  uplinkEndpoints: [mockCloudConfigUrl1],
567
555
  });
568
- // eslint-disable-next-line
569
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
570
- gateway.experimental_pollInterval = 100;
571
-
572
- // eslint-disable-next-line
573
- // @ts-ignore for testing purposes, we'll call the original `updateSchema`
574
- // function from our mock. The first call should mimic original behavior,
575
- // but the second call needs to handle the PromiseRejection. Typically for tests
576
- // like these we would leverage the `gateway.onSchemaChange` callback to drive
577
- // the test, but in this case, that callback isn't triggered when the update
578
- // fails (as expected) so we get creative with the second mock as seen below.
579
- const original = gateway.updateSchema;
580
- const mockUpdateSchema = jest
581
- .fn()
582
- .mockImplementationOnce(async () => {
583
- await original.apply(gateway);
584
- })
585
- .mockImplementationOnce(async () => {
586
- // mock the first poll and handle the error which would otherwise be caught
587
- // and logged from within the `pollServices` class method
588
-
589
- // This is the ideal, but our version of Jest has a bug with printing error snapshots.
590
- // See: https://github.com/facebook/jest/pull/10217 (fixed in v26.2.0)
591
- // expect(original.apply(gateway)).rejects.toThrowErrorMatchingInlineSnapshot(`
592
- // The gateway did not update its schema due to failed service health checks. The gateway will continue to operate with the previous schema and reattempt updates. The following error occurred during the health check:
593
- // [accounts]: 500: Internal Server Error"
594
- // `);
595
- // Instead we'll just use the regular snapshot matcher...
596
- let err;
597
- try {
598
- await original.apply(gateway);
599
- } catch (e) {
600
- err = e;
601
- }
602
-
603
- expect(err.message).toMatchInlineSnapshot(`
604
- "The gateway did not update its schema due to failed service health checks. The gateway will continue to operate with the previous schema and reattempt updates. The following error occurred during the health check:
605
- [accounts]: 500: Internal Server Error"
606
- `);
607
- // finally resolve the promise which drives this test
608
- resolve();
609
- });
610
-
611
- // eslint-disable-next-line
612
- // @ts-ignore for testing purposes, replace the `updateSchema`
613
- // function on the gateway with our mock
614
- gateway.updateSchema = mockUpdateSchema;
556
+ // for testing purposes, a short pollInterval is ideal so we'll override here
557
+ gateway['pollIntervalInMs'] = 100;
558
+
559
+ const updateSpy = jest.fn();
560
+ gateway.onSchemaLoadOrUpdate(() => updateSpy());
615
561
 
616
562
  // load the gateway as usual
617
563
  await gateway.load(mockApolloConfig);
@@ -620,11 +566,15 @@ describe('Downstream service health checks', () => {
620
566
  expect(getRootQueryFields(gateway.schema)).toContain('topReviews');
621
567
  expect(getRootQueryFields(gateway.schema)).not.toContain('review');
622
568
 
623
- await schemaChangeBlocker;
569
+ await errorLoggedPromise;
570
+ expect(logger.error).toHaveBeenCalledWith(
571
+ `UplinkFetcher failed to update supergraph with the following error: The gateway subgraphs health check failed. Updating to the provided \`supergraphSdl\` will likely result in future request failures to subgraphs. The following error occurred during the health check:\n[accounts]: 500: Internal Server Error`,
572
+ );
624
573
 
625
574
  // At this point, the mock update should have been called but the schema
626
575
  // should still be the original.
627
- expect(mockUpdateSchema).toHaveBeenCalledTimes(2);
576
+ expect(updateSpy).toHaveBeenCalledTimes(1);
577
+
628
578
  expect(getRootQueryFields(gateway.schema)).toContain('topReviews');
629
579
  expect(getRootQueryFields(gateway.schema)).not.toContain('review');
630
580
  });
@@ -1,10 +1,9 @@
1
1
  import nock from 'nock';
2
- import { MockService } from './networkRequests.test';
3
2
  import { HEALTH_CHECK_QUERY, SERVICE_DEFINITION_QUERY } from '../..';
4
- import { SUPERGRAPH_SDL_QUERY } from '../../loadSupergraphSdlFromStorage';
3
+ import { SUPERGRAPH_SDL_QUERY } from '../../supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage';
5
4
  import { getTestingSupergraphSdl } from '../../__tests__/execution-utils';
6
5
  import { print } from 'graphql';
7
- import { fixtures } from 'apollo-federation-integration-testsuite';
6
+ import { Fixture, fixtures as testingFixtures } from 'apollo-federation-integration-testsuite';
8
7
 
9
8
  export const graphRef = 'federated-service@current';
10
9
  export const apiKey = 'service:federated-service:DD71EBbGmsuh-6suUVDwnA';
@@ -19,31 +18,39 @@ export const mockApolloConfig = {
19
18
  };
20
19
 
21
20
  // Service mocks
22
- function mockSdlQuery({ url }: MockService) {
21
+ function mockSdlQuery({ url }: Fixture) {
23
22
  return nock(url).post('/', {
24
23
  query: SERVICE_DEFINITION_QUERY,
25
24
  });
26
25
  }
27
26
 
28
- export function mockSdlQuerySuccess(service: MockService) {
27
+ export function mockSdlQuerySuccess(service: Fixture) {
29
28
  return mockSdlQuery(service).reply(200, {
30
29
  data: { _service: { sdl: print(service.typeDefs) } },
31
30
  });
32
31
  }
33
32
 
34
- export function mockServiceHealthCheck({ url }: MockService) {
33
+ export function mockAllServicesSdlQuerySuccess(
34
+ fixtures: Fixture[] = testingFixtures,
35
+ ) {
36
+ return fixtures.map((fixture) => mockSdlQuerySuccess(fixture));
37
+ }
38
+
39
+ export function mockServiceHealthCheck({ url }: Fixture) {
35
40
  return nock(url).post('/', {
36
41
  query: HEALTH_CHECK_QUERY,
37
42
  });
38
43
  }
39
44
 
40
- export function mockServiceHealthCheckSuccess(service: MockService) {
45
+ export function mockServiceHealthCheckSuccess(service: Fixture) {
41
46
  return mockServiceHealthCheck(service).reply(200, {
42
47
  data: { __typename: 'Query' },
43
48
  });
44
49
  }
45
50
 
46
- export function mockAllServicesHealthCheckSuccess() {
51
+ export function mockAllServicesHealthCheckSuccess(
52
+ fixtures: Fixture[] = testingFixtures,
53
+ ) {
47
54
  return fixtures.map((fixture) =>
48
55
  mockServiceHealthCheck(fixture).reply(200, {
49
56
  data: { __typename: 'Query' },