@apollo/gateway 0.48.0 → 0.49.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/README.md +7 -5
- package/dist/__generated__/graphqlTypes.d.ts +4 -0
- package/dist/__generated__/graphqlTypes.d.ts.map +1 -1
- package/dist/__generated__/graphqlTypes.js.map +1 -1
- package/dist/config.d.ts +6 -2
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +1 -0
- package/dist/config.js.map +1 -1
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +4 -3
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +23 -10
- package/dist/index.js.map +1 -1
- package/dist/schema-helper/index.js +5 -1
- package/dist/schema-helper/index.js.map +1 -1
- package/dist/schema-helper/resolverMap.d.ts +2 -2
- package/dist/schema-helper/resolverMap.d.ts.map +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/index.js.map +1 -1
- package/dist/supergraphManagers/LegacyFetcher/index.js +1 -1
- package/dist/supergraphManagers/LegacyFetcher/index.js.map +1 -1
- package/dist/supergraphManagers/LocalCompose/index.js +1 -1
- package/dist/supergraphManagers/LocalCompose/index.js.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts +4 -1
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/index.js +22 -4
- package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts +7 -2
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js +37 -33
- package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -1
- package/dist/supergraphManagers/index.d.ts +1 -0
- package/dist/supergraphManagers/index.d.ts.map +1 -1
- package/dist/supergraphManagers/index.js +3 -1
- package/dist/supergraphManagers/index.js.map +1 -1
- package/package.json +6 -5
- package/src/__generated__/graphqlTypes.ts +11 -2
- package/src/__tests__/CucumberREADME.md +1 -0
- package/src/__tests__/build-query-plan-fragmentization.feature +10 -0
- package/src/__tests__/build-query-plan.feature +84 -16
- package/src/__tests__/buildQueryPlan.test.ts +272 -1
- package/src/__tests__/gateway/lifecycle-hooks.test.ts +3 -3
- package/src/__tests__/gateway/reporting.test.ts +4 -0
- package/src/__tests__/gateway/supergraphSdl.test.ts +3 -3
- package/src/__tests__/integration/abstract-types.test.ts +3 -3
- package/src/__tests__/integration/configuration.test.ts +0 -11
- package/src/__tests__/integration/nockMocks.ts +3 -2
- package/src/__tests__/integration/requires.test.ts +1 -1
- package/src/__tests__/integration/value-types.test.ts +1 -1
- package/src/config.ts +11 -6
- package/src/executeQueryPlan.ts +4 -0
- package/src/index.ts +16 -7
- package/src/schema-helper/resolverMap.ts +2 -2
- package/src/supergraphManagers/LegacyFetcher/index.ts +1 -1
- package/src/supergraphManagers/LocalCompose/index.ts +1 -1
- package/src/supergraphManagers/UplinkFetcher/__tests__/loadSupergraphSdlFromStorage.test.ts +123 -28
- package/src/supergraphManagers/UplinkFetcher/index.ts +39 -18
- package/src/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts +40 -27
- package/src/supergraphManagers/index.ts +1 -0
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
loadSupergraphSdlFromStorage,
|
|
3
|
-
loadSupergraphSdlFromUplinks
|
|
3
|
+
loadSupergraphSdlFromUplinks,
|
|
4
|
+
UplinkFetcherError,
|
|
4
5
|
} from '../loadSupergraphSdlFromStorage';
|
|
5
6
|
import { getDefaultFetcher } from '../../..';
|
|
6
7
|
import {
|
|
@@ -62,7 +63,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
62
63
|
errorReportingEndpoint: undefined,
|
|
63
64
|
fetcher,
|
|
64
65
|
compositionId: "originalId-1234",
|
|
65
|
-
maxRetries: 1
|
|
66
|
+
maxRetries: 1,
|
|
67
|
+
roundRobinSeed: 0,
|
|
68
|
+
earliestFetchTime: null,
|
|
66
69
|
});
|
|
67
70
|
|
|
68
71
|
expect(result).toMatchObject({
|
|
@@ -84,10 +87,14 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
84
87
|
errorReportingEndpoint: undefined,
|
|
85
88
|
fetcher,
|
|
86
89
|
compositionId: "originalId-1234",
|
|
87
|
-
maxRetries: 1
|
|
90
|
+
maxRetries: 1,
|
|
91
|
+
roundRobinSeed: 0,
|
|
92
|
+
earliestFetchTime: null,
|
|
88
93
|
}),
|
|
89
|
-
).rejects.
|
|
90
|
-
|
|
94
|
+
).rejects.toThrowError(
|
|
95
|
+
new UplinkFetcherError(
|
|
96
|
+
"An error occurred while fetching your schema from Apollo: 500 Internal Server Error",
|
|
97
|
+
)
|
|
91
98
|
);
|
|
92
99
|
})
|
|
93
100
|
|
|
@@ -105,8 +112,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
105
112
|
fetcher,
|
|
106
113
|
compositionId: null,
|
|
107
114
|
}),
|
|
108
|
-
).rejects.
|
|
109
|
-
|
|
115
|
+
).rejects.toThrowError(
|
|
116
|
+
new UplinkFetcherError(
|
|
117
|
+
"An error occurred while fetching your schema from Apollo: 200 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected token I in JSON at position 0"
|
|
118
|
+
)
|
|
110
119
|
);
|
|
111
120
|
});
|
|
112
121
|
|
|
@@ -129,7 +138,9 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
129
138
|
fetcher,
|
|
130
139
|
compositionId: null,
|
|
131
140
|
}),
|
|
132
|
-
).rejects.toThrowError(
|
|
141
|
+
).rejects.toThrowError(
|
|
142
|
+
new UplinkFetcherError(`An error occurred while fetching your schema from Apollo: \n${message}`)
|
|
143
|
+
);
|
|
133
144
|
});
|
|
134
145
|
|
|
135
146
|
it("throws on non-OK status codes when `errors` isn't present in a JSON response", async () => {
|
|
@@ -146,8 +157,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
146
157
|
fetcher,
|
|
147
158
|
compositionId: null,
|
|
148
159
|
}),
|
|
149
|
-
).rejects.
|
|
150
|
-
|
|
160
|
+
).rejects.toThrowError(
|
|
161
|
+
new UplinkFetcherError(
|
|
162
|
+
"An error occurred while fetching your schema from Apollo: 500 Internal Server Error"
|
|
163
|
+
)
|
|
151
164
|
);
|
|
152
165
|
});
|
|
153
166
|
|
|
@@ -166,8 +179,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
166
179
|
fetcher,
|
|
167
180
|
compositionId: null,
|
|
168
181
|
}),
|
|
169
|
-
).rejects.
|
|
170
|
-
|
|
182
|
+
).rejects.toThrowError(
|
|
183
|
+
new UplinkFetcherError(
|
|
184
|
+
"An error occurred while fetching your schema from Apollo: 400 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected end of JSON input",
|
|
185
|
+
)
|
|
171
186
|
);
|
|
172
187
|
});
|
|
173
188
|
|
|
@@ -185,8 +200,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
185
200
|
fetcher,
|
|
186
201
|
compositionId: null,
|
|
187
202
|
}),
|
|
188
|
-
).rejects.
|
|
189
|
-
|
|
203
|
+
).rejects.toThrowError(
|
|
204
|
+
new UplinkFetcherError(
|
|
205
|
+
"An error occurred while fetching your schema from Apollo: 400 invalid json response body at https://example1.cloud-config-url.com/cloudconfig/ reason: Unexpected end of JSON input",
|
|
206
|
+
)
|
|
190
207
|
);
|
|
191
208
|
});
|
|
192
209
|
|
|
@@ -204,8 +221,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
204
221
|
fetcher,
|
|
205
222
|
compositionId: null,
|
|
206
223
|
}),
|
|
207
|
-
).rejects.
|
|
208
|
-
|
|
224
|
+
).rejects.toThrowError(
|
|
225
|
+
new UplinkFetcherError(
|
|
226
|
+
"An error occurred while fetching your schema from Apollo: 413 Payload Too Large",
|
|
227
|
+
)
|
|
209
228
|
);
|
|
210
229
|
});
|
|
211
230
|
|
|
@@ -223,8 +242,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
223
242
|
fetcher,
|
|
224
243
|
compositionId: null,
|
|
225
244
|
}),
|
|
226
|
-
).rejects.
|
|
227
|
-
|
|
245
|
+
).rejects.toThrowError(
|
|
246
|
+
new UplinkFetcherError(
|
|
247
|
+
"An error occurred while fetching your schema from Apollo: 422 Unprocessable Entity",
|
|
248
|
+
)
|
|
228
249
|
);
|
|
229
250
|
});
|
|
230
251
|
|
|
@@ -242,8 +263,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
242
263
|
fetcher,
|
|
243
264
|
compositionId: null,
|
|
244
265
|
}),
|
|
245
|
-
).rejects.
|
|
246
|
-
|
|
266
|
+
).rejects.toThrowError(
|
|
267
|
+
new UplinkFetcherError(
|
|
268
|
+
"An error occurred while fetching your schema from Apollo: 408 Request Timeout",
|
|
269
|
+
)
|
|
247
270
|
);
|
|
248
271
|
});
|
|
249
272
|
});
|
|
@@ -263,8 +286,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
263
286
|
fetcher,
|
|
264
287
|
compositionId: null,
|
|
265
288
|
}),
|
|
266
|
-
).rejects.
|
|
267
|
-
|
|
289
|
+
).rejects.toThrowError(
|
|
290
|
+
new UplinkFetcherError(
|
|
291
|
+
"An error occurred while fetching your schema from Apollo: 504 Gateway Timeout",
|
|
292
|
+
)
|
|
268
293
|
);
|
|
269
294
|
});
|
|
270
295
|
|
|
@@ -282,8 +307,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
282
307
|
fetcher,
|
|
283
308
|
compositionId: null,
|
|
284
309
|
}),
|
|
285
|
-
).rejects.
|
|
286
|
-
|
|
310
|
+
).rejects.toThrowError(
|
|
311
|
+
new UplinkFetcherError(
|
|
312
|
+
"An error occurred while fetching your schema from Apollo: request to https://example1.cloud-config-url.com/cloudconfig/ failed, reason: no response",
|
|
313
|
+
)
|
|
287
314
|
);
|
|
288
315
|
});
|
|
289
316
|
|
|
@@ -301,8 +328,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
301
328
|
fetcher,
|
|
302
329
|
compositionId: null,
|
|
303
330
|
}),
|
|
304
|
-
).rejects.
|
|
305
|
-
|
|
331
|
+
).rejects.toThrowError(
|
|
332
|
+
new UplinkFetcherError(
|
|
333
|
+
"An error occurred while fetching your schema from Apollo: 502 Bad Gateway",
|
|
334
|
+
)
|
|
306
335
|
);
|
|
307
336
|
});
|
|
308
337
|
|
|
@@ -320,8 +349,10 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
320
349
|
fetcher,
|
|
321
350
|
compositionId: null,
|
|
322
351
|
}),
|
|
323
|
-
).rejects.
|
|
324
|
-
|
|
352
|
+
).rejects.toThrowError(
|
|
353
|
+
new UplinkFetcherError(
|
|
354
|
+
"An error occurred while fetching your schema from Apollo: 503 Service Unavailable",
|
|
355
|
+
)
|
|
325
356
|
);
|
|
326
357
|
});
|
|
327
358
|
|
|
@@ -341,3 +372,67 @@ describe('loadSupergraphSdlFromStorage', () => {
|
|
|
341
372
|
});
|
|
342
373
|
});
|
|
343
374
|
|
|
375
|
+
|
|
376
|
+
describe("loadSupergraphSdlFromUplinks", () => {
|
|
377
|
+
beforeEach(nockBeforeEach);
|
|
378
|
+
afterEach(nockAfterEach);
|
|
379
|
+
|
|
380
|
+
it("doesn't retry in the unchanged / null case", async () => {
|
|
381
|
+
mockSupergraphSdlRequestIfAfterUnchanged("id-1234", mockCloudConfigUrl1);
|
|
382
|
+
|
|
383
|
+
const fetcher = jest.fn(getDefaultFetcher());
|
|
384
|
+
const result = await loadSupergraphSdlFromUplinks({
|
|
385
|
+
graphRef,
|
|
386
|
+
apiKey,
|
|
387
|
+
endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
|
|
388
|
+
errorReportingEndpoint: mockOutOfBandReporterUrl,
|
|
389
|
+
fetcher: fetcher as any,
|
|
390
|
+
compositionId: "id-1234",
|
|
391
|
+
maxRetries: 5,
|
|
392
|
+
roundRobinSeed: 0,
|
|
393
|
+
earliestFetchTime: null,
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
expect(result).toBeNull();
|
|
397
|
+
expect(fetcher).toHaveBeenCalledTimes(1);
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
it("Waits the correct time before retrying", async () => {
|
|
401
|
+
const timeoutSpy = jest.spyOn(global, 'setTimeout');
|
|
402
|
+
|
|
403
|
+
mockSupergraphSdlRequest('originalId-1234', mockCloudConfigUrl1).reply(500);
|
|
404
|
+
mockSupergraphSdlRequestIfAfter('originalId-1234', mockCloudConfigUrl2).reply(
|
|
405
|
+
200,
|
|
406
|
+
JSON.stringify({
|
|
407
|
+
data: {
|
|
408
|
+
routerConfig: {
|
|
409
|
+
__typename: 'RouterConfigResult',
|
|
410
|
+
id: 'originalId-1234',
|
|
411
|
+
supergraphSdl: getTestingSupergraphSdl()
|
|
412
|
+
},
|
|
413
|
+
},
|
|
414
|
+
}),
|
|
415
|
+
);
|
|
416
|
+
const fetcher = getDefaultFetcher();
|
|
417
|
+
|
|
418
|
+
await loadSupergraphSdlFromUplinks({
|
|
419
|
+
graphRef,
|
|
420
|
+
apiKey,
|
|
421
|
+
endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
|
|
422
|
+
errorReportingEndpoint: undefined,
|
|
423
|
+
fetcher: fetcher,
|
|
424
|
+
compositionId: "originalId-1234",
|
|
425
|
+
maxRetries: 1,
|
|
426
|
+
roundRobinSeed: 0,
|
|
427
|
+
earliestFetchTime: new Date(Date.now() + 1000),
|
|
428
|
+
});
|
|
429
|
+
|
|
430
|
+
// test if setTimeout was called with a value in range to deal with time jitter
|
|
431
|
+
const setTimeoutCall = timeoutSpy.mock.calls[1][1];
|
|
432
|
+
expect(setTimeoutCall).toBeLessThanOrEqual(1000);
|
|
433
|
+
expect(setTimeoutCall).toBeGreaterThanOrEqual(900);
|
|
434
|
+
|
|
435
|
+
timeoutSpy.mockRestore();
|
|
436
|
+
});
|
|
437
|
+
});
|
|
438
|
+
|
|
@@ -6,7 +6,7 @@ import { SubgraphHealthCheckFunction, SupergraphSdlUpdateFunction } from '../..'
|
|
|
6
6
|
import { loadSupergraphSdlFromUplinks } from './loadSupergraphSdlFromStorage';
|
|
7
7
|
|
|
8
8
|
export interface UplinkFetcherOptions {
|
|
9
|
-
|
|
9
|
+
fallbackPollIntervalInMs: number;
|
|
10
10
|
subgraphHealthCheck?: boolean;
|
|
11
11
|
graphRef: string;
|
|
12
12
|
apiKey: string;
|
|
@@ -30,6 +30,9 @@ export class UplinkFetcher implements SupergraphManager {
|
|
|
30
30
|
private errorReportingEndpoint: string | undefined =
|
|
31
31
|
process.env.APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT ?? undefined;
|
|
32
32
|
private compositionId?: string;
|
|
33
|
+
private fetchCount: number = 0;
|
|
34
|
+
private minDelayMs: number | null = null;
|
|
35
|
+
private earliestFetchTime: Date | null = null;
|
|
33
36
|
|
|
34
37
|
constructor(options: UplinkFetcherOptions) {
|
|
35
38
|
this.config = options;
|
|
@@ -45,7 +48,12 @@ export class UplinkFetcher implements SupergraphManager {
|
|
|
45
48
|
|
|
46
49
|
let initialSupergraphSdl: string | null = null;
|
|
47
50
|
try {
|
|
48
|
-
|
|
51
|
+
const result = await this.updateSupergraphSdl();
|
|
52
|
+
initialSupergraphSdl = result?.supergraphSdl || null;
|
|
53
|
+
if (result?.minDelaySeconds) {
|
|
54
|
+
this.minDelayMs = 1000 * result?.minDelaySeconds;
|
|
55
|
+
this.earliestFetchTime = new Date(Date.now() + this.minDelayMs);
|
|
56
|
+
}
|
|
49
57
|
} catch (e) {
|
|
50
58
|
this.logUpdateFailure(e);
|
|
51
59
|
throw e;
|
|
@@ -81,6 +89,8 @@ export class UplinkFetcher implements SupergraphManager {
|
|
|
81
89
|
fetcher: this.config.fetcher,
|
|
82
90
|
compositionId: this.compositionId ?? null,
|
|
83
91
|
maxRetries: this.config.maxRetries,
|
|
92
|
+
roundRobinSeed: this.fetchCount++,
|
|
93
|
+
earliestFetchTime: this.earliestFetchTime,
|
|
84
94
|
});
|
|
85
95
|
|
|
86
96
|
if (!result) {
|
|
@@ -89,7 +99,8 @@ export class UplinkFetcher implements SupergraphManager {
|
|
|
89
99
|
this.compositionId = result.id;
|
|
90
100
|
// the healthCheck fn is only assigned if it's enabled in the config
|
|
91
101
|
await this.healthCheck?.(result.supergraphSdl);
|
|
92
|
-
|
|
102
|
+
const { supergraphSdl, minDelaySeconds } = result;
|
|
103
|
+
return { supergraphSdl, minDelaySeconds };
|
|
93
104
|
}
|
|
94
105
|
}
|
|
95
106
|
|
|
@@ -99,24 +110,34 @@ export class UplinkFetcher implements SupergraphManager {
|
|
|
99
110
|
}
|
|
100
111
|
|
|
101
112
|
private poll() {
|
|
102
|
-
this.timerRef = setTimeout(
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
113
|
+
this.timerRef = setTimeout(
|
|
114
|
+
async () => {
|
|
115
|
+
if (this.state.phase === 'polling') {
|
|
116
|
+
const pollingPromise = resolvable();
|
|
117
|
+
|
|
118
|
+
this.state.pollingPromise = pollingPromise;
|
|
119
|
+
try {
|
|
120
|
+
const result = await this.updateSupergraphSdl();
|
|
121
|
+
const maybeNewSupergraphSdl = result?.supergraphSdl || null;
|
|
122
|
+
if (result?.minDelaySeconds) {
|
|
123
|
+
this.minDelayMs = 1000 * result?.minDelaySeconds;
|
|
124
|
+
this.earliestFetchTime = new Date(Date.now() + this.minDelayMs);
|
|
125
|
+
}
|
|
126
|
+
if (maybeNewSupergraphSdl) {
|
|
127
|
+
this.update?.(maybeNewSupergraphSdl);
|
|
128
|
+
}
|
|
129
|
+
} catch (e) {
|
|
130
|
+
this.logUpdateFailure(e);
|
|
111
131
|
}
|
|
112
|
-
|
|
113
|
-
this.logUpdateFailure(e);
|
|
132
|
+
pollingPromise.resolve();
|
|
114
133
|
}
|
|
115
|
-
pollingPromise.resolve();
|
|
116
|
-
}
|
|
117
134
|
|
|
118
|
-
|
|
119
|
-
|
|
135
|
+
this.poll();
|
|
136
|
+
},
|
|
137
|
+
this.minDelayMs
|
|
138
|
+
? Math.max(this.minDelayMs, this.config.fallbackPollIntervalInMs)
|
|
139
|
+
: this.config.fallbackPollIntervalInMs,
|
|
140
|
+
);
|
|
120
141
|
}
|
|
121
142
|
|
|
122
143
|
private logUpdateFailure(e: any) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { fetch, Response, Request } from 'apollo-server-env';
|
|
2
2
|
import { GraphQLError } from 'graphql';
|
|
3
|
+
import retry from 'async-retry';
|
|
3
4
|
import { SupergraphSdlUpdate } from '../../config';
|
|
4
5
|
import { submitOutOfBandReportIfConfigured } from './outOfBandReporter';
|
|
5
6
|
import { SupergraphSdlQuery } from '../../__generated__/graphqlTypes';
|
|
@@ -12,6 +13,7 @@ export const SUPERGRAPH_SDL_QUERY = /* GraphQL */`#graphql
|
|
|
12
13
|
... on RouterConfigResult {
|
|
13
14
|
id
|
|
14
15
|
supergraphSdl: supergraphSDL
|
|
16
|
+
minDelaySeconds
|
|
15
17
|
}
|
|
16
18
|
... on FetchError {
|
|
17
19
|
code
|
|
@@ -39,7 +41,12 @@ const { name, version } = require('../../../package.json');
|
|
|
39
41
|
|
|
40
42
|
const fetchErrorMsg = "An error occurred while fetching your schema from Apollo: ";
|
|
41
43
|
|
|
42
|
-
|
|
44
|
+
export class UplinkFetcherError extends Error {
|
|
45
|
+
constructor(message: string) {
|
|
46
|
+
super(message);
|
|
47
|
+
this.name = 'UplinkFetcherError';
|
|
48
|
+
}
|
|
49
|
+
}
|
|
43
50
|
|
|
44
51
|
export async function loadSupergraphSdlFromUplinks({
|
|
45
52
|
graphRef,
|
|
@@ -49,6 +56,8 @@ export async function loadSupergraphSdlFromUplinks({
|
|
|
49
56
|
fetcher,
|
|
50
57
|
compositionId,
|
|
51
58
|
maxRetries,
|
|
59
|
+
roundRobinSeed,
|
|
60
|
+
earliestFetchTime,
|
|
52
61
|
}: {
|
|
53
62
|
graphRef: string;
|
|
54
63
|
apiKey: string;
|
|
@@ -56,29 +65,32 @@ export async function loadSupergraphSdlFromUplinks({
|
|
|
56
65
|
errorReportingEndpoint: string | undefined,
|
|
57
66
|
fetcher: typeof fetch;
|
|
58
67
|
compositionId: string | null;
|
|
59
|
-
maxRetries: number
|
|
68
|
+
maxRetries: number,
|
|
69
|
+
roundRobinSeed: number,
|
|
70
|
+
earliestFetchTime: Date | null
|
|
60
71
|
}) : Promise<SupergraphSdlUpdate | null> {
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
72
|
+
// This Promise resolves with either an updated supergraph or null if no change.
|
|
73
|
+
// This Promise can reject in the case that none of the retries are successful,
|
|
74
|
+
// in which case it will reject with the most frequently encountered error.
|
|
75
|
+
return retry(
|
|
76
|
+
() =>
|
|
77
|
+
loadSupergraphSdlFromStorage({
|
|
67
78
|
graphRef,
|
|
68
79
|
apiKey,
|
|
69
|
-
endpoint: endpoints[
|
|
80
|
+
endpoint: endpoints[roundRobinSeed++ % endpoints.length],
|
|
70
81
|
errorReportingEndpoint,
|
|
71
82
|
fetcher,
|
|
72
|
-
compositionId
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
83
|
+
compositionId,
|
|
84
|
+
}),
|
|
85
|
+
{
|
|
86
|
+
retries: maxRetries,
|
|
87
|
+
onRetry: async () => {
|
|
88
|
+
const delayMS = earliestFetchTime ? earliestFetchTime.getTime() - Date.now(): 0;
|
|
89
|
+
if (delayMS > 0) await new Promise(resolve => setTimeout(resolve, delayMS));
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
);
|
|
93
|
+
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
export async function loadSupergraphSdlFromStorage({
|
|
@@ -132,7 +144,7 @@ export async function loadSupergraphSdlFromStorage({
|
|
|
132
144
|
fetcher,
|
|
133
145
|
});
|
|
134
146
|
|
|
135
|
-
throw new
|
|
147
|
+
throw new UplinkFetcherError(fetchErrorMsg + (e.message ?? e));
|
|
136
148
|
}
|
|
137
149
|
|
|
138
150
|
const endTime = new Date();
|
|
@@ -143,11 +155,11 @@ export async function loadSupergraphSdlFromStorage({
|
|
|
143
155
|
response = await result.json();
|
|
144
156
|
} catch (e) {
|
|
145
157
|
// Bad response
|
|
146
|
-
throw new
|
|
158
|
+
throw new UplinkFetcherError(fetchErrorMsg + result.status + ' ' + e.message ?? e);
|
|
147
159
|
}
|
|
148
160
|
|
|
149
161
|
if ('errors' in response) {
|
|
150
|
-
throw new
|
|
162
|
+
throw new UplinkFetcherError(
|
|
151
163
|
[fetchErrorMsg, ...response.errors.map((error) => error.message)].join(
|
|
152
164
|
'\n',
|
|
153
165
|
),
|
|
@@ -155,7 +167,7 @@ export async function loadSupergraphSdlFromStorage({
|
|
|
155
167
|
}
|
|
156
168
|
} else {
|
|
157
169
|
await submitOutOfBandReportIfConfigured({
|
|
158
|
-
error: new
|
|
170
|
+
error: new UplinkFetcherError(fetchErrorMsg + result.status + ' ' + result.statusText),
|
|
159
171
|
request,
|
|
160
172
|
endpoint: errorReportingEndpoint,
|
|
161
173
|
response: result,
|
|
@@ -163,7 +175,7 @@ export async function loadSupergraphSdlFromStorage({
|
|
|
163
175
|
endedAt: endTime,
|
|
164
176
|
fetcher,
|
|
165
177
|
});
|
|
166
|
-
throw new
|
|
178
|
+
throw new UplinkFetcherError(fetchErrorMsg + result.status + ' ' + result.statusText);
|
|
167
179
|
}
|
|
168
180
|
|
|
169
181
|
const { routerConfig } = response.data;
|
|
@@ -171,16 +183,17 @@ export async function loadSupergraphSdlFromStorage({
|
|
|
171
183
|
const {
|
|
172
184
|
id,
|
|
173
185
|
supergraphSdl,
|
|
186
|
+
minDelaySeconds,
|
|
174
187
|
// messages,
|
|
175
188
|
} = routerConfig;
|
|
176
|
-
return { id, supergraphSdl: supergraphSdl
|
|
189
|
+
return { id, supergraphSdl: supergraphSdl!, minDelaySeconds };
|
|
177
190
|
} else if (routerConfig.__typename === 'FetchError') {
|
|
178
191
|
// FetchError case
|
|
179
192
|
const { code, message } = routerConfig;
|
|
180
|
-
throw new
|
|
193
|
+
throw new UplinkFetcherError(`${code}: ${message}`);
|
|
181
194
|
} else if (routerConfig.__typename === 'Unchanged') {
|
|
182
195
|
return null;
|
|
183
196
|
} else {
|
|
184
|
-
throw new
|
|
197
|
+
throw new UplinkFetcherError('Programming error: unhandled response failure');
|
|
185
198
|
}
|
|
186
199
|
}
|
|
@@ -2,3 +2,4 @@ export { LocalCompose } from './LocalCompose';
|
|
|
2
2
|
export { LegacyFetcher } from './LegacyFetcher';
|
|
3
3
|
export { IntrospectAndCompose } from './IntrospectAndCompose';
|
|
4
4
|
export { UplinkFetcher } from './UplinkFetcher';
|
|
5
|
+
export { UplinkFetcherError } from './UplinkFetcher/loadSupergraphSdlFromStorage'
|