@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.
- package/dist/config.d.ts +41 -15
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +28 -18
- package/dist/config.js.map +1 -1
- package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +1 -1
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts +36 -24
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +197 -295
- package/dist/index.js.map +1 -1
- package/dist/schema-helper/addResolversToSchema.d.ts +4 -0
- package/dist/schema-helper/addResolversToSchema.d.ts.map +1 -0
- package/dist/schema-helper/addResolversToSchema.js +62 -0
- package/dist/schema-helper/addResolversToSchema.js.map +1 -0
- package/dist/schema-helper/error.d.ts +6 -0
- package/dist/schema-helper/error.d.ts.map +1 -0
- package/dist/schema-helper/error.js +14 -0
- package/dist/schema-helper/error.js.map +1 -0
- package/dist/schema-helper/index.d.ts +4 -0
- package/dist/schema-helper/index.d.ts.map +1 -0
- package/dist/schema-helper/index.js +16 -0
- package/dist/schema-helper/index.js.map +1 -0
- package/dist/schema-helper/resolverMap.d.ts +16 -0
- package/dist/schema-helper/resolverMap.d.ts.map +1 -0
- package/dist/schema-helper/resolverMap.js +3 -0
- package/dist/schema-helper/resolverMap.js.map +1 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +31 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.js +112 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.js.map +1 -0
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts +12 -0
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -0
- package/dist/{loadServicesFromRemoteEndpoint.js → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js} +6 -6
- package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -0
- package/dist/supergraphManagers/LegacyFetcher/index.d.ts +33 -0
- package/dist/supergraphManagers/LegacyFetcher/index.d.ts.map +1 -0
- package/dist/supergraphManagers/LegacyFetcher/index.js +149 -0
- package/dist/supergraphManagers/LegacyFetcher/index.js.map +1 -0
- package/dist/supergraphManagers/LocalCompose/index.d.ts +19 -0
- package/dist/supergraphManagers/LocalCompose/index.d.ts.map +1 -0
- package/dist/supergraphManagers/LocalCompose/index.js +55 -0
- package/dist/supergraphManagers/LocalCompose/index.js.map +1 -0
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts +32 -0
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -0
- package/dist/supergraphManagers/UplinkFetcher/index.js +96 -0
- package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -0
- package/dist/{loadSupergraphSdlFromStorage.d.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts} +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -0
- package/dist/{loadSupergraphSdlFromStorage.js → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js} +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -0
- package/dist/{outOfBandReporter.d.ts → supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts} +0 -0
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +1 -0
- package/dist/{outOfBandReporter.js → supergraphManagers/UplinkFetcher/outOfBandReporter.js} +2 -2
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +1 -0
- package/dist/supergraphManagers/index.d.ts +5 -0
- package/dist/supergraphManagers/index.d.ts.map +1 -0
- package/dist/supergraphManagers/index.js +12 -0
- package/dist/supergraphManagers/index.js.map +1 -0
- package/dist/utilities/createHash.d.ts +2 -0
- package/dist/utilities/createHash.d.ts.map +1 -0
- package/dist/utilities/createHash.js +15 -0
- package/dist/utilities/createHash.js.map +1 -0
- package/dist/utilities/isNodeLike.d.ts +3 -0
- package/dist/utilities/isNodeLike.d.ts.map +1 -0
- package/dist/utilities/isNodeLike.js +8 -0
- package/dist/utilities/isNodeLike.js.map +1 -0
- package/package.json +8 -7
- package/src/__tests__/executeQueryPlan.test.ts +1 -1
- package/src/__tests__/execution-utils.ts +3 -3
- package/src/__tests__/gateway/buildService.test.ts +2 -2
- package/src/__tests__/gateway/endToEnd.test.ts +1 -1
- package/src/__tests__/gateway/lifecycle-hooks.test.ts +59 -125
- package/src/__tests__/gateway/opentelemetry.test.ts +8 -4
- package/src/__tests__/gateway/queryPlanCache.test.ts +25 -12
- package/src/__tests__/gateway/reporting.test.ts +34 -8
- package/src/__tests__/gateway/supergraphSdl.test.ts +397 -0
- package/src/__tests__/integration/aliases.test.ts +9 -4
- package/src/__tests__/integration/configuration.test.ts +109 -12
- package/src/__tests__/integration/logger.test.ts +1 -1
- package/src/__tests__/integration/networkRequests.test.ts +81 -131
- package/src/__tests__/integration/nockMocks.ts +15 -8
- package/src/config.ts +148 -39
- package/src/datasources/LocalGraphQLDataSource.ts +1 -1
- package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
- package/src/executeQueryPlan.ts +3 -1
- package/src/index.ts +322 -466
- package/src/schema-helper/addResolversToSchema.ts +83 -0
- package/src/schema-helper/error.ts +11 -0
- package/src/schema-helper/index.ts +3 -0
- package/src/schema-helper/resolverMap.ts +23 -0
- package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +370 -0
- package/src/{__tests__ → supergraphManagers/IntrospectAndCompose/__tests__}/loadServicesFromRemoteEndpoint.test.ts +5 -5
- package/src/supergraphManagers/IntrospectAndCompose/__tests__/tsconfig.json +8 -0
- package/src/supergraphManagers/IntrospectAndCompose/index.ts +160 -0
- package/src/{loadServicesFromRemoteEndpoint.ts → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts} +6 -6
- package/src/supergraphManagers/LegacyFetcher/index.ts +226 -0
- package/src/supergraphManagers/LocalCompose/index.ts +79 -0
- package/src/{__tests__ → supergraphManagers/UplinkFetcher/__tests__}/loadSupergraphSdlFromStorage.test.ts +4 -4
- package/src/supergraphManagers/UplinkFetcher/__tests__/tsconfig.json +8 -0
- package/src/supergraphManagers/UplinkFetcher/index.ts +128 -0
- package/src/{loadSupergraphSdlFromStorage.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts} +3 -3
- package/src/{outOfBandReporter.ts → supergraphManagers/UplinkFetcher/outOfBandReporter.ts} +2 -2
- package/src/supergraphManagers/index.ts +4 -0
- package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +12 -9
- package/src/utilities/createHash.ts +10 -0
- package/src/utilities/isNodeLike.ts +11 -0
- package/dist/loadServicesFromRemoteEndpoint.d.ts +0 -13
- package/dist/loadServicesFromRemoteEndpoint.d.ts.map +0 -1
- package/dist/loadServicesFromRemoteEndpoint.js.map +0 -1
- package/dist/loadSupergraphSdlFromStorage.d.ts.map +0 -1
- package/dist/loadSupergraphSdlFromStorage.js.map +0 -1
- package/dist/outOfBandReporter.d.ts.map +0 -1
- package/dist/outOfBandReporter.js.map +0 -1
- package/src/__tests__/gateway/composedSdl.test.ts +0 -44
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import gql from 'graphql-tag';
|
|
2
|
-
import {
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
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
|
-
|
|
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
|
-
|
|
196
|
-
|
|
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
|
-
//
|
|
206
|
-
|
|
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
|
-
|
|
231
|
-
|
|
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
|
-
//
|
|
240
|
-
|
|
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
|
-
|
|
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
|
-
|
|
270
|
-
|
|
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
|
-
//
|
|
278
|
-
|
|
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
|
-
//
|
|
309
|
-
|
|
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
|
-
|
|
337
|
-
|
|
338
|
-
|
|
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(() =>
|
|
346
|
-
.mockImplementationOnce(() =>
|
|
347
|
-
.mockImplementationOnce(() =>
|
|
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
|
-
//
|
|
354
|
-
|
|
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
|
|
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
|
-
//
|
|
440
|
-
|
|
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
|
|
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
|
-
|
|
511
|
-
|
|
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(() =>
|
|
517
|
-
.mockImplementationOnce(() =>
|
|
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
|
-
//
|
|
525
|
-
|
|
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
|
-
//
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
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
|
|
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(
|
|
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 }:
|
|
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:
|
|
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
|
|
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:
|
|
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' },
|