@apollo/gateway 0.45.1 → 0.48.0
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 +42 -16
- 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/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
- package/dist/datasources/RemoteGraphQLDataSource.js +4 -1
- package/dist/datasources/RemoteGraphQLDataSource.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 +35 -23
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +205 -308
- 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 +12 -10
- package/src/__mocks__/make-fetch-happen-fetcher.ts +3 -1
- package/src/__tests__/executeQueryPlan.test.ts +1 -1
- package/src/__tests__/execution-utils.ts +3 -3
- package/src/__tests__/gateway/buildService.test.ts +3 -3
- package/src/__tests__/gateway/endToEnd.test.ts +1 -1
- package/src/__tests__/gateway/executor.test.ts +1 -1
- package/src/__tests__/gateway/lifecycle-hooks.test.ts +58 -99
- package/src/__tests__/gateway/opentelemetry.test.ts +8 -3
- package/src/__tests__/gateway/queryPlanCache.test.ts +25 -9
- package/src/__tests__/gateway/reporting.test.ts +44 -19
- package/src/__tests__/gateway/supergraphSdl.test.ts +394 -0
- package/src/__tests__/integration/aliases.test.ts +9 -3
- 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 -118
- package/src/__tests__/integration/nockMocks.ts +15 -8
- package/src/config.ts +149 -40
- package/src/datasources/LocalGraphQLDataSource.ts +1 -1
- package/src/datasources/RemoteGraphQLDataSource.ts +8 -2
- package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
- package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +4 -4
- package/src/executeQueryPlan.ts +3 -1
- package/src/index.ts +314 -485
- 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 +163 -0
- package/src/{loadServicesFromRemoteEndpoint.ts → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts} +6 -6
- package/src/supergraphManagers/LegacyFetcher/index.ts +229 -0
- package/src/supergraphManagers/LocalCompose/index.ts +83 -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 +5 -5
- package/src/utilities/createHash.ts +10 -0
- package/src/utilities/isNodeLike.ts +11 -0
- package/CHANGELOG.md +0 -474
- 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,14 +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
|
-
name: string;
|
|
32
|
-
url: string;
|
|
33
|
-
typeDefs: DocumentNode;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const simpleService: MockService = {
|
|
32
|
+
const simpleService: Fixture = {
|
|
37
33
|
name: 'accounts',
|
|
38
34
|
url: 'http://localhost:4001',
|
|
39
35
|
typeDefs: gql`
|
|
@@ -137,28 +133,39 @@ it('Updates Supergraph SDL from remote storage', async () => {
|
|
|
137
133
|
|
|
138
134
|
// This test is only interested in the second time the gateway notifies of an
|
|
139
135
|
// update, since the first happens on load.
|
|
140
|
-
|
|
141
|
-
const secondUpdate = new Promise((res) => (secondUpdateResolve = res));
|
|
142
|
-
const schemaChangeCallback = jest
|
|
143
|
-
.fn()
|
|
144
|
-
.mockImplementationOnce(() => {})
|
|
145
|
-
.mockImplementationOnce(() => {
|
|
146
|
-
secondUpdateResolve();
|
|
147
|
-
});
|
|
136
|
+
const secondUpdate = resolvable();
|
|
148
137
|
|
|
149
138
|
gateway = new ApolloGateway({
|
|
150
139
|
logger,
|
|
151
140
|
uplinkEndpoints: [mockCloudConfigUrl1],
|
|
152
141
|
});
|
|
153
|
-
//
|
|
154
|
-
gateway
|
|
155
|
-
|
|
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
|
+
);
|
|
156
155
|
|
|
157
156
|
await gateway.load(mockApolloConfig);
|
|
158
|
-
expect(gateway['compositionId']).toMatchInlineSnapshot(`"originalId-1234"`);
|
|
159
157
|
|
|
160
158
|
await secondUpdate;
|
|
161
|
-
|
|
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();
|
|
162
169
|
});
|
|
163
170
|
|
|
164
171
|
describe('Supergraph SDL update failures', () => {
|
|
@@ -168,7 +175,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
168
175
|
gateway = new ApolloGateway({
|
|
169
176
|
logger,
|
|
170
177
|
uplinkEndpoints: [mockCloudConfigUrl1],
|
|
171
|
-
uplinkMaxRetries: 0
|
|
178
|
+
uplinkMaxRetries: 0,
|
|
172
179
|
});
|
|
173
180
|
|
|
174
181
|
await expect(
|
|
@@ -190,24 +197,23 @@ describe('Supergraph SDL update failures', () => {
|
|
|
190
197
|
mockSupergraphSdlRequestIfAfter('originalId-1234').reply(500);
|
|
191
198
|
|
|
192
199
|
// Spy on logger.error so we can just await once it's been called
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
logger.error = jest.fn(() => errorLogged());
|
|
200
|
+
const errorLoggedPromise = resolvable();
|
|
201
|
+
logger.error = jest.fn(() => errorLoggedPromise.resolve());
|
|
196
202
|
|
|
197
203
|
gateway = new ApolloGateway({
|
|
198
204
|
logger,
|
|
199
205
|
uplinkEndpoints: [mockCloudConfigUrl1],
|
|
200
|
-
uplinkMaxRetries: 0
|
|
206
|
+
uplinkMaxRetries: 0,
|
|
201
207
|
});
|
|
202
208
|
|
|
203
|
-
//
|
|
204
|
-
gateway
|
|
209
|
+
// for testing purposes, a short pollInterval is ideal so we'll override here
|
|
210
|
+
gateway['pollIntervalInMs'] = 100;
|
|
205
211
|
|
|
206
212
|
await gateway.load(mockApolloConfig);
|
|
207
213
|
await errorLoggedPromise;
|
|
208
214
|
|
|
209
215
|
expect(logger.error).toHaveBeenCalledWith(
|
|
210
|
-
'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',
|
|
211
217
|
);
|
|
212
218
|
});
|
|
213
219
|
|
|
@@ -224,25 +230,22 @@ describe('Supergraph SDL update failures', () => {
|
|
|
224
230
|
});
|
|
225
231
|
|
|
226
232
|
// Spy on logger.error so we can just await once it's been called
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
logger.error = jest.fn(() => errorLogged());
|
|
233
|
+
const errorLoggedPromise = resolvable();
|
|
234
|
+
logger.error = jest.fn(() => errorLoggedPromise.resolve());
|
|
230
235
|
|
|
231
236
|
gateway = new ApolloGateway({
|
|
232
237
|
logger,
|
|
233
238
|
uplinkEndpoints: [mockCloudConfigUrl1],
|
|
234
|
-
uplinkMaxRetries: 0
|
|
239
|
+
uplinkMaxRetries: 0,
|
|
235
240
|
});
|
|
236
|
-
//
|
|
237
|
-
gateway
|
|
241
|
+
// for testing purposes, a short pollInterval is ideal so we'll override here
|
|
242
|
+
gateway['pollIntervalInMs'] = 100;
|
|
238
243
|
|
|
239
244
|
await gateway.load(mockApolloConfig);
|
|
240
245
|
await errorLoggedPromise;
|
|
241
246
|
|
|
242
247
|
expect(logger.error).toHaveBeenCalledWith(
|
|
243
|
-
|
|
244
|
-
'\n' +
|
|
245
|
-
'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".`,
|
|
246
249
|
);
|
|
247
250
|
});
|
|
248
251
|
|
|
@@ -262,22 +265,21 @@ describe('Supergraph SDL update failures', () => {
|
|
|
262
265
|
);
|
|
263
266
|
|
|
264
267
|
// Spy on logger.error so we can just await once it's been called
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
logger.error = jest.fn(() => errorLogged());
|
|
268
|
+
const errorLoggedPromise = resolvable();
|
|
269
|
+
logger.error = jest.fn(() => errorLoggedPromise.resolve());
|
|
268
270
|
|
|
269
271
|
gateway = new ApolloGateway({
|
|
270
272
|
logger,
|
|
271
273
|
uplinkEndpoints: [mockCloudConfigUrl1],
|
|
272
274
|
});
|
|
273
|
-
//
|
|
274
|
-
gateway
|
|
275
|
+
// for testing purposes, a short pollInterval is ideal so we'll override here
|
|
276
|
+
gateway['pollIntervalInMs'] = 100;
|
|
275
277
|
|
|
276
278
|
await gateway.load(mockApolloConfig);
|
|
277
279
|
await errorLoggedPromise;
|
|
278
280
|
|
|
279
281
|
expect(logger.error).toHaveBeenCalledWith(
|
|
280
|
-
'Syntax Error: Unexpected Name "Syntax".',
|
|
282
|
+
'UplinkFetcher failed to update supergraph with the following error: Syntax Error: Unexpected Name "Syntax".',
|
|
281
283
|
);
|
|
282
284
|
expect(gateway.schema).toBeTruthy();
|
|
283
285
|
});
|
|
@@ -300,8 +302,8 @@ describe('Supergraph SDL update failures', () => {
|
|
|
300
302
|
logger,
|
|
301
303
|
uplinkEndpoints: [mockCloudConfigUrl1],
|
|
302
304
|
});
|
|
303
|
-
//
|
|
304
|
-
gateway
|
|
305
|
+
// for testing purposes, a short pollInterval is ideal so we'll override here
|
|
306
|
+
gateway['pollIntervalInMs'] = 100;
|
|
305
307
|
|
|
306
308
|
await expect(
|
|
307
309
|
gateway.load(mockApolloConfig),
|
|
@@ -327,25 +329,22 @@ it('Rollsback to a previous schema when triggered', async () => {
|
|
|
327
329
|
);
|
|
328
330
|
mockSupergraphSdlRequestSuccessIfAfter('updatedId-5678');
|
|
329
331
|
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
const firstSchemaChangeBlocker = new Promise((res) => (firstResolve = res));
|
|
334
|
-
const secondSchemaChangeBlocker = new Promise((res) => (secondResolve = res));
|
|
335
|
-
const thirdSchemaChangeBlocker = new Promise((res) => (thirdResolve = res));
|
|
332
|
+
const firstSchemaChangeBlocker = resolvable();
|
|
333
|
+
const secondSchemaChangeBlocker = resolvable();
|
|
334
|
+
const thirdSchemaChangeBlocker = resolvable();
|
|
336
335
|
|
|
337
336
|
const onChange = jest
|
|
338
337
|
.fn()
|
|
339
|
-
.mockImplementationOnce(() =>
|
|
340
|
-
.mockImplementationOnce(() =>
|
|
341
|
-
.mockImplementationOnce(() =>
|
|
338
|
+
.mockImplementationOnce(() => firstSchemaChangeBlocker.resolve())
|
|
339
|
+
.mockImplementationOnce(() => secondSchemaChangeBlocker.resolve())
|
|
340
|
+
.mockImplementationOnce(() => thirdSchemaChangeBlocker.resolve());
|
|
342
341
|
|
|
343
342
|
gateway = new ApolloGateway({
|
|
344
343
|
logger,
|
|
345
344
|
uplinkEndpoints: [mockCloudConfigUrl1],
|
|
346
345
|
});
|
|
347
|
-
//
|
|
348
|
-
gateway
|
|
346
|
+
// for testing purposes, a short pollInterval is ideal so we'll override here
|
|
347
|
+
gateway['pollIntervalInMs'] = 100;
|
|
349
348
|
|
|
350
349
|
gateway.onSchemaChange(onChange);
|
|
351
350
|
await gateway.load(mockApolloConfig);
|
|
@@ -402,9 +401,8 @@ describe('Downstream service health checks', () => {
|
|
|
402
401
|
var err = e;
|
|
403
402
|
}
|
|
404
403
|
|
|
405
|
-
// TODO: smell that we should be awaiting something else
|
|
406
404
|
expect(err.message).toMatchInlineSnapshot(`
|
|
407
|
-
"The gateway
|
|
405
|
+
"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:
|
|
408
406
|
[accounts]: 500: Internal Server Error"
|
|
409
407
|
`);
|
|
410
408
|
|
|
@@ -428,8 +426,8 @@ describe('Downstream service health checks', () => {
|
|
|
428
426
|
logger,
|
|
429
427
|
uplinkEndpoints: [mockCloudConfigUrl1],
|
|
430
428
|
});
|
|
431
|
-
//
|
|
432
|
-
gateway
|
|
429
|
+
// for testing purposes, a short pollInterval is ideal so we'll override here
|
|
430
|
+
gateway['pollIntervalInMs'] = 100;
|
|
433
431
|
|
|
434
432
|
await gateway.load(mockApolloConfig);
|
|
435
433
|
await gateway.stop();
|
|
@@ -468,7 +466,7 @@ describe('Downstream service health checks', () => {
|
|
|
468
466
|
|
|
469
467
|
// TODO: smell that we should be awaiting something else
|
|
470
468
|
expect(err.message).toMatchInlineSnapshot(`
|
|
471
|
-
"The gateway
|
|
469
|
+
"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:
|
|
472
470
|
[accounts]: 500: Internal Server Error"
|
|
473
471
|
`);
|
|
474
472
|
|
|
@@ -497,22 +495,20 @@ describe('Downstream service health checks', () => {
|
|
|
497
495
|
);
|
|
498
496
|
mockAllServicesHealthCheckSuccess();
|
|
499
497
|
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
const schemaChangeBlocker1 = new Promise((res) => (resolve1 = res));
|
|
503
|
-
const schemaChangeBlocker2 = new Promise((res) => (resolve2 = res));
|
|
498
|
+
const schemaChangeBlocker1 = resolvable();
|
|
499
|
+
const schemaChangeBlocker2 = resolvable();
|
|
504
500
|
const onChange = jest
|
|
505
501
|
.fn()
|
|
506
|
-
.mockImplementationOnce(() =>
|
|
507
|
-
.mockImplementationOnce(() =>
|
|
502
|
+
.mockImplementationOnce(() => schemaChangeBlocker1.resolve())
|
|
503
|
+
.mockImplementationOnce(() => schemaChangeBlocker2.resolve());
|
|
508
504
|
|
|
509
505
|
gateway = new ApolloGateway({
|
|
510
506
|
serviceHealthCheck: true,
|
|
511
507
|
logger,
|
|
512
508
|
uplinkEndpoints: [mockCloudConfigUrl1],
|
|
513
509
|
});
|
|
514
|
-
//
|
|
515
|
-
gateway
|
|
510
|
+
// for testing purposes, a short pollInterval is ideal so we'll override here
|
|
511
|
+
gateway['pollIntervalInMs'] = 100;
|
|
516
512
|
|
|
517
513
|
gateway.onSchemaChange(onChange);
|
|
518
514
|
await gateway.load(mockApolloConfig);
|
|
@@ -530,6 +526,10 @@ describe('Downstream service health checks', () => {
|
|
|
530
526
|
});
|
|
531
527
|
|
|
532
528
|
it('Preserves original schema when health check fails', async () => {
|
|
529
|
+
const errorLoggedPromise = resolvable();
|
|
530
|
+
const errorSpy = jest.fn(() => errorLoggedPromise.resolve());
|
|
531
|
+
logger.error = errorSpy;
|
|
532
|
+
|
|
533
533
|
mockSupergraphSdlRequestSuccess();
|
|
534
534
|
mockAllServicesHealthCheckSuccess();
|
|
535
535
|
|
|
@@ -546,57 +546,16 @@ describe('Downstream service health checks', () => {
|
|
|
546
546
|
mockServiceHealthCheckSuccess(reviews);
|
|
547
547
|
mockServiceHealthCheckSuccess(documents);
|
|
548
548
|
|
|
549
|
-
let resolve: Function;
|
|
550
|
-
const schemaChangeBlocker = new Promise((res) => (resolve = res));
|
|
551
|
-
|
|
552
549
|
gateway = new ApolloGateway({
|
|
553
550
|
serviceHealthCheck: true,
|
|
554
551
|
logger,
|
|
555
552
|
uplinkEndpoints: [mockCloudConfigUrl1],
|
|
556
553
|
});
|
|
557
|
-
//
|
|
558
|
-
gateway
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
// but the second call needs to handle the PromiseRejection. Typically for tests
|
|
563
|
-
// like these we would leverage the `gateway.onSchemaChange` callback to drive
|
|
564
|
-
// the test, but in this case, that callback isn't triggered when the update
|
|
565
|
-
// fails (as expected) so we get creative with the second mock as seen below.
|
|
566
|
-
const original = gateway.updateSchema;
|
|
567
|
-
const mockUpdateSchema = jest
|
|
568
|
-
.fn()
|
|
569
|
-
.mockImplementationOnce(async () => {
|
|
570
|
-
await original.apply(gateway);
|
|
571
|
-
})
|
|
572
|
-
.mockImplementationOnce(async () => {
|
|
573
|
-
// mock the first poll and handle the error which would otherwise be caught
|
|
574
|
-
// and logged from within the `pollServices` class method
|
|
575
|
-
|
|
576
|
-
// This is the ideal, but our version of Jest has a bug with printing error snapshots.
|
|
577
|
-
// See: https://github.com/facebook/jest/pull/10217 (fixed in v26.2.0)
|
|
578
|
-
// expect(original.apply(gateway)).rejects.toThrowErrorMatchingInlineSnapshot(`
|
|
579
|
-
// 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:
|
|
580
|
-
// [accounts]: 500: Internal Server Error"
|
|
581
|
-
// `);
|
|
582
|
-
// Instead we'll just use the regular snapshot matcher...
|
|
583
|
-
try {
|
|
584
|
-
await original.apply(gateway);
|
|
585
|
-
} catch (e) {
|
|
586
|
-
var err = e;
|
|
587
|
-
}
|
|
588
|
-
|
|
589
|
-
expect(err.message).toMatchInlineSnapshot(`
|
|
590
|
-
"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:
|
|
591
|
-
[accounts]: 500: Internal Server Error"
|
|
592
|
-
`);
|
|
593
|
-
// finally resolve the promise which drives this test
|
|
594
|
-
resolve();
|
|
595
|
-
});
|
|
596
|
-
|
|
597
|
-
// @ts-ignore for testing purposes, replace the `updateSchema`
|
|
598
|
-
// function on the gateway with our mock
|
|
599
|
-
gateway.updateSchema = mockUpdateSchema;
|
|
554
|
+
// for testing purposes, a short pollInterval is ideal so we'll override here
|
|
555
|
+
gateway['pollIntervalInMs'] = 100;
|
|
556
|
+
|
|
557
|
+
const updateSpy = jest.fn();
|
|
558
|
+
gateway.onSchemaLoadOrUpdate(() => updateSpy());
|
|
600
559
|
|
|
601
560
|
// load the gateway as usual
|
|
602
561
|
await gateway.load(mockApolloConfig);
|
|
@@ -605,11 +564,15 @@ describe('Downstream service health checks', () => {
|
|
|
605
564
|
expect(getRootQueryFields(gateway.schema)).toContain('topReviews');
|
|
606
565
|
expect(getRootQueryFields(gateway.schema)).not.toContain('review');
|
|
607
566
|
|
|
608
|
-
await
|
|
567
|
+
await errorLoggedPromise;
|
|
568
|
+
expect(logger.error).toHaveBeenCalledWith(
|
|
569
|
+
`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`,
|
|
570
|
+
);
|
|
609
571
|
|
|
610
572
|
// At this point, the mock update should have been called but the schema
|
|
611
573
|
// should still be the original.
|
|
612
|
-
expect(
|
|
574
|
+
expect(updateSpy).toHaveBeenCalledTimes(1);
|
|
575
|
+
|
|
613
576
|
expect(getRootQueryFields(gateway.schema)).toContain('topReviews');
|
|
614
577
|
expect(getRootQueryFields(gateway.schema)).not.toContain('review');
|
|
615
578
|
});
|
|
@@ -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' },
|