@apollo/gateway 2.1.0-alpha.0 → 2.1.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (59) hide show
  1. package/dist/config.d.ts +1 -1
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
  4. package/dist/datasources/RemoteGraphQLDataSource.js +2 -3
  5. package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
  6. package/dist/datasources/types.d.ts +1 -0
  7. package/dist/datasources/types.d.ts.map +1 -1
  8. package/dist/executeQueryPlan.d.ts +2 -2
  9. package/dist/executeQueryPlan.d.ts.map +1 -1
  10. package/dist/executeQueryPlan.js +24 -16
  11. package/dist/executeQueryPlan.js.map +1 -1
  12. package/dist/index.d.ts +2 -1
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +8 -18
  15. package/dist/index.js.map +1 -1
  16. package/dist/operationContext.d.ts.map +1 -1
  17. package/dist/operationContext.js +3 -7
  18. package/dist/operationContext.js.map +1 -1
  19. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +1 -1
  20. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -1
  21. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts +1 -1
  22. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -1
  23. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -1
  24. package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts +7 -7
  25. package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts.map +1 -1
  26. package/dist/supergraphManagers/UplinkSupergraphManager/index.js +38 -42
  27. package/dist/supergraphManagers/UplinkSupergraphManager/index.js.map +1 -1
  28. package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.d.ts +9 -8
  29. package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.d.ts.map +1 -1
  30. package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.js +19 -10
  31. package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.js.map +1 -1
  32. package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.d.ts +2 -1
  33. package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.d.ts.map +1 -1
  34. package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.js.map +1 -1
  35. package/dist/supergraphManagers/UplinkSupergraphManager/types.d.ts +9 -0
  36. package/dist/supergraphManagers/UplinkSupergraphManager/types.d.ts.map +1 -0
  37. package/dist/supergraphManagers/UplinkSupergraphManager/types.js +5 -0
  38. package/dist/supergraphManagers/UplinkSupergraphManager/types.js.map +1 -0
  39. package/package.json +7 -7
  40. package/src/__tests__/executeQueryPlan.test.ts +199 -1
  41. package/src/__tests__/execution-utils.ts +5 -3
  42. package/src/__tests__/integration/abstract-types.test.ts +31 -65
  43. package/src/__tests__/integration/configuration.test.ts +2 -2
  44. package/src/__tests__/integration/managed.test.ts +47 -44
  45. package/src/__tests__/integration/networkRequests.test.ts +10 -23
  46. package/src/config.ts +1 -1
  47. package/src/datasources/RemoteGraphQLDataSource.ts +1 -2
  48. package/src/datasources/types.ts +4 -0
  49. package/src/executeQueryPlan.ts +41 -30
  50. package/src/index.ts +8 -22
  51. package/src/operationContext.ts +5 -7
  52. package/src/supergraphManagers/IntrospectAndCompose/index.ts +1 -1
  53. package/src/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts +1 -1
  54. package/src/supergraphManagers/UplinkSupergraphManager/__tests__/UplinkSupergraphManager.test.ts +1 -3
  55. package/src/supergraphManagers/UplinkSupergraphManager/__tests__/loadSupergraphSdlFromStorage.test.ts +51 -16
  56. package/src/supergraphManagers/UplinkSupergraphManager/index.ts +64 -58
  57. package/src/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.ts +31 -19
  58. package/src/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.ts +2 -1
  59. package/src/supergraphManagers/UplinkSupergraphManager/types.ts +10 -0
@@ -8,23 +8,7 @@ import {
8
8
  } from '../..';
9
9
  import { getDefaultLogger } from '../../logger';
10
10
  import { loadSupergraphSdlFromUplinks } from './loadSupergraphSdlFromStorage';
11
- import { Fetcher } from '@apollo/utils.fetcher';
12
-
13
- export const DEFAULT_UPLINK_ENDPOINTS = [
14
- 'https://uplink.api.apollographql.com/',
15
- 'https://aws.uplink.api.apollographql.com/',
16
- ];
17
-
18
- function getUplinkEndpoints(): string[] {
19
- /**
20
- * Configuration priority order:
21
- * 1. APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT environment variable
22
- * 2. default (GCP and AWS)
23
- */
24
- const envEndpoints =
25
- process.env.APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT?.split(',');
26
- return envEndpoints ?? DEFAULT_UPLINK_ENDPOINTS;
27
- }
11
+ import type { AbortableFetcher } from './types';
28
12
 
29
13
  export type FailureToFetchSupergraphSdlFunctionParams = {
30
14
  error: Error;
@@ -47,8 +31,9 @@ export type FailureToFetchSupergraphSdlAfterInit = ({
47
31
  fetchCount,
48
32
  mostRecentSuccessfulFetchAt,
49
33
  }:
50
- | FailureToFetchSupergraphSdlFunctionParams
51
- & { mostRecentSuccessfulFetchAt?: Date }) => Promise<string | null>;
34
+ | FailureToFetchSupergraphSdlFunctionParams & {
35
+ mostRecentSuccessfulFetchAt?: Date;
36
+ }) => Promise<string | null>;
52
37
 
53
38
  type State =
54
39
  | { phase: 'constructed' }
@@ -61,13 +46,24 @@ type State =
61
46
  | { phase: 'stopped' };
62
47
 
63
48
  export class UplinkSupergraphManager implements SupergraphManager {
64
- public readonly uplinkEndpoints: string[] = getUplinkEndpoints();
49
+ public static readonly DEFAULT_REQUEST_TIMEOUT_MS = 30_000;
50
+ public static readonly MIN_POLL_INTERVAL_MS = 10_000;
51
+
52
+ public static readonly DEFAULT_UPLINK_ENDPOINTS = [
53
+ 'https://uplink.api.apollographql.com/',
54
+ 'https://aws.uplink.api.apollographql.com/',
55
+ ];
56
+
57
+ public readonly uplinkEndpoints: string[] =
58
+ UplinkSupergraphManager.getUplinkEndpoints();
65
59
  private apiKey: string;
66
60
  private graphRef: string;
67
- private fetcher: Fetcher = makeFetchHappen.defaults();
61
+ private fetcher: AbortableFetcher = makeFetchHappen.defaults();
68
62
  private maxRetries: number;
63
+ private requestTimeoutMs: number =
64
+ UplinkSupergraphManager.DEFAULT_REQUEST_TIMEOUT_MS;
69
65
  private initialMaxRetries: number;
70
- private fallbackPollIntervalMs: number = 10_000;
66
+ private pollIntervalMs: number = UplinkSupergraphManager.MIN_POLL_INTERVAL_MS;
71
67
  private logger: Logger;
72
68
  private update?: SupergraphSdlUpdateFunction;
73
69
  private shouldRunSubgraphHealthcheck: boolean = false;
@@ -80,8 +76,6 @@ export class UplinkSupergraphManager implements SupergraphManager {
80
76
  process.env.APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT ?? undefined;
81
77
  private compositionId?: string;
82
78
  private fetchCount: number = 0;
83
- private minDelayMs: number | null = null;
84
- private earliestFetchTime: Date | null = null;
85
79
  private mostRecentSuccessfulFetchAt?: Date;
86
80
 
87
81
  constructor({
@@ -89,7 +83,6 @@ export class UplinkSupergraphManager implements SupergraphManager {
89
83
  graphRef,
90
84
  debug,
91
85
  logger,
92
- fetcher,
93
86
  uplinkEndpoints,
94
87
  fallbackPollIntervalInMs,
95
88
  maxRetries,
@@ -102,7 +95,6 @@ export class UplinkSupergraphManager implements SupergraphManager {
102
95
  graphRef: string;
103
96
  debug?: boolean;
104
97
  logger?: Logger;
105
- fetcher?: Fetcher;
106
98
  uplinkEndpoints?: string[];
107
99
  fallbackPollIntervalInMs?: number;
108
100
  maxRetries?: number;
@@ -122,9 +114,14 @@ export class UplinkSupergraphManager implements SupergraphManager {
122
114
  this.maxRetries = maxRetries ?? this.uplinkEndpoints.length * 3 - 1;
123
115
  this.initialMaxRetries = initialMaxRetries ?? this.maxRetries;
124
116
 
125
- this.fetcher = fetcher ?? this.fetcher;
126
- this.fallbackPollIntervalMs =
127
- fallbackPollIntervalInMs ?? this.fallbackPollIntervalMs;
117
+ this.pollIntervalMs = fallbackPollIntervalInMs ?? this.pollIntervalMs;
118
+ if (this.pollIntervalMs < UplinkSupergraphManager.MIN_POLL_INTERVAL_MS) {
119
+ this.logger.warn(
120
+ 'Polling Apollo services at a frequency of less than once per 10 seconds (10000) is disallowed. Instead, the minimum allowed pollInterval of 10000 will be used. Please reconfigure your `fallbackPollIntervalInMs` accordingly. If this is problematic for your team, please contact support.',
121
+ );
122
+ this.pollIntervalMs = UplinkSupergraphManager.MIN_POLL_INTERVAL_MS;
123
+ }
124
+
128
125
  this.shouldRunSubgraphHealthcheck =
129
126
  shouldRunSubgraphHealthcheck ?? this.shouldRunSubgraphHealthcheck;
130
127
  this.onFailureToFetchSupergraphSdlDuringInit =
@@ -142,19 +139,16 @@ export class UplinkSupergraphManager implements SupergraphManager {
142
139
  this.healthCheck = healthCheck;
143
140
  }
144
141
 
145
- let initialSupergraphSdl: string | undefined = undefined;
142
+ let initialSupergraphSdl: string | null = null;
146
143
  try {
147
- const result = await this.updateSupergraphSdl(this.initialMaxRetries);
148
- if (!result) {
144
+ initialSupergraphSdl = await this.updateSupergraphSdl(
145
+ this.initialMaxRetries,
146
+ );
147
+ if (!initialSupergraphSdl) {
149
148
  throw new Error(
150
149
  'Invalid supergraph schema supplied during initialization.',
151
150
  );
152
151
  }
153
- initialSupergraphSdl = result.supergraphSdl;
154
- if (result.minDelaySeconds) {
155
- this.minDelayMs = 1000 * result.minDelaySeconds;
156
- this.earliestFetchTime = new Date(Date.now() + this.minDelayMs);
157
- }
158
152
  } catch (e) {
159
153
  this.logUpdateFailure(e);
160
154
  throw e;
@@ -187,12 +181,21 @@ export class UplinkSupergraphManager implements SupergraphManager {
187
181
  return this.state.nextFetchPromise;
188
182
  }
189
183
 
190
- private async updateSupergraphSdl(maxRetries: number): Promise<{
191
- supergraphSdl: string;
192
- minDelaySeconds: number;
193
- } | null> {
184
+ /**
185
+ * Configuration priority order:
186
+ * 1. APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT environment variable
187
+ * 2. default (GCP and AWS)
188
+ */
189
+ public static getUplinkEndpoints(): string[] {
190
+ const envEndpoints =
191
+ process.env.APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT?.split(',');
192
+ return envEndpoints ?? UplinkSupergraphManager.DEFAULT_UPLINK_ENDPOINTS;
193
+ }
194
+
195
+ private async updateSupergraphSdl(
196
+ maxRetries: number,
197
+ ): Promise<string | null> {
194
198
  let supergraphSdl;
195
- let minDelaySeconds = this.fallbackPollIntervalMs / 1000;
196
199
 
197
200
  try {
198
201
  const result = await loadSupergraphSdlFromUplinks({
@@ -203,10 +206,15 @@ export class UplinkSupergraphManager implements SupergraphManager {
203
206
  fetcher: this.fetcher,
204
207
  compositionId: this.compositionId ?? null,
205
208
  maxRetries,
209
+ requestTimeoutMs: this.requestTimeoutMs,
206
210
  roundRobinSeed: this.fetchCount++,
207
- earliestFetchTime: this.earliestFetchTime,
208
211
  logger: this.logger,
209
212
  });
213
+ this.mostRecentSuccessfulFetchAt = new Date();
214
+
215
+ this.logger.debug(
216
+ `Received Uplink response. Has updated SDL? ${!!result?.supergraphSdl}`,
217
+ );
210
218
 
211
219
  if (!result) {
212
220
  return null;
@@ -214,8 +222,10 @@ export class UplinkSupergraphManager implements SupergraphManager {
214
222
 
215
223
  this.compositionId = result.id;
216
224
 
217
- ({ supergraphSdl, minDelaySeconds } = result);
218
- this.mostRecentSuccessfulFetchAt = new Date();
225
+ supergraphSdl = result.supergraphSdl;
226
+ if (result?.minDelaySeconds) {
227
+ this.pollIntervalMs = result.minDelaySeconds * 1000;
228
+ }
219
229
  } catch (e) {
220
230
  this.logger.debug(
221
231
  `Error fetching supergraphSdl from Uplink during phase '${this.state.phase}'`,
@@ -254,7 +264,7 @@ export class UplinkSupergraphManager implements SupergraphManager {
254
264
 
255
265
  // the healthCheck fn is only assigned if it's enabled in the config
256
266
  await this.healthCheck?.(supergraphSdl);
257
- return { supergraphSdl, minDelaySeconds };
267
+ return supergraphSdl;
258
268
  }
259
269
 
260
270
  private beginPolling() {
@@ -263,28 +273,24 @@ export class UplinkSupergraphManager implements SupergraphManager {
263
273
  }
264
274
 
265
275
  private poll() {
266
- const delay = this.minDelayMs
267
- ? Math.max(this.minDelayMs, this.fallbackPollIntervalMs)
268
- : this.fallbackPollIntervalMs;
269
-
270
276
  if (this.state.phase !== 'polling') {
277
+ this.logger.debug(`Stopped polling Uplink [phase: ${this.state.phase}]`);
271
278
  return;
272
279
  }
273
280
 
274
281
  this.state.nextFetchPromise = resolvable();
275
282
 
283
+ this.logger.debug(
284
+ `Will poll Uplink after ${this.pollIntervalMs}ms [phase: ${this.state.phase}]`,
285
+ );
276
286
  this.timerRef = setTimeout(async () => {
277
287
  if (this.state.phase === 'polling') {
278
288
  const pollingPromise = resolvable();
279
289
  this.state.pollingPromise = pollingPromise;
280
290
  try {
281
- const result = await this.updateSupergraphSdl(this.maxRetries);
282
- if (result?.minDelaySeconds) {
283
- this.minDelayMs = 1000 * result.minDelaySeconds;
284
- this.earliestFetchTime = new Date(Date.now() + this.minDelayMs);
285
- }
286
- if (result?.supergraphSdl) {
287
- this.update?.(result.supergraphSdl);
291
+ const supergraphSdl = await this.updateSupergraphSdl(this.maxRetries);
292
+ if (supergraphSdl) {
293
+ this.update?.(supergraphSdl);
288
294
  }
289
295
  } catch (e) {
290
296
  this.logUpdateFailure(e);
@@ -294,7 +300,7 @@ export class UplinkSupergraphManager implements SupergraphManager {
294
300
  }
295
301
 
296
302
  this.poll();
297
- }, delay);
303
+ }, this.pollIntervalMs);
298
304
  }
299
305
 
300
306
  private logUpdateFailure(e: any) {
@@ -1,14 +1,15 @@
1
1
  import { GraphQLError } from 'graphql';
2
2
  import retry from 'async-retry';
3
+ import { AbortController } from "node-abort-controller";
3
4
  import { SupergraphSdlUpdate } from '../../config';
4
5
  import { submitOutOfBandReportIfConfigured } from './outOfBandReporter';
5
6
  import { SupergraphSdlQuery } from '../../__generated__/graphqlTypes';
6
- import type {
7
- Fetcher,
8
- FetcherResponse,
9
- FetcherRequestInit,
10
- } from '@apollo/utils.fetcher';
7
+ import type { FetcherResponse } from '@apollo/utils.fetcher';
11
8
  import type { Logger } from '@apollo/utils.logger';
9
+ import type {
10
+ AbortableFetcher as Fetcher,
11
+ AbortableFetcherRequestInit as FetcherRequestInit,
12
+ } from './types';
12
13
 
13
14
  // Magic /* GraphQL */ comment below is for codegen, do not remove
14
15
  export const SUPERGRAPH_SDL_QUERY = /* GraphQL */`#graphql
@@ -61,8 +62,8 @@ export async function loadSupergraphSdlFromUplinks({
61
62
  fetcher,
62
63
  compositionId,
63
64
  maxRetries,
65
+ requestTimeoutMs,
64
66
  roundRobinSeed,
65
- earliestFetchTime,
66
67
  logger,
67
68
  }: {
68
69
  graphRef: string;
@@ -72,10 +73,10 @@ export async function loadSupergraphSdlFromUplinks({
72
73
  fetcher: Fetcher;
73
74
  compositionId: string | null;
74
75
  maxRetries: number,
76
+ requestTimeoutMs: number,
75
77
  roundRobinSeed: number,
76
- earliestFetchTime: Date | null,
77
- logger?: Logger | undefined,
78
- }) : Promise<Required<SupergraphSdlUpdate> | null> {
78
+ logger: Logger,
79
+ }) : Promise<SupergraphSdlUpdate | null> {
79
80
  // This Promise resolves with either an updated supergraph or null if no change.
80
81
  // This Promise can reject in the case that none of the retries are successful,
81
82
  // in which case it will reject with the most frequently encountered error.
@@ -87,19 +88,18 @@ export async function loadSupergraphSdlFromUplinks({
87
88
  endpoint: endpoints[roundRobinSeed++ % endpoints.length],
88
89
  errorReportingEndpoint,
89
90
  fetcher,
91
+ requestTimeoutMs,
90
92
  compositionId,
91
93
  logger,
92
94
  }),
93
95
  {
94
96
  retries: maxRetries,
95
- onRetry: async () => {
96
- const delayMS = earliestFetchTime ? earliestFetchTime.getTime() - Date.now(): 0;
97
- logger?.debug(`Waiting ${delayMS}ms before retrying (earliest fetch time ${earliestFetchTime})...`);
98
- if (delayMS > 0) await new Promise(resolve => setTimeout(resolve, delayMS));
99
- }
97
+ maxTimeout: 60_000,
98
+ onRetry(e, attempt) {
99
+ logger.debug(`Unable to fetch supergraph SDL (attempt ${attempt}), waiting before retry: ${e}`);
100
+ },
100
101
  },
101
102
  );
102
-
103
103
  }
104
104
 
105
105
  export async function loadSupergraphSdlFromStorage({
@@ -108,6 +108,7 @@ export async function loadSupergraphSdlFromStorage({
108
108
  endpoint,
109
109
  errorReportingEndpoint,
110
110
  fetcher,
111
+ requestTimeoutMs,
111
112
  compositionId,
112
113
  logger,
113
114
  }: {
@@ -116,9 +117,10 @@ export async function loadSupergraphSdlFromStorage({
116
117
  endpoint: string;
117
118
  errorReportingEndpoint?: string;
118
119
  fetcher: Fetcher;
120
+ requestTimeoutMs: number;
119
121
  compositionId: string | null;
120
- logger?: Logger | undefined;
121
- }) : Promise<Required<SupergraphSdlUpdate> | null> {
122
+ logger: Logger;
123
+ }) : Promise<SupergraphSdlUpdate | null> {
122
124
  const requestBody = JSON.stringify({
123
125
  query: SUPERGRAPH_SDL_QUERY,
124
126
  variables: {
@@ -128,6 +130,12 @@ export async function loadSupergraphSdlFromStorage({
128
130
  },
129
131
  })
130
132
 
133
+ const controller = new AbortController();
134
+ const signal = setTimeout(() => {
135
+ logger.debug(`Aborting request due to timeout`);
136
+ controller.abort();
137
+ }, requestTimeoutMs);
138
+
131
139
  const requestDetails: FetcherRequestInit = {
132
140
  method: 'POST',
133
141
  body: requestBody,
@@ -137,12 +145,14 @@ export async function loadSupergraphSdlFromStorage({
137
145
  'user-agent': `${name}/${version}`,
138
146
  'content-type': 'application/json',
139
147
  },
148
+ signal: controller.signal,
140
149
  };
141
150
 
151
+ logger.debug(`🔧 Fetching ${graphRef} supergraph schema from ${endpoint} ifAfterId ${compositionId}`);
152
+
142
153
  const startTime = new Date();
143
154
  let result: FetcherResponse;
144
155
  try {
145
- logger?.debug(`🔧 Fetching supergraph schema from ${endpoint}`);
146
156
  result = await fetcher(endpoint, requestDetails);
147
157
  } catch (e) {
148
158
  const endTime = new Date();
@@ -158,6 +168,8 @@ export async function loadSupergraphSdlFromStorage({
158
168
  });
159
169
 
160
170
  throw new UplinkFetcherError(fetchErrorMsg + (e.message ?? e));
171
+ } finally {
172
+ clearTimeout(signal);
161
173
  }
162
174
 
163
175
  const endTime = new Date();
@@ -200,7 +212,7 @@ export async function loadSupergraphSdlFromStorage({
200
212
  minDelaySeconds,
201
213
  // messages,
202
214
  } = routerConfig;
203
- return { id, supergraphSdl: supergraphSdl!, minDelaySeconds };
215
+ return { id, supergraphSdl, minDelaySeconds };
204
216
  } else if (routerConfig.__typename === 'FetchError') {
205
217
  // FetchError case
206
218
  const { code, message } = routerConfig;
@@ -1,10 +1,11 @@
1
- import { Fetcher, FetcherResponse } from '@apollo/utils.fetcher';
1
+ import { FetcherResponse } from '@apollo/utils.fetcher';
2
2
  import { GraphQLError } from 'graphql';
3
3
  import {
4
4
  ErrorCode,
5
5
  OobReportMutation,
6
6
  OobReportMutationVariables,
7
7
  } from '../../__generated__/graphqlTypes';
8
+ import { type AbortableFetcher as Fetcher } from './types';
8
9
 
9
10
  // Magic /* GraphQL */ comment below is for codegen, do not remove
10
11
  export const OUT_OF_BAND_REPORTER_QUERY = /* GraphQL */`#graphql
@@ -0,0 +1,10 @@
1
+ import type { AbortSignal } from 'node-abort-controller';
2
+ import type { Fetcher, FetcherRequestInit } from '@apollo/utils.fetcher';
3
+
4
+ export interface AbortableFetcherRequestInit extends FetcherRequestInit {
5
+ signal?: AbortSignal | null | undefined;
6
+ };
7
+
8
+ export interface AbortableFetcher extends Fetcher {
9
+ init?: AbortableFetcherRequestInit;
10
+ };