@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
@@ -118,8 +118,7 @@ export class RemoteGraphQLDataSource<
118
118
  // there.
119
119
  const overallCachePolicy =
120
120
  this.honorSubgraphCacheControlHeader &&
121
- options.kind === GraphQLDataSourceRequestKind.INCOMING_OPERATION &&
122
- options.incomingRequestContext.overallCachePolicy?.restrict
121
+ options.kind === GraphQLDataSourceRequestKind.INCOMING_OPERATION
123
122
  ? options.incomingRequestContext.overallCachePolicy
124
123
  : null;
125
124
 
@@ -39,6 +39,10 @@ export type GraphQLDataSourceProcessOptions<
39
39
  * checking `kind`).
40
40
  */
41
41
  context: GraphQLRequestContext<TContext>['context'];
42
+ /**
43
+ * The document representation of the request's query being sent to the subgraph, if available.
44
+ */
45
+ document?: GraphQLRequestContext<TContext>['document'];
42
46
  }
43
47
  | {
44
48
  kind:
@@ -14,6 +14,8 @@ import {
14
14
  GraphQLSchema,
15
15
  isObjectType,
16
16
  isInterfaceType,
17
+ GraphQLErrorOptions,
18
+ DocumentNode,
17
19
  } from 'graphql';
18
20
  import { Trace, google } from 'apollo-reporting-protobuf';
19
21
  import { GraphQLDataSource, GraphQLDataSourceRequestKind } from './datasources/types';
@@ -31,7 +33,7 @@ import { deepMerge } from './utilities/deepMerge';
31
33
  import { isNotNullOrUndefined } from './utilities/array';
32
34
  import { SpanStatusCode } from "@opentelemetry/api";
33
35
  import { OpenTelemetrySpanNames, tracer } from "./utilities/opentelemetry";
34
- import { defaultRootName } from '@apollo/federation-internals';
36
+ import { defaultRootName, errorCodeDef, ERRORS } from '@apollo/federation-internals';
35
37
 
36
38
  export type ServiceMap = {
37
39
  [serviceName: string]: GraphQLDataSource;
@@ -44,6 +46,7 @@ interface ExecutionContext<TContext> {
44
46
  operationContext: OperationContext;
45
47
  serviceMap: ServiceMap;
46
48
  requestContext: GraphQLRequestContext<TContext>;
49
+ supergraphSchema: GraphQLSchema;
47
50
  errors: GraphQLError[];
48
51
  }
49
52
 
@@ -52,6 +55,7 @@ export async function executeQueryPlan<TContext>(
52
55
  serviceMap: ServiceMap,
53
56
  requestContext: GraphQLRequestContext<TContext>,
54
57
  operationContext: OperationContext,
58
+ supergraphSchema: GraphQLSchema,
55
59
  ): Promise<GraphQLExecutionResult> {
56
60
 
57
61
  const logger = requestContext.logger || console;
@@ -65,6 +69,7 @@ export async function executeQueryPlan<TContext>(
65
69
  operationContext,
66
70
  serviceMap,
67
71
  requestContext,
72
+ supergraphSchema,
68
73
  errors,
69
74
  };
70
75
 
@@ -118,11 +123,7 @@ export async function executeQueryPlan<TContext>(
118
123
  errors: [
119
124
  new GraphQLError(
120
125
  error.message,
121
- undefined,
122
- undefined,
123
- undefined,
124
- undefined,
125
- error as Error,
126
+ { originalError: error },
126
127
  )
127
128
  ]
128
129
  };
@@ -298,7 +299,8 @@ async function executeFetch<TContext>(
298
299
  context,
299
300
  fetch.operation,
300
301
  variables,
301
- fetch.operationName
302
+ fetch.operationName,
303
+ fetch.operationDocumentNode
302
304
  );
303
305
 
304
306
  for (const entity of entities) {
@@ -312,7 +314,10 @@ async function executeFetch<TContext>(
312
314
 
313
315
  entities.forEach((entity, index) => {
314
316
  const representation = executeSelectionSet(
315
- context.operationContext,
317
+ // Note that `requires` may include references to inacessible elements, so we should "execute" it using the supergrah
318
+ // schema, _not_ the API schema (the one in `context.operationContext.schema`). And this is not a security risk since
319
+ // what we're extracting here is what is sent to subgraphs, and subgraphs knows `@inacessible` elements.
320
+ context.supergraphSchema,
316
321
  entity,
317
322
  requires,
318
323
  );
@@ -334,7 +339,8 @@ async function executeFetch<TContext>(
334
339
  context,
335
340
  fetch.operation,
336
341
  {...variables, representations},
337
- fetch.operationName
342
+ fetch.operationName,
343
+ fetch.operationDocumentNode
338
344
  );
339
345
 
340
346
  if (!dataReceivedFromService) {
@@ -376,7 +382,8 @@ async function executeFetch<TContext>(
376
382
  context: ExecutionContext<TContext>,
377
383
  source: string,
378
384
  variables: Record<string, any>,
379
- operationName: string | undefined
385
+ operationName: string | undefined,
386
+ operationDocumentNode?: DocumentNode
380
387
  ): Promise<ResultMap | void | null> {
381
388
  // We declare this as 'any' because it is missing url and method, which
382
389
  // GraphQLRequest.http is supposed to have if it exists.
@@ -414,6 +421,7 @@ async function executeFetch<TContext>(
414
421
  },
415
422
  incomingRequestContext: context.requestContext,
416
423
  context: context.requestContext.context,
424
+ document: operationDocumentNode
417
425
  });
418
426
 
419
427
  if (response.errors) {
@@ -479,7 +487,7 @@ async function executeFetch<TContext>(
479
487
  * @param selectionSet
480
488
  */
481
489
  function executeSelectionSet(
482
- operationContext: OperationContext,
490
+ schema: GraphQLSchema,
483
491
  source: Record<string, any> | null,
484
492
  selections: QueryPlanSelectionNode[],
485
493
  ): Record<string, any> | null {
@@ -514,12 +522,12 @@ function executeSelectionSet(
514
522
  if (Array.isArray(source[responseName])) {
515
523
  result[responseName] = source[responseName].map((value: any) =>
516
524
  selections
517
- ? executeSelectionSet(operationContext, value, selections)
525
+ ? executeSelectionSet(schema, value, selections)
518
526
  : value,
519
527
  );
520
528
  } else if (selections) {
521
529
  result[responseName] = executeSelectionSet(
522
- operationContext,
530
+ schema,
523
531
  source[responseName],
524
532
  selections,
525
533
  );
@@ -533,10 +541,10 @@ function executeSelectionSet(
533
541
  const typename = source && source['__typename'];
534
542
  if (!typename) continue;
535
543
 
536
- if (doesTypeConditionMatch(operationContext.schema, selection.typeCondition, typename)) {
544
+ if (doesTypeConditionMatch(schema, selection.typeCondition, typename)) {
537
545
  deepMerge(
538
546
  result,
539
- executeSelectionSet(operationContext, source, selection.selections),
547
+ executeSelectionSet(schema, source, selection.selections),
540
548
  );
541
549
  }
542
550
  break;
@@ -593,22 +601,25 @@ function downstreamServiceError(
593
601
  if (!message) {
594
602
  message = `Error while fetching subquery from service "${serviceName}"`;
595
603
  }
596
- extensions = {
597
- code: 'DOWNSTREAM_SERVICE_ERROR',
598
- // XXX The presence of a serviceName in extensions is used to
599
- // determine if this error should be captured for metrics reporting.
600
- serviceName,
601
- ...extensions,
604
+
605
+ const errorOptions: GraphQLErrorOptions = {
606
+ originalError: originalError as Error,
607
+ extensions: {
608
+ ...extensions,
609
+ // XXX The presence of a serviceName in extensions is used to
610
+ // determine if this error should be captured for metrics reporting.
611
+ serviceName,
612
+ }
602
613
  };
603
- return new GraphQLError(
604
- message,
605
- undefined,
606
- undefined,
607
- undefined,
608
- undefined,
609
- originalError as Error,
610
- extensions,
611
- );
614
+
615
+ let codeDef = errorCodeDef(originalError);
616
+ // It's possible the orignal has a code, but not one we know about (one generated by the underlying `GraphQLDataSource`,
617
+ // which we don't control). In that case, we want to use that code (and have thus no `ErrorCodeDefinition` usable).
618
+ if (!codeDef && extensions?.code) {
619
+ return new GraphQLError(message, errorOptions);
620
+ }
621
+ // Otherwise, we either use the code we found and know, or default to a general downstream error code.
622
+ return (codeDef ?? ERRORS.DOWNSTREAM_SERVICE_ERROR).err(message, errorOptions);
612
623
  }
613
624
 
614
625
  export const defaultFieldResolverWithAliasSupport: GraphQLFieldResolver<
package/src/index.ts CHANGED
@@ -142,6 +142,7 @@ export class ApolloGateway implements GraphQLService {
142
142
  private warnedStates: WarnedStates = Object.create(null);
143
143
  private queryPlanner?: QueryPlanner;
144
144
  private supergraphSdl?: string;
145
+ private supergraphSchema?: GraphQLSchema;
145
146
  private compositionId?: string;
146
147
  private state: GatewayState;
147
148
  private _supergraphManager?: SupergraphManager;
@@ -207,22 +208,6 @@ export class ApolloGateway implements GraphQLService {
207
208
  }
208
209
 
209
210
  private issueConfigurationWarningsIfApplicable() {
210
- // Warn against a pollInterval of < 10s in managed mode and reset it to 10s
211
- if (
212
- isManagedConfig(this.config) &&
213
- this.pollIntervalInMs &&
214
- this.pollIntervalInMs < 10000
215
- ) {
216
- this.pollIntervalInMs = 10000;
217
- this.logger.warn(
218
- 'Polling Apollo services at a frequency of less than once per 10 ' +
219
- 'seconds (10000) is disallowed. Instead, the minimum allowed ' +
220
- 'pollInterval of 10000 will be used. Please reconfigure your ' +
221
- '`fallbackPollIntervalInMs` accordingly. If this is problematic for ' +
222
- 'your team, please contact support.',
223
- );
224
- }
225
-
226
211
  // Warn against using the pollInterval and a serviceList simultaneously
227
212
  // TODO(trevor:removeServiceList)
228
213
  if (this.pollIntervalInMs && isServiceListConfig(this.config)) {
@@ -522,7 +507,7 @@ export class ApolloGateway implements GraphQLService {
522
507
  // In the case that it throws, the gateway will:
523
508
  // * on initial load, throw the error
524
509
  // * on update, log the error and don't update
525
- const { schema, supergraphSdl } = this.createSchemaFromSupergraphSdl(
510
+ const { supergraphSchema, supergraphSdl } = this.createSchemaFromSupergraphSdl(
526
511
  result.supergraphSdl,
527
512
  );
528
513
 
@@ -531,25 +516,26 @@ export class ApolloGateway implements GraphQLService {
531
516
  const previousCompositionId = this.compositionId;
532
517
 
533
518
  if (previousSchema) {
534
- this.logger.info('Updated Supergraph SDL was found.');
519
+ this.logger.info(`Updated Supergraph SDL was found [Composition ID ${this.compositionId} => ${result.id}]`);
535
520
  }
536
521
 
537
522
  this.compositionId = result.id;
538
523
  this.supergraphSdl = supergraphSdl;
524
+ this.supergraphSchema = supergraphSchema.toGraphQLJSSchema();
539
525
 
540
526
  if (!supergraphSdl) {
541
527
  this.logger.error(
542
528
  "A valid schema couldn't be composed. Falling back to previous schema.",
543
529
  );
544
530
  } else {
545
- this.updateWithSchemaAndNotify(schema, supergraphSdl);
531
+ this.updateWithSchemaAndNotify(supergraphSchema, supergraphSdl);
546
532
 
547
533
  if (this.experimental_didUpdateSupergraph) {
548
534
  this.experimental_didUpdateSupergraph(
549
535
  {
550
536
  compositionId: result.id,
551
537
  supergraphSdl,
552
- schema: schema.toGraphQLJSSchema(),
538
+ schema: this.schema!,
553
539
  },
554
540
  previousCompositionId && previousSupergraphSdl && previousSchema
555
541
  ? {
@@ -654,7 +640,7 @@ export class ApolloGateway implements GraphQLService {
654
640
  this.createServices(serviceList);
655
641
 
656
642
  return {
657
- schema,
643
+ supergraphSchema: schema,
658
644
  supergraphSdl,
659
645
  };
660
646
  }
@@ -859,6 +845,7 @@ export class ApolloGateway implements GraphQLService {
859
845
  serviceMap,
860
846
  requestContext,
861
847
  operationContext,
848
+ this.supergraphSchema!,
862
849
  );
863
850
 
864
851
  const shouldShowQueryPlan =
@@ -1076,5 +1063,4 @@ export {
1076
1063
  FailureToFetchSupergraphSdlAfterInit,
1077
1064
  FailureToFetchSupergraphSdlDuringInit,
1078
1065
  FailureToFetchSupergraphSdlFunctionParams,
1079
- DEFAULT_UPLINK_ENDPOINTS,
1080
1066
  } from './supergraphManagers';
@@ -1,7 +1,7 @@
1
+ import { ERRORS } from '@apollo/federation-internals';
1
2
  import {
2
3
  DocumentNode,
3
4
  FragmentDefinitionNode,
4
- GraphQLError,
5
5
  GraphQLSchema,
6
6
  Kind,
7
7
  OperationDefinitionNode,
@@ -37,7 +37,7 @@ export function buildOperationContext({
37
37
  case Kind.OPERATION_DEFINITION:
38
38
  operationCount++;
39
39
  if (!operationName && operationCount > 1) {
40
- throw new GraphQLError(
40
+ throw ERRORS.INVALID_GRAPHQL.err(
41
41
  'Must provide operation name if query contains ' +
42
42
  'multiple operations.',
43
43
  );
@@ -55,11 +55,9 @@ export function buildOperationContext({
55
55
  }
56
56
  });
57
57
  if (!operation) {
58
- if (operationName) {
59
- throw new GraphQLError(`Unknown operation named "${operationName}".`);
60
- } else {
61
- throw new GraphQLError('Must provide an operation.');
62
- }
58
+ throw ERRORS.INVALID_GRAPHQL.err(
59
+ operationName ? `Unknown operation named "${operationName}".` : 'Must provide an operation.'
60
+ );
63
61
  }
64
62
 
65
63
  return {
@@ -1,5 +1,5 @@
1
1
  import type { Logger } from '@apollo/utils.logger';
2
- import { HeadersInit } from 'node-fetch';
2
+ import type { HeadersInit } from 'node-fetch';
3
3
  import resolvable from '@josephg/resolvable';
4
4
  import {
5
5
  ServiceEndpointDefinition,
@@ -1,6 +1,6 @@
1
1
  import { GraphQLRequest } from 'apollo-server-types';
2
2
  import { parse } from 'graphql';
3
- import { Headers, HeadersInit } from 'node-fetch';
3
+ import { Headers, type HeadersInit } from 'node-fetch';
4
4
  import { ServiceDefinition } from '@apollo/federation-internals';
5
5
  import { GraphQLDataSource, GraphQLDataSourceRequestKind } from '../../datasources/types';
6
6
  import { SERVICE_DEFINITION_QUERY } from '../..';
@@ -2,8 +2,6 @@ import mockedEnv from 'mocked-env';
2
2
 
3
3
  import { UplinkSupergraphManager } from '@apollo/gateway';
4
4
 
5
- import { DEFAULT_UPLINK_ENDPOINTS } from '..';
6
-
7
5
  let cleanUp: (() => void) | undefined;
8
6
 
9
7
  const logger = {
@@ -31,7 +29,7 @@ describe('UplinkSupergraphManager', () => {
31
29
  it('uses default uplink URLs', async () => {
32
30
  const manager = new UplinkSupergraphManager({ apiKey, graphRef, logger });
33
31
 
34
- expect(manager.uplinkEndpoints).toEqual(DEFAULT_UPLINK_ENDPOINTS);
32
+ expect(manager.uplinkEndpoints).toEqual(UplinkSupergraphManager.DEFAULT_UPLINK_ENDPOINTS);
35
33
  });
36
34
 
37
35
  it('can set uplink URLs via config', async () => {
@@ -22,6 +22,15 @@ import {
22
22
  nockBeforeEach,
23
23
  } from '../../../__tests__/nockAssertions';
24
24
 
25
+ const logger = {
26
+ warn: jest.fn(),
27
+ debug: jest.fn(),
28
+ error: jest.fn(),
29
+ info: jest.fn(),
30
+ };
31
+
32
+ const requestTimeoutMs = 100;
33
+
25
34
  describe('loadSupergraphSdlFromStorage', () => {
26
35
  beforeEach(nockBeforeEach);
27
36
  afterEach(nockAfterEach);
@@ -34,7 +43,9 @@ describe('loadSupergraphSdlFromStorage', () => {
34
43
  endpoint: mockCloudConfigUrl1,
35
44
  errorReportingEndpoint: undefined,
36
45
  fetcher,
46
+ requestTimeoutMs,
37
47
  compositionId: null,
48
+ logger,
38
49
  });
39
50
  expect(result).toMatchObject({
40
51
  id: 'originalId-1234',
@@ -66,10 +77,11 @@ describe('loadSupergraphSdlFromStorage', () => {
66
77
  endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
67
78
  errorReportingEndpoint: undefined,
68
79
  fetcher,
80
+ requestTimeoutMs,
69
81
  compositionId: 'originalId-1234',
70
82
  maxRetries: 1,
71
83
  roundRobinSeed: 0,
72
- earliestFetchTime: null,
84
+ logger,
73
85
  });
74
86
 
75
87
  expect(result).toMatchObject({
@@ -92,10 +104,11 @@ describe('loadSupergraphSdlFromStorage', () => {
92
104
  endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
93
105
  errorReportingEndpoint: undefined,
94
106
  fetcher,
107
+ requestTimeoutMs,
95
108
  compositionId: 'originalId-1234',
96
109
  maxRetries: 1,
97
110
  roundRobinSeed: 0,
98
- earliestFetchTime: null,
111
+ logger,
99
112
  }),
100
113
  ).rejects.toThrowError(
101
114
  new UplinkFetcherError(
@@ -115,7 +128,9 @@ describe('loadSupergraphSdlFromStorage', () => {
115
128
  endpoint: mockCloudConfigUrl1,
116
129
  errorReportingEndpoint: mockOutOfBandReporterUrl,
117
130
  fetcher,
131
+ requestTimeoutMs,
118
132
  compositionId: null,
133
+ logger,
119
134
  }),
120
135
  ).rejects.toThrowError(
121
136
  new UplinkFetcherError(
@@ -140,7 +155,9 @@ describe('loadSupergraphSdlFromStorage', () => {
140
155
  endpoint: mockCloudConfigUrl1,
141
156
  errorReportingEndpoint: mockOutOfBandReporterUrl,
142
157
  fetcher,
158
+ requestTimeoutMs,
143
159
  compositionId: null,
160
+ logger,
144
161
  }),
145
162
  ).rejects.toThrowError(
146
163
  new UplinkFetcherError(
@@ -160,7 +177,9 @@ describe('loadSupergraphSdlFromStorage', () => {
160
177
  endpoint: mockCloudConfigUrl1,
161
178
  errorReportingEndpoint: mockOutOfBandReporterUrl,
162
179
  fetcher,
180
+ requestTimeoutMs,
163
181
  compositionId: null,
182
+ logger,
164
183
  }),
165
184
  ).rejects.toThrowError(
166
185
  new UplinkFetcherError(
@@ -181,7 +200,9 @@ describe('loadSupergraphSdlFromStorage', () => {
181
200
  endpoint: mockCloudConfigUrl1,
182
201
  errorReportingEndpoint: mockOutOfBandReporterUrl,
183
202
  fetcher,
203
+ requestTimeoutMs,
184
204
  compositionId: null,
205
+ logger,
185
206
  }),
186
207
  ).rejects.toThrowError(
187
208
  new UplinkFetcherError(
@@ -200,7 +221,9 @@ describe('loadSupergraphSdlFromStorage', () => {
200
221
  endpoint: mockCloudConfigUrl1,
201
222
  errorReportingEndpoint: mockOutOfBandReporterUrl,
202
223
  fetcher,
224
+ requestTimeoutMs,
203
225
  compositionId: null,
226
+ logger,
204
227
  }),
205
228
  ).rejects.toThrowError(
206
229
  new UplinkFetcherError(
@@ -220,7 +243,9 @@ describe('loadSupergraphSdlFromStorage', () => {
220
243
  endpoint: mockCloudConfigUrl1,
221
244
  errorReportingEndpoint: mockOutOfBandReporterUrl,
222
245
  fetcher,
246
+ requestTimeoutMs,
223
247
  compositionId: null,
248
+ logger,
224
249
  }),
225
250
  ).rejects.toThrowError(
226
251
  new UplinkFetcherError(
@@ -240,7 +265,9 @@ describe('loadSupergraphSdlFromStorage', () => {
240
265
  endpoint: mockCloudConfigUrl1,
241
266
  errorReportingEndpoint: mockOutOfBandReporterUrl,
242
267
  fetcher,
268
+ requestTimeoutMs,
243
269
  compositionId: null,
270
+ logger,
244
271
  }),
245
272
  ).rejects.toThrowError(
246
273
  new UplinkFetcherError(
@@ -260,7 +287,9 @@ describe('loadSupergraphSdlFromStorage', () => {
260
287
  endpoint: mockCloudConfigUrl1,
261
288
  errorReportingEndpoint: mockOutOfBandReporterUrl,
262
289
  fetcher,
290
+ requestTimeoutMs,
263
291
  compositionId: null,
292
+ logger,
264
293
  }),
265
294
  ).rejects.toThrowError(
266
295
  new UplinkFetcherError(
@@ -281,7 +310,9 @@ describe('loadSupergraphSdlFromStorage', () => {
281
310
  endpoint: mockCloudConfigUrl1,
282
311
  errorReportingEndpoint: mockOutOfBandReporterUrl,
283
312
  fetcher,
313
+ requestTimeoutMs,
284
314
  compositionId: null,
315
+ logger,
285
316
  }),
286
317
  ).rejects.toThrowError(
287
318
  new UplinkFetcherError(
@@ -301,7 +332,9 @@ describe('loadSupergraphSdlFromStorage', () => {
301
332
  endpoint: mockCloudConfigUrl1,
302
333
  errorReportingEndpoint: mockOutOfBandReporterUrl,
303
334
  fetcher,
335
+ requestTimeoutMs,
304
336
  compositionId: null,
337
+ logger,
305
338
  }),
306
339
  ).rejects.toThrowError(
307
340
  new UplinkFetcherError(
@@ -321,7 +354,9 @@ describe('loadSupergraphSdlFromStorage', () => {
321
354
  endpoint: mockCloudConfigUrl1,
322
355
  errorReportingEndpoint: mockOutOfBandReporterUrl,
323
356
  fetcher,
357
+ requestTimeoutMs,
324
358
  compositionId: null,
359
+ logger,
325
360
  }),
326
361
  ).rejects.toThrowError(
327
362
  new UplinkFetcherError(
@@ -341,7 +376,9 @@ describe('loadSupergraphSdlFromStorage', () => {
341
376
  endpoint: mockCloudConfigUrl1,
342
377
  errorReportingEndpoint: mockOutOfBandReporterUrl,
343
378
  fetcher,
379
+ requestTimeoutMs,
344
380
  compositionId: null,
381
+ logger,
345
382
  }),
346
383
  ).rejects.toThrowError(
347
384
  new UplinkFetcherError(
@@ -359,7 +396,9 @@ describe('loadSupergraphSdlFromStorage', () => {
359
396
  endpoint: mockCloudConfigUrl1,
360
397
  errorReportingEndpoint: mockOutOfBandReporterUrl,
361
398
  fetcher,
399
+ requestTimeoutMs,
362
400
  compositionId: 'id-1234',
401
+ logger,
363
402
  });
364
403
  expect(result).toBeNull();
365
404
  });
@@ -382,20 +421,20 @@ describe('loadSupergraphSdlFromUplinks', () => {
382
421
  calls++;
383
422
  return fetcher(...args);
384
423
  },
424
+ requestTimeoutMs,
385
425
  compositionId: 'id-1234',
386
426
  maxRetries: 5,
387
427
  roundRobinSeed: 0,
388
- earliestFetchTime: null,
428
+ logger,
389
429
  });
390
430
 
391
431
  expect(result).toBeNull();
392
432
  expect(calls).toBe(1);
393
433
  });
394
434
 
395
- it('Waits the correct time before retrying', async () => {
396
- const timeoutSpy = jest.spyOn(global, 'setTimeout');
397
-
435
+ it('Retries on error', async () => {
398
436
  mockSupergraphSdlRequest('originalId-1234', mockCloudConfigUrl1).reply(500);
437
+ const supergraphSdl = getTestingSupergraphSdl();
399
438
  mockSupergraphSdlRequestIfAfter(
400
439
  'originalId-1234',
401
440
  mockCloudConfigUrl2,
@@ -406,29 +445,25 @@ describe('loadSupergraphSdlFromUplinks', () => {
406
445
  routerConfig: {
407
446
  __typename: 'RouterConfigResult',
408
447
  id: 'originalId-1234',
409
- supergraphSdl: getTestingSupergraphSdl(),
448
+ supergraphSdl,
410
449
  },
411
450
  },
412
451
  }),
413
452
  );
414
453
 
415
- await loadSupergraphSdlFromUplinks({
454
+ const result = await loadSupergraphSdlFromUplinks({
416
455
  graphRef,
417
456
  apiKey,
418
457
  endpoints: [mockCloudConfigUrl1, mockCloudConfigUrl2],
419
458
  errorReportingEndpoint: undefined,
420
- fetcher: fetcher,
459
+ fetcher,
460
+ requestTimeoutMs,
421
461
  compositionId: 'originalId-1234',
422
462
  maxRetries: 1,
423
463
  roundRobinSeed: 0,
424
- earliestFetchTime: new Date(Date.now() + 1000),
464
+ logger,
425
465
  });
426
466
 
427
- // test if setTimeout was called with a value in range to deal with time jitter
428
- const setTimeoutCall = timeoutSpy.mock.calls[1][1];
429
- expect(setTimeoutCall).toBeLessThanOrEqual(1000);
430
- expect(setTimeoutCall).toBeGreaterThanOrEqual(900);
431
-
432
- timeoutSpy.mockRestore();
467
+ expect(result?.supergraphSdl).toEqual(supergraphSdl);
433
468
  });
434
469
  });