@apollo/gateway 2.0.5 → 2.1.0-alpha.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.map +1 -1
- package/dist/config.js +2 -0
- package/dist/config.js.map +1 -1
- package/dist/index.d.ts +7 -8
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +21 -46
- package/dist/index.js.map +1 -1
- package/dist/logger.d.ts +3 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +15 -0
- package/dist/logger.js.map +1 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts +61 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts.map +1 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/index.js +213 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/index.js.map +1 -0
- package/dist/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/loadSupergraphSdlFromStorage.d.ts +7 -4
- package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.d.ts.map +1 -0
- package/dist/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/loadSupergraphSdlFromStorage.js +5 -2
- package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.js.map +1 -0
- package/dist/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/outOfBandReporter.d.ts +0 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.d.ts.map +1 -0
- package/dist/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/outOfBandReporter.js +0 -0
- package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.js.map +1 -0
- package/dist/supergraphManagers/index.d.ts +2 -2
- package/dist/supergraphManagers/index.d.ts.map +1 -1
- package/dist/supergraphManagers/index.js +17 -4
- package/dist/supergraphManagers/index.js.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/integration/configuration.test.ts +0 -43
- package/src/__tests__/integration/managed.test.ts +289 -0
- package/src/__tests__/integration/networkRequests.test.ts +4 -31
- package/src/__tests__/integration/nockMocks.ts +7 -6
- package/src/config.ts +2 -0
- package/src/index.ts +26 -67
- package/src/logger.ts +11 -0
- package/src/supergraphManagers/UplinkSupergraphManager/__tests__/UplinkSupergraphManager.test.ts +67 -0
- package/src/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/__tests__/loadSupergraphSdlFromStorage.test.ts +0 -0
- package/src/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/__tests__/tsconfig.json +0 -0
- package/src/supergraphManagers/UplinkSupergraphManager/index.ts +306 -0
- package/src/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/loadSupergraphSdlFromStorage.ts +11 -3
- package/src/supergraphManagers/{UplinkFetcher → UplinkSupergraphManager}/outOfBandReporter.ts +0 -0
- package/src/supergraphManagers/index.ts +2 -2
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts +0 -35
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +0 -1
- package/dist/supergraphManagers/UplinkFetcher/index.js +0 -114
- package/dist/supergraphManagers/UplinkFetcher/index.js.map +0 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +0 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +0 -1
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +0 -1
- package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +0 -1
- package/src/supergraphManagers/UplinkFetcher/index.ts +0 -149
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import mockedEnv from 'mocked-env';
|
|
2
|
+
|
|
3
|
+
import { ApolloGateway, UplinkSupergraphManager } from '@apollo/gateway';
|
|
4
|
+
import { ApolloServer } from 'apollo-server';
|
|
5
|
+
import { ApolloServerPluginUsageReportingDisabled } from 'apollo-server-core';
|
|
6
|
+
|
|
7
|
+
import { nockAfterEach, nockBeforeEach } from '../nockAssertions';
|
|
8
|
+
import {
|
|
9
|
+
mockSupergraphSdlRequestSuccess,
|
|
10
|
+
graphRef,
|
|
11
|
+
apiKey,
|
|
12
|
+
mockSupergraphSdlRequest,
|
|
13
|
+
} from '../integration/nockMocks';
|
|
14
|
+
import { getTestingSupergraphSdl } from '../execution-utils';
|
|
15
|
+
|
|
16
|
+
let gateway: ApolloGateway | undefined;
|
|
17
|
+
let server: ApolloServer | undefined;
|
|
18
|
+
let cleanUp: (() => void) | undefined;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
nockBeforeEach();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
afterEach(async () => {
|
|
25
|
+
nockAfterEach();
|
|
26
|
+
|
|
27
|
+
if (server) {
|
|
28
|
+
await server.stop();
|
|
29
|
+
server = undefined;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
if (gateway) {
|
|
33
|
+
await gateway.stop();
|
|
34
|
+
gateway = undefined;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (cleanUp) {
|
|
38
|
+
cleanUp();
|
|
39
|
+
cleanUp = undefined;
|
|
40
|
+
}
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
const logger = {
|
|
44
|
+
warn: jest.fn(),
|
|
45
|
+
debug: jest.fn(),
|
|
46
|
+
error: jest.fn(),
|
|
47
|
+
info: jest.fn(),
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
describe('minimal gateway', () => {
|
|
51
|
+
it('uses managed federation', async () => {
|
|
52
|
+
cleanUp = mockedEnv({
|
|
53
|
+
APOLLO_KEY: apiKey,
|
|
54
|
+
APOLLO_GRAPH_REF: graphRef,
|
|
55
|
+
});
|
|
56
|
+
mockSupergraphSdlRequestSuccess({ url: /.*?apollographql.com/ });
|
|
57
|
+
|
|
58
|
+
gateway = new ApolloGateway({ logger });
|
|
59
|
+
server = new ApolloServer({
|
|
60
|
+
gateway,
|
|
61
|
+
plugins: [ApolloServerPluginUsageReportingDisabled()],
|
|
62
|
+
});
|
|
63
|
+
await server.listen({ port: 0 });
|
|
64
|
+
expect(gateway.supergraphManager).toBeInstanceOf(UplinkSupergraphManager);
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('fetches from provided `uplinkEndpoints`', async () => {
|
|
68
|
+
cleanUp = mockedEnv({
|
|
69
|
+
APOLLO_KEY: apiKey,
|
|
70
|
+
APOLLO_GRAPH_REF: graphRef,
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
const uplinkEndpoint = 'https://example.com';
|
|
74
|
+
mockSupergraphSdlRequestSuccess({ url: uplinkEndpoint });
|
|
75
|
+
|
|
76
|
+
gateway = new ApolloGateway({ logger, uplinkEndpoints: [uplinkEndpoint] });
|
|
77
|
+
server = new ApolloServer({
|
|
78
|
+
gateway,
|
|
79
|
+
plugins: [ApolloServerPluginUsageReportingDisabled()],
|
|
80
|
+
});
|
|
81
|
+
await server.listen({ port: 0 });
|
|
82
|
+
expect(gateway.supergraphManager).toBeInstanceOf(UplinkSupergraphManager);
|
|
83
|
+
const uplinkManager = gateway.supergraphManager as UplinkSupergraphManager;
|
|
84
|
+
expect(uplinkManager.uplinkEndpoints).toEqual([uplinkEndpoint]);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('fetches from (deprecated) provided `schemaConfigDeliveryEndpoint`', async () => {
|
|
88
|
+
cleanUp = mockedEnv({
|
|
89
|
+
APOLLO_KEY: apiKey,
|
|
90
|
+
APOLLO_GRAPH_REF: graphRef,
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
const schemaConfigDeliveryEndpoint = 'https://example.com';
|
|
94
|
+
mockSupergraphSdlRequestSuccess({ url: schemaConfigDeliveryEndpoint });
|
|
95
|
+
|
|
96
|
+
gateway = new ApolloGateway({ logger, schemaConfigDeliveryEndpoint });
|
|
97
|
+
server = new ApolloServer({
|
|
98
|
+
gateway,
|
|
99
|
+
plugins: [ApolloServerPluginUsageReportingDisabled()],
|
|
100
|
+
});
|
|
101
|
+
await server.listen({ port: 0 });
|
|
102
|
+
expect(gateway.supergraphManager).toBeInstanceOf(UplinkSupergraphManager);
|
|
103
|
+
const uplinkManager = gateway.supergraphManager as UplinkSupergraphManager;
|
|
104
|
+
expect(uplinkManager.uplinkEndpoints).toEqual([
|
|
105
|
+
schemaConfigDeliveryEndpoint,
|
|
106
|
+
]);
|
|
107
|
+
});
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
describe('Managed gateway with explicit UplinkSupergraphManager', () => {
|
|
111
|
+
it('waits for supergraph schema to load', async () => {
|
|
112
|
+
mockSupergraphSdlRequestSuccess({ url: /.*?apollographql.com/ });
|
|
113
|
+
|
|
114
|
+
gateway = new ApolloGateway({
|
|
115
|
+
logger,
|
|
116
|
+
supergraphSdl: new UplinkSupergraphManager({
|
|
117
|
+
apiKey,
|
|
118
|
+
graphRef,
|
|
119
|
+
logger,
|
|
120
|
+
}),
|
|
121
|
+
});
|
|
122
|
+
await expect(gateway.load()).resolves.not.toThrow();
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
it('invokes callback if uplink throws an error during init', async () => {
|
|
126
|
+
mockSupergraphSdlRequest(null, /.*?apollographql.com/).reply(500);
|
|
127
|
+
|
|
128
|
+
const supergraphSchema = getTestingSupergraphSdl();
|
|
129
|
+
let hasFired;
|
|
130
|
+
gateway = new ApolloGateway({
|
|
131
|
+
logger,
|
|
132
|
+
supergraphSdl: new UplinkSupergraphManager({
|
|
133
|
+
apiKey,
|
|
134
|
+
graphRef,
|
|
135
|
+
logger,
|
|
136
|
+
maxRetries: 0,
|
|
137
|
+
fallbackPollIntervalInMs: 0,
|
|
138
|
+
async onFailureToFetchSupergraphSdlDuringInit() {
|
|
139
|
+
hasFired = true;
|
|
140
|
+
return supergraphSchema;
|
|
141
|
+
},
|
|
142
|
+
}),
|
|
143
|
+
});
|
|
144
|
+
|
|
145
|
+
await expect(gateway.load()).resolves.not.toThrow();
|
|
146
|
+
expect(gateway.__testing().supergraphSdl).toBe(supergraphSchema);
|
|
147
|
+
expect(hasFired).toBeTruthy();
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
it('invokes callback if uplink throws an error after init', async () => {
|
|
151
|
+
// This is kinda wonky to read: we're responding the first time with success, then the next fetch should fail
|
|
152
|
+
mockSupergraphSdlRequestSuccess({ url: /.*?apollographql.com/ })
|
|
153
|
+
.post('/')
|
|
154
|
+
.reply(500);
|
|
155
|
+
|
|
156
|
+
const supergraphSchema = getTestingSupergraphSdl();
|
|
157
|
+
let hasFired;
|
|
158
|
+
gateway = new ApolloGateway({
|
|
159
|
+
logger,
|
|
160
|
+
supergraphSdl: new UplinkSupergraphManager({
|
|
161
|
+
apiKey,
|
|
162
|
+
graphRef,
|
|
163
|
+
logger,
|
|
164
|
+
maxRetries: 0,
|
|
165
|
+
fallbackPollIntervalInMs: 0,
|
|
166
|
+
async onFailureToFetchSupergraphSdlAfterInit() {
|
|
167
|
+
hasFired = true;
|
|
168
|
+
return supergraphSchema;
|
|
169
|
+
},
|
|
170
|
+
}),
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
await expect(gateway.load()).resolves.not.toThrow();
|
|
174
|
+
expect(hasFired).toBeFalsy();
|
|
175
|
+
|
|
176
|
+
const uplinkManager = gateway.supergraphManager as UplinkSupergraphManager;
|
|
177
|
+
await uplinkManager.nextFetch();
|
|
178
|
+
|
|
179
|
+
expect(hasFired).toBeTruthy();
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it.each([
|
|
183
|
+
['x', 'Syntax Error: Unexpected Name "x".'],
|
|
184
|
+
['', 'Syntax Error: Unexpected <EOF>.'],
|
|
185
|
+
[' ', 'Syntax Error: Unexpected <EOF>.'],
|
|
186
|
+
['type Query {hi: String}', 'Invalid supergraph: must be a core schema'],
|
|
187
|
+
])(
|
|
188
|
+
'throws if invalid supergraph schema returned from callback during init: %p',
|
|
189
|
+
async (schemaText, expectedMessage) => {
|
|
190
|
+
mockSupergraphSdlRequest(null, /.*?apollographql.com/).reply(500);
|
|
191
|
+
|
|
192
|
+
gateway = new ApolloGateway({
|
|
193
|
+
logger,
|
|
194
|
+
supergraphSdl: new UplinkSupergraphManager({
|
|
195
|
+
apiKey,
|
|
196
|
+
graphRef,
|
|
197
|
+
logger,
|
|
198
|
+
maxRetries: 0,
|
|
199
|
+
fallbackPollIntervalInMs: 0,
|
|
200
|
+
async onFailureToFetchSupergraphSdlDuringInit() {
|
|
201
|
+
return schemaText;
|
|
202
|
+
},
|
|
203
|
+
}),
|
|
204
|
+
});
|
|
205
|
+
|
|
206
|
+
await expect(gateway.load()).rejects.toThrowError(expectedMessage);
|
|
207
|
+
},
|
|
208
|
+
);
|
|
209
|
+
|
|
210
|
+
it.each([
|
|
211
|
+
['x', 'Syntax Error: Unexpected Name "x".'],
|
|
212
|
+
[' ', 'Syntax Error: Unexpected <EOF>.'],
|
|
213
|
+
['type Query {hi: String}', 'Invalid supergraph: must be a core schema'],
|
|
214
|
+
])(
|
|
215
|
+
'throws if invalid supergraph schema returned from callback after init: %p',
|
|
216
|
+
async (schemaText, expectedMessage) => {
|
|
217
|
+
// This is kinda wonky to read: we're responding the first time with success, then the next fetch should fail
|
|
218
|
+
mockSupergraphSdlRequestSuccess({ url: /.*?apollographql.com/ })
|
|
219
|
+
.post('/')
|
|
220
|
+
.reply(500);
|
|
221
|
+
|
|
222
|
+
let hasFired;
|
|
223
|
+
gateway = new ApolloGateway({
|
|
224
|
+
logger,
|
|
225
|
+
supergraphSdl: new UplinkSupergraphManager({
|
|
226
|
+
apiKey,
|
|
227
|
+
graphRef,
|
|
228
|
+
logger,
|
|
229
|
+
maxRetries: 0,
|
|
230
|
+
fallbackPollIntervalInMs: 0,
|
|
231
|
+
async onFailureToFetchSupergraphSdlAfterInit() {
|
|
232
|
+
hasFired = true;
|
|
233
|
+
return schemaText;
|
|
234
|
+
},
|
|
235
|
+
}),
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
await expect(gateway.load()).resolves.not.toThrow();
|
|
239
|
+
expect(hasFired).toBeFalsy();
|
|
240
|
+
|
|
241
|
+
const uplinkManager =
|
|
242
|
+
gateway.supergraphManager as UplinkSupergraphManager;
|
|
243
|
+
await uplinkManager.nextFetch();
|
|
244
|
+
|
|
245
|
+
expect(hasFired).toBeTruthy();
|
|
246
|
+
expect(logger.error).toBeCalledWith(
|
|
247
|
+
`UplinkSupergraphManager failed to update supergraph with the following error: ${expectedMessage}`,
|
|
248
|
+
);
|
|
249
|
+
},
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
it.each([null, ''])(
|
|
253
|
+
'uses existing supergraph schema is false-y value returned from callback after init: %p',
|
|
254
|
+
async (schemaText) => {
|
|
255
|
+
// This is kinda wonky to read: we're responding the first time with success, then the next fetch should fail
|
|
256
|
+
mockSupergraphSdlRequestSuccess({ url: /.*?apollographql.com/ })
|
|
257
|
+
.post('/')
|
|
258
|
+
.reply(500);
|
|
259
|
+
|
|
260
|
+
let hasFired;
|
|
261
|
+
gateway = new ApolloGateway({
|
|
262
|
+
logger,
|
|
263
|
+
supergraphSdl: new UplinkSupergraphManager({
|
|
264
|
+
apiKey,
|
|
265
|
+
graphRef,
|
|
266
|
+
logger,
|
|
267
|
+
maxRetries: 0,
|
|
268
|
+
fallbackPollIntervalInMs: 0,
|
|
269
|
+
async onFailureToFetchSupergraphSdlAfterInit() {
|
|
270
|
+
hasFired = true;
|
|
271
|
+
return schemaText;
|
|
272
|
+
},
|
|
273
|
+
}),
|
|
274
|
+
});
|
|
275
|
+
|
|
276
|
+
await expect(gateway.load()).resolves.not.toThrow();
|
|
277
|
+
expect(hasFired).toBeFalsy();
|
|
278
|
+
|
|
279
|
+
const uplinkManager =
|
|
280
|
+
gateway.supergraphManager as UplinkSupergraphManager;
|
|
281
|
+
await uplinkManager.nextFetch();
|
|
282
|
+
|
|
283
|
+
expect(hasFired).toBeTruthy();
|
|
284
|
+
|
|
285
|
+
const supergraphSchema = getTestingSupergraphSdl();
|
|
286
|
+
expect(gateway.__testing().supergraphSdl).toBe(supergraphSchema);
|
|
287
|
+
},
|
|
288
|
+
);
|
|
289
|
+
});
|
|
@@ -183,13 +183,6 @@ describe('Supergraph SDL update failures', () => {
|
|
|
183
183
|
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
184
184
|
`"An error occurred while fetching your schema from Apollo: 401 Unauthorized"`,
|
|
185
185
|
);
|
|
186
|
-
|
|
187
|
-
await expect(gateway.stop()).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
188
|
-
`"ApolloGateway.stop does not need to be called before ApolloGateway.load is called successfully"`,
|
|
189
|
-
);
|
|
190
|
-
// Set to `null` so we don't try to call `stop` on it in the `afterEach`,
|
|
191
|
-
// which triggers a different error that we're not testing for here.
|
|
192
|
-
gateway = null;
|
|
193
186
|
});
|
|
194
187
|
|
|
195
188
|
it('Handles arbitrary fetch failures (non 200 response)', async () => {
|
|
@@ -213,7 +206,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
213
206
|
await errorLoggedPromise;
|
|
214
207
|
|
|
215
208
|
expect(logger.error).toHaveBeenCalledWith(
|
|
216
|
-
'
|
|
209
|
+
'UplinkSupergraphManager failed to update supergraph with the following error: An error occurred while fetching your schema from Apollo: 500 Internal Server Error',
|
|
217
210
|
);
|
|
218
211
|
});
|
|
219
212
|
|
|
@@ -245,7 +238,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
245
238
|
await errorLoggedPromise;
|
|
246
239
|
|
|
247
240
|
expect(logger.error).toHaveBeenCalledWith(
|
|
248
|
-
`
|
|
241
|
+
`UplinkSupergraphManager failed to update supergraph with the following error: An error occurred while fetching your schema from Apollo: \nCannot query field "fail" on type "Query".`,
|
|
249
242
|
);
|
|
250
243
|
});
|
|
251
244
|
|
|
@@ -279,7 +272,7 @@ describe('Supergraph SDL update failures', () => {
|
|
|
279
272
|
await errorLoggedPromise;
|
|
280
273
|
|
|
281
274
|
expect(logger.error).toHaveBeenCalledWith(
|
|
282
|
-
'
|
|
275
|
+
'UplinkSupergraphManager failed to update supergraph with the following error: Syntax Error: Unexpected Name "Syntax".',
|
|
283
276
|
);
|
|
284
277
|
expect(gateway.schema).toBeTruthy();
|
|
285
278
|
});
|
|
@@ -312,10 +305,6 @@ describe('Supergraph SDL update failures', () => {
|
|
|
312
305
|
);
|
|
313
306
|
|
|
314
307
|
expect(gateway['state'].phase).toEqual('failed to load');
|
|
315
|
-
|
|
316
|
-
// Set to `null` so we don't try to call `stop` on it in the `afterEach`,
|
|
317
|
-
// which triggers a different error that we're not testing for here.
|
|
318
|
-
gateway = null;
|
|
319
308
|
});
|
|
320
309
|
});
|
|
321
310
|
|
|
@@ -406,14 +395,6 @@ describe('Downstream service health checks', () => {
|
|
|
406
395
|
"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:
|
|
407
396
|
[accounts]: 500: Internal Server Error"
|
|
408
397
|
`);
|
|
409
|
-
|
|
410
|
-
await expect(gateway.stop()).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
411
|
-
`"ApolloGateway.stop does not need to be called before ApolloGateway.load is called successfully"`,
|
|
412
|
-
);
|
|
413
|
-
|
|
414
|
-
// Set to `null` so we don't try to call `stop` on it in the `afterEach`,
|
|
415
|
-
// which triggers a different error that we're not testing for here.
|
|
416
|
-
gateway = null;
|
|
417
398
|
});
|
|
418
399
|
});
|
|
419
400
|
|
|
@@ -471,14 +452,6 @@ describe('Downstream service health checks', () => {
|
|
|
471
452
|
"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
453
|
[accounts]: 500: Internal Server Error"
|
|
473
454
|
`);
|
|
474
|
-
|
|
475
|
-
await expect(gateway.stop()).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
476
|
-
`"ApolloGateway.stop does not need to be called before ApolloGateway.load is called successfully"`,
|
|
477
|
-
);
|
|
478
|
-
|
|
479
|
-
// Set to `null` so we don't try to call `stop` on it in the `afterEach`,
|
|
480
|
-
// which triggers a different error that we're not testing for here.
|
|
481
|
-
gateway = null;
|
|
482
455
|
});
|
|
483
456
|
|
|
484
457
|
// This test has been flaky for a long time, and fails consistently after changes
|
|
@@ -568,7 +541,7 @@ describe('Downstream service health checks', () => {
|
|
|
568
541
|
|
|
569
542
|
await errorLoggedPromise;
|
|
570
543
|
expect(logger.error).toHaveBeenCalledWith(
|
|
571
|
-
`
|
|
544
|
+
`UplinkSupergraphManager 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
545
|
);
|
|
573
546
|
|
|
574
547
|
// At this point, the mock update should have been called but the schema
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import nock from 'nock';
|
|
2
2
|
import { HEALTH_CHECK_QUERY, SERVICE_DEFINITION_QUERY } from '../..';
|
|
3
|
-
import { SUPERGRAPH_SDL_QUERY } from '../../supergraphManagers/
|
|
3
|
+
import { SUPERGRAPH_SDL_QUERY } from '../../supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage';
|
|
4
4
|
import { getTestingSupergraphSdl } from '../../__tests__/execution-utils';
|
|
5
5
|
import { print } from 'graphql';
|
|
6
6
|
import { Fixture, fixtures as testingFixtures } from 'apollo-federation-integration-testsuite';
|
|
@@ -83,7 +83,7 @@ export const mockCloudConfigUrl3 =
|
|
|
83
83
|
export const mockOutOfBandReporterUrl =
|
|
84
84
|
'https://example.outofbandreporter.com/monitoring/';
|
|
85
85
|
|
|
86
|
-
export function mockSupergraphSdlRequestIfAfter(ifAfter: string | null, url: string = mockCloudConfigUrl1) {
|
|
86
|
+
export function mockSupergraphSdlRequestIfAfter(ifAfter: string | null, url: string | RegExp = mockCloudConfigUrl1) {
|
|
87
87
|
return gatewayNock(url).post('/', {
|
|
88
88
|
query: SUPERGRAPH_SDL_QUERY,
|
|
89
89
|
variables: {
|
|
@@ -94,7 +94,7 @@ export function mockSupergraphSdlRequestIfAfter(ifAfter: string | null, url: str
|
|
|
94
94
|
});
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
export function mockSupergraphSdlRequest(ifAfter: string | null = null, url: string = mockCloudConfigUrl1) {
|
|
97
|
+
export function mockSupergraphSdlRequest(ifAfter: string | null = null, url: string | RegExp = mockCloudConfigUrl1) {
|
|
98
98
|
return mockSupergraphSdlRequestIfAfter(ifAfter, url);
|
|
99
99
|
}
|
|
100
100
|
|
|
@@ -102,11 +102,12 @@ export function mockSupergraphSdlRequestSuccessIfAfter(
|
|
|
102
102
|
ifAfter: string | null = null,
|
|
103
103
|
id: string = 'originalId-1234',
|
|
104
104
|
supergraphSdl: string = getTestingSupergraphSdl(),
|
|
105
|
+
url: string | RegExp = mockCloudConfigUrl1,
|
|
105
106
|
) {
|
|
106
107
|
if (supergraphSdl == null) {
|
|
107
108
|
supergraphSdl = getTestingSupergraphSdl();
|
|
108
109
|
}
|
|
109
|
-
return mockSupergraphSdlRequestIfAfter(ifAfter).reply(
|
|
110
|
+
return mockSupergraphSdlRequestIfAfter(ifAfter, url).reply(
|
|
110
111
|
200,
|
|
111
112
|
JSON.stringify({
|
|
112
113
|
data: {
|
|
@@ -136,8 +137,8 @@ export function mockSupergraphSdlRequestIfAfterUnchanged(
|
|
|
136
137
|
);
|
|
137
138
|
}
|
|
138
139
|
|
|
139
|
-
export function mockSupergraphSdlRequestSuccess() {
|
|
140
|
-
return mockSupergraphSdlRequestSuccessIfAfter(null);
|
|
140
|
+
export function mockSupergraphSdlRequestSuccess({supergraphSdl = getTestingSupergraphSdl(), url = mockCloudConfigUrl1}: {supergraphSdl?: string, url?: string | RegExp} = {}) {
|
|
141
|
+
return mockSupergraphSdlRequestSuccessIfAfter(null, undefined, supergraphSdl, url);
|
|
141
142
|
}
|
|
142
143
|
|
|
143
144
|
export function mockOutOfBandReportRequest() {
|
package/src/config.ts
CHANGED
|
@@ -8,6 +8,7 @@ import { OperationContext } from './operationContext';
|
|
|
8
8
|
import { ServiceMap } from './executeQueryPlan';
|
|
9
9
|
import { ServiceDefinition } from "@apollo/federation-internals";
|
|
10
10
|
import { Fetcher } from '@apollo/utils.fetcher';
|
|
11
|
+
import { UplinkSupergraphManager } from './supergraphManagers';
|
|
11
12
|
|
|
12
13
|
export type ServiceEndpointDefinition = Pick<ServiceDefinition, 'name' | 'url'>;
|
|
13
14
|
|
|
@@ -325,6 +326,7 @@ export function isManagedConfig(
|
|
|
325
326
|
'schemaConfigDeliveryEndpoint' in config ||
|
|
326
327
|
'uplinkEndpoints' in config ||
|
|
327
328
|
'fallbackPollIntervalInMs' in config ||
|
|
329
|
+
(isSupergraphManagerConfig(config) && config.supergraphSdl instanceof UplinkSupergraphManager) ||
|
|
328
330
|
(!isLocalConfig(config) &&
|
|
329
331
|
!isStaticSupergraphSdlConfig(config) &&
|
|
330
332
|
!isManuallyManagedConfig(config))
|
package/src/index.ts
CHANGED
|
@@ -13,7 +13,6 @@ import {
|
|
|
13
13
|
GraphQLSchema,
|
|
14
14
|
VariableDefinitionNode,
|
|
15
15
|
} from 'graphql';
|
|
16
|
-
import loglevel from 'loglevel';
|
|
17
16
|
import { buildOperationContext, OperationContext } from './operationContext';
|
|
18
17
|
import {
|
|
19
18
|
executeQueryPlan,
|
|
@@ -26,7 +25,6 @@ import {
|
|
|
26
25
|
} from './datasources/types';
|
|
27
26
|
import { RemoteGraphQLDataSource } from './datasources/RemoteGraphQLDataSource';
|
|
28
27
|
import { getVariableValues } from 'graphql/execution/values';
|
|
29
|
-
import fetcher from 'make-fetch-happen';
|
|
30
28
|
import {
|
|
31
29
|
QueryPlanner,
|
|
32
30
|
QueryPlan,
|
|
@@ -46,7 +44,6 @@ import {
|
|
|
46
44
|
isManagedConfig,
|
|
47
45
|
SupergraphSdlUpdate,
|
|
48
46
|
isManuallyManagedSupergraphSdlGatewayConfig,
|
|
49
|
-
ManagedGatewayConfig,
|
|
50
47
|
isStaticSupergraphSdlConfig,
|
|
51
48
|
SupergraphManager,
|
|
52
49
|
} from './config';
|
|
@@ -55,7 +52,7 @@ import { OpenTelemetrySpanNames, tracer } from './utilities/opentelemetry';
|
|
|
55
52
|
import { addExtensions } from './schema-helper/addExtensions';
|
|
56
53
|
import {
|
|
57
54
|
IntrospectAndCompose,
|
|
58
|
-
|
|
55
|
+
UplinkSupergraphManager,
|
|
59
56
|
LegacyFetcher,
|
|
60
57
|
LocalCompose,
|
|
61
58
|
} from './supergraphManagers';
|
|
@@ -65,7 +62,7 @@ import {
|
|
|
65
62
|
Schema,
|
|
66
63
|
ServiceDefinition,
|
|
67
64
|
} from '@apollo/federation-internals';
|
|
68
|
-
import {
|
|
65
|
+
import { getDefaultLogger } from './logger';
|
|
69
66
|
|
|
70
67
|
type DataSourceMap = {
|
|
71
68
|
[serviceName: string]: { url?: string; dataSource: GraphQLDataSource };
|
|
@@ -145,9 +142,9 @@ export class ApolloGateway implements GraphQLService {
|
|
|
145
142
|
private warnedStates: WarnedStates = Object.create(null);
|
|
146
143
|
private queryPlanner?: QueryPlanner;
|
|
147
144
|
private supergraphSdl?: string;
|
|
148
|
-
private fetcher: Fetcher;
|
|
149
145
|
private compositionId?: string;
|
|
150
146
|
private state: GatewayState;
|
|
147
|
+
private _supergraphManager?: SupergraphManager;
|
|
151
148
|
|
|
152
149
|
// Observe query plan, service info, and operation info prior to execution.
|
|
153
150
|
// The information made available here will give insight into the resulting
|
|
@@ -169,11 +166,10 @@ export class ApolloGateway implements GraphQLService {
|
|
|
169
166
|
...config,
|
|
170
167
|
};
|
|
171
168
|
|
|
172
|
-
this.logger = this.
|
|
169
|
+
this.logger = this.config.logger ?? getDefaultLogger(this.config.debug);
|
|
173
170
|
this.queryPlanStore = this.initQueryPlanStore(
|
|
174
171
|
config?.experimental_approximateQueryPlanStoreMiB,
|
|
175
172
|
);
|
|
176
|
-
this.fetcher = config?.fetcher || fetcher;
|
|
177
173
|
|
|
178
174
|
// set up experimental observability callbacks and config settings
|
|
179
175
|
this.experimental_didResolveQueryPlan =
|
|
@@ -194,23 +190,8 @@ export class ApolloGateway implements GraphQLService {
|
|
|
194
190
|
this.state = { phase: 'initialized' };
|
|
195
191
|
}
|
|
196
192
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
if (this.config.logger) {
|
|
200
|
-
return this.config.logger;
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
// If the user didn't provide their own logger, we'll initialize one.
|
|
204
|
-
const loglevelLogger = loglevel.getLogger(`apollo-gateway`);
|
|
205
|
-
|
|
206
|
-
// And also support the `debug` option, if it's truthy.
|
|
207
|
-
if (this.config.debug === true) {
|
|
208
|
-
loglevelLogger.setLevel(loglevelLogger.levels.DEBUG);
|
|
209
|
-
} else {
|
|
210
|
-
loglevelLogger.setLevel(loglevelLogger.levels.WARN);
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
return loglevelLogger;
|
|
193
|
+
public get supergraphManager(): SupergraphManager | undefined {
|
|
194
|
+
return this._supergraphManager;
|
|
214
195
|
}
|
|
215
196
|
|
|
216
197
|
private initQueryPlanStore(approximateQueryPlanStoreMiB?: number) {
|
|
@@ -381,19 +362,21 @@ export class ApolloGateway implements GraphQLService {
|
|
|
381
362
|
'`serviceList`, `supergraphSdl`, and `experimental_updateServiceDefinitions`.',
|
|
382
363
|
);
|
|
383
364
|
}
|
|
384
|
-
const uplinkEndpoints = this.getUplinkEndpoints(this.config);
|
|
385
365
|
|
|
366
|
+
const schemaDeliveryEndpoints: string[] | undefined = this.config
|
|
367
|
+
.schemaConfigDeliveryEndpoint
|
|
368
|
+
? [this.config.schemaConfigDeliveryEndpoint]
|
|
369
|
+
: undefined;
|
|
386
370
|
await this.initializeSupergraphManager(
|
|
387
|
-
new
|
|
371
|
+
new UplinkSupergraphManager({
|
|
388
372
|
graphRef: this.apolloConfig!.graphRef!,
|
|
389
373
|
apiKey: this.apolloConfig!.key!,
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
this.config.
|
|
393
|
-
|
|
394
|
-
fetcher: this.fetcher,
|
|
374
|
+
shouldRunSubgraphHealthcheck: this.config.serviceHealthCheck,
|
|
375
|
+
uplinkEndpoints:
|
|
376
|
+
this.config.uplinkEndpoints ?? schemaDeliveryEndpoints,
|
|
377
|
+
maxRetries: this.config.uplinkMaxRetries,
|
|
395
378
|
logger: this.logger,
|
|
396
|
-
fallbackPollIntervalInMs: this.pollIntervalInMs
|
|
379
|
+
fallbackPollIntervalInMs: this.pollIntervalInMs,
|
|
397
380
|
}),
|
|
398
381
|
);
|
|
399
382
|
}
|
|
@@ -415,29 +398,6 @@ export class ApolloGateway implements GraphQLService {
|
|
|
415
398
|
};
|
|
416
399
|
}
|
|
417
400
|
|
|
418
|
-
private getUplinkEndpoints(config: ManagedGatewayConfig) {
|
|
419
|
-
/**
|
|
420
|
-
* Configuration priority order:
|
|
421
|
-
* 1. `uplinkEndpoints` configuration option
|
|
422
|
-
* 2. (deprecated) `schemaConfigDeliveryEndpoint` configuration option
|
|
423
|
-
* 3. APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT environment variable
|
|
424
|
-
* 4. default (GCP and AWS)
|
|
425
|
-
*/
|
|
426
|
-
const rawEndpointsString =
|
|
427
|
-
process.env.APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT;
|
|
428
|
-
const envEndpoints = rawEndpointsString?.split(',') ?? null;
|
|
429
|
-
return (
|
|
430
|
-
config.uplinkEndpoints ??
|
|
431
|
-
(config.schemaConfigDeliveryEndpoint
|
|
432
|
-
? [config.schemaConfigDeliveryEndpoint]
|
|
433
|
-
: null) ??
|
|
434
|
-
envEndpoints ?? [
|
|
435
|
-
'https://uplink.api.apollographql.com/',
|
|
436
|
-
'https://aws.uplink.api.apollographql.com/',
|
|
437
|
-
]
|
|
438
|
-
);
|
|
439
|
-
}
|
|
440
|
-
|
|
441
401
|
private getIdForSupergraphSdl(supergraphSdl: string) {
|
|
442
402
|
return createHash('sha256').update(supergraphSdl).digest('hex');
|
|
443
403
|
}
|
|
@@ -451,11 +411,6 @@ export class ApolloGateway implements GraphQLService {
|
|
|
451
411
|
healthCheck: this.externalSubgraphHealthCheckCallback.bind(this),
|
|
452
412
|
getDataSource: this.externalGetDataSourceCallback.bind(this),
|
|
453
413
|
});
|
|
454
|
-
if (!result?.supergraphSdl) {
|
|
455
|
-
throw new Error(
|
|
456
|
-
'Provided `supergraphSdl` function did not return an object containing a `supergraphSdl` property',
|
|
457
|
-
);
|
|
458
|
-
}
|
|
459
414
|
if (result?.cleanup) {
|
|
460
415
|
if (typeof result.cleanup === 'function') {
|
|
461
416
|
this.toDispose.push(result.cleanup);
|
|
@@ -473,6 +428,7 @@ export class ApolloGateway implements GraphQLService {
|
|
|
473
428
|
throw e;
|
|
474
429
|
}
|
|
475
430
|
|
|
431
|
+
this._supergraphManager = supergraphManager;
|
|
476
432
|
this.state = { phase: 'loaded' };
|
|
477
433
|
}
|
|
478
434
|
|
|
@@ -1008,9 +964,6 @@ export class ApolloGateway implements GraphQLService {
|
|
|
1008
964
|
switch (this.state.phase) {
|
|
1009
965
|
case 'initialized':
|
|
1010
966
|
case 'failed to load':
|
|
1011
|
-
throw Error(
|
|
1012
|
-
'ApolloGateway.stop does not need to be called before ApolloGateway.load is called successfully',
|
|
1013
|
-
);
|
|
1014
967
|
case 'stopped':
|
|
1015
968
|
// Calls to stop() are idempotent.
|
|
1016
969
|
return;
|
|
@@ -1105,6 +1058,7 @@ export {
|
|
|
1105
1058
|
CompositionInfo,
|
|
1106
1059
|
IntrospectAndCompose,
|
|
1107
1060
|
LocalCompose,
|
|
1061
|
+
UplinkSupergraphManager,
|
|
1108
1062
|
};
|
|
1109
1063
|
|
|
1110
1064
|
export * from './datasources';
|
|
@@ -1114,8 +1068,13 @@ export {
|
|
|
1114
1068
|
SubgraphHealthCheckFunction,
|
|
1115
1069
|
GetDataSourceFunction,
|
|
1116
1070
|
SupergraphSdlHook,
|
|
1117
|
-
SupergraphManager
|
|
1071
|
+
SupergraphManager,
|
|
1118
1072
|
} from './config';
|
|
1119
1073
|
|
|
1120
|
-
export {
|
|
1121
|
-
|
|
1074
|
+
export {
|
|
1075
|
+
UplinkFetcherError,
|
|
1076
|
+
FailureToFetchSupergraphSdlAfterInit,
|
|
1077
|
+
FailureToFetchSupergraphSdlDuringInit,
|
|
1078
|
+
FailureToFetchSupergraphSdlFunctionParams,
|
|
1079
|
+
DEFAULT_UPLINK_ENDPOINTS,
|
|
1080
|
+
} from './supergraphManagers';
|
package/src/logger.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import loglevel from 'loglevel';
|
|
2
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
3
|
+
|
|
4
|
+
export function getDefaultLogger(debug: boolean = true): Logger {
|
|
5
|
+
const logger = loglevel.getLogger('apollo-gateway');
|
|
6
|
+
|
|
7
|
+
const level = debug === true ? loglevel.levels.DEBUG : loglevel.levels.WARN;
|
|
8
|
+
logger.setLevel(level);
|
|
9
|
+
|
|
10
|
+
return logger;
|
|
11
|
+
}
|