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

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 (75) hide show
  1. package/dist/config.d.ts +5 -4
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js.map +1 -1
  4. package/dist/datasources/LocalGraphQLDataSource.d.ts +2 -2
  5. package/dist/datasources/LocalGraphQLDataSource.d.ts.map +1 -1
  6. package/dist/datasources/LocalGraphQLDataSource.js +0 -2
  7. package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
  8. package/dist/datasources/RemoteGraphQLDataSource.d.ts +6 -6
  9. package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
  10. package/dist/datasources/RemoteGraphQLDataSource.js +15 -20
  11. package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
  12. package/dist/datasources/types.d.ts +6 -5
  13. package/dist/datasources/types.d.ts.map +1 -1
  14. package/dist/executeQueryPlan.d.ts +3 -3
  15. package/dist/executeQueryPlan.d.ts.map +1 -1
  16. package/dist/executeQueryPlan.js +30 -16
  17. package/dist/executeQueryPlan.js.map +1 -1
  18. package/dist/index.d.ts +9 -9
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +23 -30
  21. package/dist/index.js.map +1 -1
  22. package/dist/logger.d.ts.map +1 -1
  23. package/dist/logger.js +1 -1
  24. package/dist/logger.js.map +1 -1
  25. package/dist/operationContext.d.ts.map +1 -1
  26. package/dist/operationContext.js +3 -7
  27. package/dist/operationContext.js.map +1 -1
  28. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +1 -1
  29. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -1
  30. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts +1 -1
  31. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -1
  32. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -1
  33. package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts +9 -7
  34. package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts.map +1 -1
  35. package/dist/supergraphManagers/UplinkSupergraphManager/index.js +38 -41
  36. package/dist/supergraphManagers/UplinkSupergraphManager/index.js.map +1 -1
  37. package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.d.ts +9 -8
  38. package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.d.ts.map +1 -1
  39. package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.js +19 -10
  40. package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.js.map +1 -1
  41. package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.d.ts +2 -1
  42. package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.d.ts.map +1 -1
  43. package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.js.map +1 -1
  44. package/dist/supergraphManagers/UplinkSupergraphManager/types.d.ts +9 -0
  45. package/dist/supergraphManagers/UplinkSupergraphManager/types.d.ts.map +1 -0
  46. package/dist/supergraphManagers/UplinkSupergraphManager/types.js +5 -0
  47. package/dist/supergraphManagers/UplinkSupergraphManager/types.js.map +1 -0
  48. package/package.json +9 -11
  49. package/src/__tests__/buildQueryPlan.test.ts +6 -2
  50. package/src/__tests__/executeQueryPlan.test.ts +208 -8
  51. package/src/__tests__/execution-utils.ts +8 -6
  52. package/src/__tests__/gateway/executor.test.ts +2 -2
  53. package/src/__tests__/gateway/lifecycle-hooks.test.ts +1 -1
  54. package/src/__tests__/integration/abstract-types.test.ts +39 -71
  55. package/src/__tests__/integration/configuration.test.ts +2 -2
  56. package/src/__tests__/integration/managed.test.ts +72 -44
  57. package/src/__tests__/integration/networkRequests.test.ts +10 -23
  58. package/src/config.ts +6 -6
  59. package/src/datasources/LocalGraphQLDataSource.ts +2 -4
  60. package/src/datasources/RemoteGraphQLDataSource.ts +28 -44
  61. package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +2 -2
  62. package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +14 -19
  63. package/src/datasources/types.ts +9 -5
  64. package/src/executeQueryPlan.ts +59 -45
  65. package/src/index.ts +31 -65
  66. package/src/logger.ts +1 -1
  67. package/src/operationContext.ts +5 -7
  68. package/src/supergraphManagers/IntrospectAndCompose/index.ts +1 -1
  69. package/src/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts +3 -3
  70. package/src/supergraphManagers/UplinkSupergraphManager/__tests__/UplinkSupergraphManager.test.ts +1 -3
  71. package/src/supergraphManagers/UplinkSupergraphManager/__tests__/loadSupergraphSdlFromStorage.test.ts +51 -16
  72. package/src/supergraphManagers/UplinkSupergraphManager/index.ts +67 -57
  73. package/src/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.ts +31 -19
  74. package/src/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.ts +2 -1
  75. package/src/supergraphManagers/UplinkSupergraphManager/types.ts +10 -0
package/src/index.ts CHANGED
@@ -1,12 +1,7 @@
1
1
  import { deprecate } from 'util';
2
- import { GraphQLService, Unsubscriber } from 'apollo-server-core';
3
- import {
4
- GraphQLExecutionResult,
5
- GraphQLRequestContextExecutionDidStart,
6
- } from 'apollo-server-types';
7
2
  import { createHash } from '@apollo/utils.createhash';
8
3
  import type { Logger } from '@apollo/utils.logger';
9
- import { InMemoryLRUCache } from 'apollo-server-caching';
4
+ import LRUCache from 'lru-cache';
10
5
  import {
11
6
  isObjectType,
12
7
  isIntrospectionType,
@@ -63,6 +58,7 @@ import {
63
58
  ServiceDefinition,
64
59
  } from '@apollo/federation-internals';
65
60
  import { getDefaultLogger } from './logger';
61
+ import {GatewayInterface, GatewayUnsubscriber, GatewayGraphQLRequestContext, GatewayExecutionResult} from '@apollo/server-gateway-interface';
66
62
 
67
63
  type DataSourceMap = {
68
64
  [serviceName: string]: { url?: string; dataSource: GraphQLDataSource };
@@ -118,7 +114,7 @@ interface GraphQLServiceEngineConfig {
118
114
  graphVariant?: string;
119
115
  }
120
116
 
121
- export class ApolloGateway implements GraphQLService {
117
+ export class ApolloGateway implements GatewayInterface {
122
118
  public schema?: GraphQLSchema;
123
119
  // Same as a `schema` but as a `Schema` to avoid reconverting when we need it.
124
120
  // TODO(sylvain): if we add caching in `Schema.toGraphQLJSSchema`, we could maybe only keep `apiSchema`
@@ -130,7 +126,7 @@ export class ApolloGateway implements GraphQLService {
130
126
  private serviceMap: DataSourceMap = Object.create(null);
131
127
  private config: GatewayConfig;
132
128
  private logger: Logger;
133
- private queryPlanStore: InMemoryLRUCache<QueryPlan>;
129
+ private queryPlanStore: LRUCache<string, QueryPlan>;
134
130
  private apolloConfig?: ApolloConfigFromAS3;
135
131
  private onSchemaChangeListeners = new Set<(schema: GraphQLSchema) => void>();
136
132
  private onSchemaLoadOrUpdateListeners = new Set<
@@ -142,6 +138,7 @@ export class ApolloGateway implements GraphQLService {
142
138
  private warnedStates: WarnedStates = Object.create(null);
143
139
  private queryPlanner?: QueryPlanner;
144
140
  private supergraphSdl?: string;
141
+ private supergraphSchema?: GraphQLSchema;
145
142
  private compositionId?: string;
146
143
  private state: GatewayState;
147
144
  private _supergraphManager?: SupergraphManager;
@@ -195,34 +192,18 @@ export class ApolloGateway implements GraphQLService {
195
192
  }
196
193
 
197
194
  private initQueryPlanStore(approximateQueryPlanStoreMiB?: number) {
198
- return new InMemoryLRUCache<QueryPlan>({
195
+ return new LRUCache<string, QueryPlan>({
199
196
  // Create ~about~ a 30MiB InMemoryLRUCache. This is less than precise
200
197
  // since the technique to calculate the size of a DocumentNode is
201
198
  // only using JSON.stringify on the DocumentNode (and thus doesn't account
202
199
  // for unicode characters, etc.), but it should do a reasonable job at
203
200
  // providing a caching document store for most operations.
204
201
  maxSize: Math.pow(2, 20) * (approximateQueryPlanStoreMiB || 30),
205
- sizeCalculator: approximateObjectSize,
202
+ sizeCalculation: approximateObjectSize,
206
203
  });
207
204
  }
208
205
 
209
206
  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
207
  // Warn against using the pollInterval and a serviceList simultaneously
227
208
  // TODO(trevor:removeServiceList)
228
209
  if (this.pollIntervalInMs && isServiceListConfig(this.config)) {
@@ -375,6 +356,7 @@ export class ApolloGateway implements GraphQLService {
375
356
  uplinkEndpoints:
376
357
  this.config.uplinkEndpoints ?? schemaDeliveryEndpoints,
377
358
  maxRetries: this.config.uplinkMaxRetries,
359
+ fetcher: this.config.fetcher,
378
360
  logger: this.logger,
379
361
  fallbackPollIntervalInMs: this.pollIntervalInMs,
380
362
  }),
@@ -522,7 +504,7 @@ export class ApolloGateway implements GraphQLService {
522
504
  // In the case that it throws, the gateway will:
523
505
  // * on initial load, throw the error
524
506
  // * on update, log the error and don't update
525
- const { schema, supergraphSdl } = this.createSchemaFromSupergraphSdl(
507
+ const { supergraphSchema, supergraphSdl } = this.createSchemaFromSupergraphSdl(
526
508
  result.supergraphSdl,
527
509
  );
528
510
 
@@ -531,25 +513,26 @@ export class ApolloGateway implements GraphQLService {
531
513
  const previousCompositionId = this.compositionId;
532
514
 
533
515
  if (previousSchema) {
534
- this.logger.info('Updated Supergraph SDL was found.');
516
+ this.logger.info(`Updated Supergraph SDL was found [Composition ID ${this.compositionId} => ${result.id}]`);
535
517
  }
536
518
 
537
519
  this.compositionId = result.id;
538
520
  this.supergraphSdl = supergraphSdl;
521
+ this.supergraphSchema = supergraphSchema.toGraphQLJSSchema();
539
522
 
540
523
  if (!supergraphSdl) {
541
524
  this.logger.error(
542
525
  "A valid schema couldn't be composed. Falling back to previous schema.",
543
526
  );
544
527
  } else {
545
- this.updateWithSchemaAndNotify(schema, supergraphSdl);
528
+ this.updateWithSchemaAndNotify(supergraphSchema, supergraphSdl);
546
529
 
547
530
  if (this.experimental_didUpdateSupergraph) {
548
531
  this.experimental_didUpdateSupergraph(
549
532
  {
550
533
  compositionId: result.id,
551
534
  supergraphSdl,
552
- schema: schema.toGraphQLJSSchema(),
535
+ schema: this.schema!,
553
536
  },
554
537
  previousCompositionId && previousSupergraphSdl && previousSchema
555
538
  ? {
@@ -572,12 +555,12 @@ export class ApolloGateway implements GraphQLService {
572
555
  // Once we remove the deprecated onSchemaChange() method, we can remove this.
573
556
  legacyDontNotifyOnSchemaChangeListeners: boolean = false,
574
557
  ): void {
575
- if (this.queryPlanStore) this.queryPlanStore.flush();
558
+ this.queryPlanStore.clear();
576
559
  this.apiSchema = coreSchema.toAPISchema();
577
560
  this.schema = addExtensions(
578
561
  wrapSchemaWithAliasResolver(this.apiSchema.toGraphQLJSSchema()),
579
562
  );
580
- this.queryPlanner = new QueryPlanner(coreSchema);
563
+ this.queryPlanner = new QueryPlanner(coreSchema, this.config.queryPlannerConfig);
581
564
 
582
565
  // Notify onSchemaChange listeners of the updated schema
583
566
  if (!legacyDontNotifyOnSchemaChangeListeners) {
@@ -654,7 +637,7 @@ export class ApolloGateway implements GraphQLService {
654
637
  this.createServices(serviceList);
655
638
 
656
639
  return {
657
- schema,
640
+ supergraphSchema: schema,
658
641
  supergraphSdl,
659
642
  };
660
643
  }
@@ -664,7 +647,7 @@ export class ApolloGateway implements GraphQLService {
664
647
  */
665
648
  public onSchemaChange(
666
649
  callback: (schema: GraphQLSchema) => void,
667
- ): Unsubscriber {
650
+ ): GatewayUnsubscriber {
668
651
  this.onSchemaChangeListeners.add(callback);
669
652
 
670
653
  return () => {
@@ -677,7 +660,7 @@ export class ApolloGateway implements GraphQLService {
677
660
  apiSchema: GraphQLSchema;
678
661
  coreSupergraphSdl: string;
679
662
  }) => void,
680
- ): Unsubscriber {
663
+ ): GatewayUnsubscriber {
681
664
  this.onSchemaLoadOrUpdateListeners.add(callback);
682
665
 
683
666
  return () => {
@@ -757,9 +740,9 @@ export class ApolloGateway implements GraphQLService {
757
740
  // ApolloServerPluginUsageReporting) assumes that. In fact, errors talking to backends
758
741
  // are unlikely to show up as GraphQLErrors. Do we need to use
759
742
  // formatApolloErrors or something?
760
- public executor = async <TContext>(
761
- requestContext: GraphQLRequestContextExecutionDidStart<TContext>,
762
- ): Promise<GraphQLExecutionResult> => {
743
+ public executor = async (
744
+ requestContext: GatewayGraphQLRequestContext,
745
+ ): Promise<GatewayExecutionResult> => {
763
746
  const spanAttributes = requestContext.operationName
764
747
  ? { operationName: requestContext.operationName }
765
748
  : {};
@@ -788,10 +771,7 @@ export class ApolloGateway implements GraphQLService {
788
771
  span.setStatus({ code: SpanStatusCode.ERROR });
789
772
  return { errors: validationErrors };
790
773
  }
791
- let queryPlan: QueryPlan | undefined;
792
- if (this.queryPlanStore) {
793
- queryPlan = await this.queryPlanStore.get(queryPlanStoreKey);
794
- }
774
+ let queryPlan = this.queryPlanStore.get(queryPlanStoreKey);
795
775
 
796
776
  if (!queryPlan) {
797
777
  queryPlan = tracer.startActiveSpan(
@@ -814,25 +794,11 @@ export class ApolloGateway implements GraphQLService {
814
794
  },
815
795
  );
816
796
 
817
- if (this.queryPlanStore) {
818
- // The underlying cache store behind the `documentStore` returns a
819
- // `Promise` which is resolved (or rejected), eventually, based on the
820
- // success or failure (respectively) of the cache save attempt. While
821
- // it's certainly possible to `await` this `Promise`, we don't care about
822
- // whether or not it's successful at this point. We'll instead proceed
823
- // to serve the rest of the request and just hope that this works out.
824
- // If it doesn't work, the next request will have another opportunity to
825
- // try again. Errors will surface as warnings, as appropriate.
826
- //
827
- // While it shouldn't normally be necessary to wrap this `Promise` in a
828
- // `Promise.resolve` invocation, it seems that the underlying cache store
829
- // is returning a non-native `Promise` (e.g. Bluebird, etc.).
830
- Promise.resolve(
831
- this.queryPlanStore.set(queryPlanStoreKey, queryPlan),
832
- ).catch((err) =>
833
- this.logger.warn(
834
- 'Could not store queryPlan' + ((err && err.message) || err),
835
- ),
797
+ try {
798
+ this.queryPlanStore.set(queryPlanStoreKey, queryPlan);
799
+ } catch (err) {
800
+ this.logger.warn(
801
+ 'Could not store queryPlan' + ((err && err.message) || err),
836
802
  );
837
803
  }
838
804
  }
@@ -854,11 +820,12 @@ export class ApolloGateway implements GraphQLService {
854
820
  });
855
821
  }
856
822
 
857
- const response = await executeQueryPlan<TContext>(
823
+ const response = await executeQueryPlan(
858
824
  queryPlan,
859
825
  serviceMap,
860
826
  requestContext,
861
827
  operationContext,
828
+ this.supergraphSchema!,
862
829
  );
863
830
 
864
831
  const shouldShowQueryPlan =
@@ -911,8 +878,8 @@ export class ApolloGateway implements GraphQLService {
911
878
  );
912
879
  };
913
880
 
914
- private validateIncomingRequest<TContext>(
915
- requestContext: GraphQLRequestContextExecutionDidStart<TContext>,
881
+ private validateIncomingRequest(
882
+ requestContext: GatewayGraphQLRequestContext,
916
883
  operationContext: OperationContext,
917
884
  ) {
918
885
  return tracer.startActiveSpan(OpenTelemetrySpanNames.VALIDATE, (span) => {
@@ -1076,5 +1043,4 @@ export {
1076
1043
  FailureToFetchSupergraphSdlAfterInit,
1077
1044
  FailureToFetchSupergraphSdlDuringInit,
1078
1045
  FailureToFetchSupergraphSdlFunctionParams,
1079
- DEFAULT_UPLINK_ENDPOINTS,
1080
1046
  } from './supergraphManagers';
package/src/logger.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import loglevel from 'loglevel';
2
2
  import type { Logger } from '@apollo/utils.logger';
3
3
 
4
- export function getDefaultLogger(debug: boolean = true): Logger {
4
+ export function getDefaultLogger(debug: boolean = false): Logger {
5
5
  const logger = loglevel.getLogger('apollo-gateway');
6
6
 
7
7
  const level = debug === true ? loglevel.levels.DEBUG : loglevel.levels.WARN;
@@ -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,10 +1,10 @@
1
- import { GraphQLRequest } from 'apollo-server-types';
2
1
  import { parse } from 'graphql';
3
- import { Headers, HeadersInit } from 'node-fetch';
2
+ import { Headers, type HeadersInit } from 'node-fetch';
4
3
  import { ServiceDefinition } from '@apollo/federation-internals';
5
4
  import { GraphQLDataSource, GraphQLDataSourceRequestKind } from '../../datasources/types';
6
5
  import { SERVICE_DEFINITION_QUERY } from '../..';
7
6
  import { ServiceDefinitionUpdate, ServiceEndpointDefinition } from '../../config';
7
+ import { GatewayGraphQLRequest } from '@apollo/server-gateway-interface';
8
8
 
9
9
  export type Service = ServiceEndpointDefinition & {
10
10
  dataSource: GraphQLDataSource;
@@ -35,7 +35,7 @@ export async function loadServicesFromRemoteEndpoint({
35
35
  `Tried to load schema for '${name}' but no 'url' was specified.`);
36
36
  }
37
37
 
38
- const request: GraphQLRequest = {
38
+ const request: GatewayGraphQLRequest = {
39
39
  query: SERVICE_DEFINITION_QUERY,
40
40
  http: {
41
41
  url,
@@ -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
  });