@apollo/gateway 2.0.0-alpha.0 → 2.0.0-alpha.4

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 (154) hide show
  1. package/README.md +1 -1
  2. package/dist/__generated__/graphqlTypes.d.ts +13 -11
  3. package/dist/__generated__/graphqlTypes.d.ts.map +1 -1
  4. package/dist/__generated__/graphqlTypes.js.map +1 -1
  5. package/dist/config.d.ts +45 -24
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +30 -31
  8. package/dist/config.js.map +1 -1
  9. package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
  10. package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
  11. package/dist/datasources/RemoteGraphQLDataSource.js +4 -1
  12. package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
  13. package/dist/datasources/types.d.ts +1 -1
  14. package/dist/datasources/types.d.ts.map +1 -1
  15. package/dist/executeQueryPlan.d.ts.map +1 -1
  16. package/dist/executeQueryPlan.js +6 -6
  17. package/dist/executeQueryPlan.js.map +1 -1
  18. package/dist/index.d.ts +36 -23
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +197 -297
  21. package/dist/index.js.map +1 -1
  22. package/dist/operationContext.js +0 -1
  23. package/dist/operationContext.js.map +1 -1
  24. package/dist/schema-helper/addResolversToSchema.d.ts +4 -0
  25. package/dist/schema-helper/addResolversToSchema.d.ts.map +1 -0
  26. package/dist/schema-helper/addResolversToSchema.js +62 -0
  27. package/dist/schema-helper/addResolversToSchema.js.map +1 -0
  28. package/dist/schema-helper/error.d.ts +6 -0
  29. package/dist/schema-helper/error.d.ts.map +1 -0
  30. package/dist/schema-helper/error.js +14 -0
  31. package/dist/schema-helper/error.js.map +1 -0
  32. package/dist/schema-helper/index.d.ts +4 -0
  33. package/dist/schema-helper/index.d.ts.map +1 -0
  34. package/dist/schema-helper/index.js +16 -0
  35. package/dist/schema-helper/index.js.map +1 -0
  36. package/dist/schema-helper/resolverMap.d.ts +16 -0
  37. package/dist/schema-helper/resolverMap.d.ts.map +1 -0
  38. package/dist/schema-helper/resolverMap.js +3 -0
  39. package/dist/schema-helper/resolverMap.js.map +1 -0
  40. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +31 -0
  41. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -0
  42. package/dist/supergraphManagers/IntrospectAndCompose/index.js +112 -0
  43. package/dist/supergraphManagers/IntrospectAndCompose/index.js.map +1 -0
  44. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts +12 -0
  45. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -0
  46. package/dist/{loadServicesFromRemoteEndpoint.js → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js} +6 -6
  47. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -0
  48. package/dist/supergraphManagers/LegacyFetcher/index.d.ts +33 -0
  49. package/dist/supergraphManagers/LegacyFetcher/index.d.ts.map +1 -0
  50. package/dist/supergraphManagers/LegacyFetcher/index.js +149 -0
  51. package/dist/supergraphManagers/LegacyFetcher/index.js.map +1 -0
  52. package/dist/supergraphManagers/LocalCompose/index.d.ts +19 -0
  53. package/dist/supergraphManagers/LocalCompose/index.d.ts.map +1 -0
  54. package/dist/supergraphManagers/LocalCompose/index.js +55 -0
  55. package/dist/supergraphManagers/LocalCompose/index.js.map +1 -0
  56. package/dist/supergraphManagers/UplinkFetcher/index.d.ts +32 -0
  57. package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -0
  58. package/dist/supergraphManagers/UplinkFetcher/index.js +96 -0
  59. package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -0
  60. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts +21 -0
  61. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -0
  62. package/dist/{loadSupergraphSdlFromStorage.js → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js} +41 -10
  63. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -0
  64. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts +13 -0
  65. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +1 -0
  66. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js +85 -0
  67. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +1 -0
  68. package/dist/supergraphManagers/index.d.ts +5 -0
  69. package/dist/supergraphManagers/index.d.ts.map +1 -0
  70. package/dist/supergraphManagers/index.js +12 -0
  71. package/dist/supergraphManagers/index.js.map +1 -0
  72. package/dist/utilities/array.js +1 -1
  73. package/dist/utilities/array.js.map +1 -1
  74. package/dist/utilities/createHash.d.ts +2 -0
  75. package/dist/utilities/createHash.d.ts.map +1 -0
  76. package/dist/utilities/createHash.js +15 -0
  77. package/dist/utilities/createHash.js.map +1 -0
  78. package/dist/utilities/isNodeLike.d.ts +3 -0
  79. package/dist/utilities/isNodeLike.d.ts.map +1 -0
  80. package/dist/utilities/isNodeLike.js +8 -0
  81. package/dist/utilities/isNodeLike.js.map +1 -0
  82. package/package.json +9 -9
  83. package/src/__generated__/graphqlTypes.ts +13 -11
  84. package/src/__mocks__/make-fetch-happen-fetcher.ts +3 -1
  85. package/src/__tests__/buildQueryPlan.test.ts +1 -1
  86. package/src/__tests__/executeQueryPlan.test.ts +1171 -77
  87. package/src/__tests__/execution-utils.ts +5 -7
  88. package/src/__tests__/gateway/buildService.test.ts +3 -3
  89. package/src/__tests__/gateway/endToEnd.test.ts +1 -1
  90. package/src/__tests__/gateway/executor.test.ts +3 -1
  91. package/src/__tests__/gateway/lifecycle-hooks.test.ts +59 -121
  92. package/src/__tests__/gateway/opentelemetry.test.ts +8 -3
  93. package/src/__tests__/gateway/queryPlanCache.test.ts +25 -9
  94. package/src/__tests__/gateway/reporting.test.ts +42 -13
  95. package/src/__tests__/gateway/supergraphSdl.test.ts +397 -0
  96. package/src/__tests__/integration/aliases.test.ts +9 -3
  97. package/src/__tests__/integration/configuration.test.ts +140 -21
  98. package/src/__tests__/integration/logger.test.ts +2 -2
  99. package/src/__tests__/integration/networkRequests.test.ts +126 -149
  100. package/src/__tests__/integration/nockMocks.ts +57 -16
  101. package/src/__tests__/nockAssertions.ts +20 -0
  102. package/src/config.ts +153 -77
  103. package/src/core/__tests__/core.test.ts +6 -6
  104. package/src/datasources/LocalGraphQLDataSource.ts +1 -1
  105. package/src/datasources/RemoteGraphQLDataSource.ts +8 -2
  106. package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
  107. package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +4 -4
  108. package/src/datasources/types.ts +1 -1
  109. package/src/executeQueryPlan.ts +18 -9
  110. package/src/index.ts +323 -481
  111. package/src/make-fetch-happen.d.ts +1 -1
  112. package/src/operationContext.ts +2 -2
  113. package/src/schema-helper/addResolversToSchema.ts +83 -0
  114. package/src/schema-helper/error.ts +11 -0
  115. package/src/schema-helper/index.ts +3 -0
  116. package/src/schema-helper/resolverMap.ts +23 -0
  117. package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +370 -0
  118. package/src/{__tests__ → supergraphManagers/IntrospectAndCompose/__tests__}/loadServicesFromRemoteEndpoint.test.ts +7 -7
  119. package/src/supergraphManagers/IntrospectAndCompose/__tests__/tsconfig.json +8 -0
  120. package/src/supergraphManagers/IntrospectAndCompose/index.ts +160 -0
  121. package/src/{loadServicesFromRemoteEndpoint.ts → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts} +7 -7
  122. package/src/supergraphManagers/LegacyFetcher/index.ts +226 -0
  123. package/src/supergraphManagers/LocalCompose/index.ts +79 -0
  124. package/src/supergraphManagers/UplinkFetcher/__tests__/loadSupergraphSdlFromStorage.test.ts +343 -0
  125. package/src/supergraphManagers/UplinkFetcher/__tests__/tsconfig.json +8 -0
  126. package/src/supergraphManagers/UplinkFetcher/index.ts +128 -0
  127. package/src/{loadSupergraphSdlFromStorage.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts} +63 -14
  128. package/src/supergraphManagers/UplinkFetcher/outOfBandReporter.ts +126 -0
  129. package/src/supergraphManagers/index.ts +4 -0
  130. package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +13 -10
  131. package/src/utilities/array.ts +1 -1
  132. package/src/utilities/createHash.ts +10 -0
  133. package/src/utilities/isNodeLike.ts +11 -0
  134. package/CHANGELOG.md +0 -452
  135. package/dist/legacyLoadServicesFromStorage.d.ts +0 -20
  136. package/dist/legacyLoadServicesFromStorage.d.ts.map +0 -1
  137. package/dist/legacyLoadServicesFromStorage.js +0 -62
  138. package/dist/legacyLoadServicesFromStorage.js.map +0 -1
  139. package/dist/loadServicesFromRemoteEndpoint.d.ts +0 -13
  140. package/dist/loadServicesFromRemoteEndpoint.d.ts.map +0 -1
  141. package/dist/loadServicesFromRemoteEndpoint.js.map +0 -1
  142. package/dist/loadSupergraphSdlFromStorage.d.ts +0 -12
  143. package/dist/loadSupergraphSdlFromStorage.d.ts.map +0 -1
  144. package/dist/loadSupergraphSdlFromStorage.js.map +0 -1
  145. package/dist/outOfBandReporter.d.ts +0 -15
  146. package/dist/outOfBandReporter.d.ts.map +0 -1
  147. package/dist/outOfBandReporter.js +0 -88
  148. package/dist/outOfBandReporter.js.map +0 -1
  149. package/src/__tests__/gateway/composedSdl.test.ts +0 -44
  150. package/src/__tests__/integration/legacyNetworkRequests.test.ts +0 -279
  151. package/src/__tests__/integration/legacyNockMocks.ts +0 -113
  152. package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +0 -664
  153. package/src/legacyLoadServicesFromStorage.ts +0 -170
  154. package/src/outOfBandReporter.ts +0 -128
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
13
13
  return (mod && mod.__esModule) ? mod : { "default": mod };
14
14
  };
15
15
  Object.defineProperty(exports, "__esModule", { value: true });
16
- exports.buildOperationContext = exports.executeQueryPlan = exports.ApolloGateway = exports.SERVICE_DEFINITION_QUERY = exports.HEALTH_CHECK_QUERY = exports.GCS_RETRY_COUNT = exports.getDefaultGcsFetcher = exports.getDefaultFetcher = void 0;
16
+ exports.LocalCompose = exports.IntrospectAndCompose = exports.buildOperationContext = exports.executeQueryPlan = exports.ApolloGateway = exports.SERVICE_DEFINITION_QUERY = exports.HEALTH_CHECK_QUERY = exports.getDefaultFetcher = void 0;
17
17
  const util_1 = require("util");
18
18
  const apollo_server_caching_1 = require("apollo-server-caching");
19
19
  const graphql_1 = require("graphql");
@@ -22,7 +22,6 @@ const operationContext_1 = require("./operationContext");
22
22
  Object.defineProperty(exports, "buildOperationContext", { enumerable: true, get: function () { return operationContext_1.buildOperationContext; } });
23
23
  const executeQueryPlan_1 = require("./executeQueryPlan");
24
24
  Object.defineProperty(exports, "executeQueryPlan", { enumerable: true, get: function () { return executeQueryPlan_1.executeQueryPlan; } });
25
- const loadServicesFromRemoteEndpoint_1 = require("./loadServicesFromRemoteEndpoint");
26
25
  const types_1 = require("./datasources/types");
27
26
  const RemoteGraphQLDataSource_1 = require("./datasources/RemoteGraphQLDataSource");
28
27
  const values_1 = require("graphql/execution/values");
@@ -30,12 +29,13 @@ const make_fetch_happen_1 = __importDefault(require("make-fetch-happen"));
30
29
  const cache_1 = require("./cache");
31
30
  const query_planner_1 = require("@apollo/query-planner");
32
31
  const config_1 = require("./config");
33
- const loadSupergraphSdlFromStorage_1 = require("./loadSupergraphSdlFromStorage");
34
- const legacyLoadServicesFromStorage_1 = require("./legacyLoadServicesFromStorage");
35
32
  const api_1 = require("@opentelemetry/api");
36
33
  const opentelemetry_1 = require("./utilities/opentelemetry");
34
+ const createHash_1 = require("./utilities/createHash");
35
+ const supergraphManagers_1 = require("./supergraphManagers");
36
+ Object.defineProperty(exports, "IntrospectAndCompose", { enumerable: true, get: function () { return supergraphManagers_1.IntrospectAndCompose; } });
37
+ Object.defineProperty(exports, "LocalCompose", { enumerable: true, get: function () { return supergraphManagers_1.LocalCompose; } });
37
38
  const federation_internals_1 = require("@apollo/federation-internals");
38
- const composition_1 = require("@apollo/composition");
39
39
  function getDefaultFetcher() {
40
40
  const { name, version } = require('../package.json');
41
41
  return make_fetch_happen_1.default.defaults({
@@ -55,19 +55,16 @@ function getDefaultFetcher() {
55
55
  });
56
56
  }
57
57
  exports.getDefaultFetcher = getDefaultFetcher;
58
- exports.getDefaultGcsFetcher = (0, util_1.deprecate)(getDefaultFetcher, `'getDefaultGcsFetcher' is deprecated. Use 'getDefaultFetcher' instead.`);
59
- exports.GCS_RETRY_COUNT = 5;
60
58
  exports.HEALTH_CHECK_QUERY = 'query __ApolloServiceHealthCheck__ { __typename }';
61
59
  exports.SERVICE_DEFINITION_QUERY = 'query __ApolloGetServiceDefinition__ { _service { sdl } }';
62
60
  class ApolloGateway {
63
61
  constructor(config) {
64
- var _a, _b;
62
+ var _a;
65
63
  this.serviceMap = Object.create(null);
66
64
  this.onSchemaChangeListeners = new Set();
67
65
  this.onSchemaLoadOrUpdateListeners = new Set();
68
- this.serviceDefinitions = [];
69
- this.serviceSdlCache = new Map();
70
66
  this.warnedStates = Object.create(null);
67
+ this.toDispose = [];
71
68
  this.executor = async (requestContext) => {
72
69
  const spanAttributes = requestContext.operationName
73
70
  ? { operationName: requestContext.operationName }
@@ -160,38 +157,12 @@ class ApolloGateway {
160
157
  this.fetcher = (config === null || config === void 0 ? void 0 : config.fetcher) || getDefaultFetcher();
161
158
  this.experimental_didResolveQueryPlan =
162
159
  config === null || config === void 0 ? void 0 : config.experimental_didResolveQueryPlan;
163
- this.experimental_didFailComposition =
164
- config === null || config === void 0 ? void 0 : config.experimental_didFailComposition;
165
- this.experimental_didUpdateComposition =
166
- config === null || config === void 0 ? void 0 : config.experimental_didUpdateComposition;
167
- this.experimental_pollInterval = config === null || config === void 0 ? void 0 : config.experimental_pollInterval;
168
- if ((0, config_1.isPrecomposedManagedConfig)(this.config)) {
169
- const envEndpoint = process.env.APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT;
170
- this.schemaConfigDeliveryEndpoint =
171
- (_b = (_a = this.config.schemaConfigDeliveryEndpoint) !== null && _a !== void 0 ? _a : envEndpoint) !== null && _b !== void 0 ? _b : 'https://uplink.api.apollographql.com/';
172
- }
173
- else if ((0, config_1.isLegacyManagedConfig)(this.config)) {
174
- this.schemaConfigDeliveryEndpoint = null;
175
- }
176
- if ((0, config_1.isManuallyManagedConfig)(this.config)) {
177
- if ('experimental_updateSupergraphSdl' in this.config) {
178
- this.updateServiceDefinitions =
179
- this.config.experimental_updateSupergraphSdl;
180
- }
181
- else if ('experimental_updateServiceDefinitions' in this.config) {
182
- this.updateServiceDefinitions =
183
- this.config.experimental_updateServiceDefinitions;
184
- }
185
- else {
186
- throw Error('Programming error: unexpected manual configuration provided');
187
- }
188
- }
189
- else {
190
- this.updateServiceDefinitions = this.loadServiceDefinitions;
191
- }
192
- if ((0, config_1.isDynamicConfig)(this.config)) {
193
- this.issueDynamicWarningsIfApplicable();
194
- }
160
+ this.experimental_didUpdateSupergraph =
161
+ config === null || config === void 0 ? void 0 : config.experimental_didUpdateSupergraph;
162
+ this.pollIntervalInMs =
163
+ (_a = config === null || config === void 0 ? void 0 : config.pollIntervalInMs) !== null && _a !== void 0 ? _a : config === null || config === void 0 ? void 0 : config.experimental_pollInterval;
164
+ this.issueConfigurationWarningsIfApplicable();
165
+ this.logger.debug('Gateway successfully initialized (but not yet loaded)');
195
166
  this.state = { phase: 'initialized' };
196
167
  }
197
168
  initLogger() {
@@ -213,18 +184,18 @@ class ApolloGateway {
213
184
  sizeCalculator: approximateObjectSize,
214
185
  });
215
186
  }
216
- issueDynamicWarningsIfApplicable() {
187
+ issueConfigurationWarningsIfApplicable() {
217
188
  if ((0, config_1.isManagedConfig)(this.config) &&
218
- this.config.experimental_pollInterval &&
219
- this.config.experimental_pollInterval < 10000) {
220
- this.experimental_pollInterval = 10000;
189
+ this.pollIntervalInMs &&
190
+ this.pollIntervalInMs < 10000) {
191
+ this.pollIntervalInMs = 10000;
221
192
  this.logger.warn('Polling Apollo services at a frequency of less than once per 10 ' +
222
193
  'seconds (10000) is disallowed. Instead, the minimum allowed ' +
223
194
  'pollInterval of 10000 will be used. Please reconfigure your ' +
224
- 'experimental_pollInterval accordingly. If this is problematic for ' +
195
+ '`pollIntervalInMs` accordingly. If this is problematic for ' +
225
196
  'your team, please contact support.');
226
197
  }
227
- if (this.config.experimental_pollInterval && (0, config_1.isRemoteConfig)(this.config)) {
198
+ if (this.pollIntervalInMs && (0, config_1.isServiceListConfig)(this.config)) {
228
199
  this.logger.warn('Polling running services is dangerous and not recommended in production. ' +
229
200
  'Polling should only be used against a registry. ' +
230
201
  'If you are polling running services, use with caution.');
@@ -237,8 +208,16 @@ class ApolloGateway {
237
208
  'function when both `experimental_updateSupergraphSdl` and experimental_updateServiceDefinitions` ' +
238
209
  'are provided.');
239
210
  }
211
+ if ('schemaConfigDeliveryEndpoint' in this.config) {
212
+ this.logger.warn('The `schemaConfigDeliveryEndpoint` option is deprecated and will be removed in a future version of `@apollo/gateway`. Please migrate to the equivalent (array form) `uplinkEndpoints` configuration option.');
213
+ }
214
+ if ('experimental_pollInterval' in this.config) {
215
+ this.logger.warn('The `experimental_pollInterval` option is deprecated and will be removed in a future version of `@apollo/gateway`. Please migrate to the equivalent `pollIntervalInMs` configuration option.');
216
+ }
240
217
  }
241
218
  async load(options) {
219
+ var _a, _b, _c, _d;
220
+ this.logger.debug('Loading gateway...');
242
221
  if (this.state.phase !== 'initialized') {
243
222
  throw Error(`ApolloGateway.load called in surprising state ${this.state.phase}`);
244
223
  }
@@ -259,11 +238,72 @@ class ApolloGateway {
259
238
  : undefined,
260
239
  };
261
240
  }
262
- const unrefTimer = !!options && !options.apollo;
263
241
  this.maybeWarnOnConflictingConfig();
264
- (0, config_1.isStaticConfig)(this.config)
265
- ? this.loadStatic(this.config)
266
- : await this.loadDynamic(unrefTimer);
242
+ if ((0, config_1.isStaticSupergraphSdlConfig)(this.config)) {
243
+ const supergraphSdl = this.config.supergraphSdl;
244
+ await this.initializeSupergraphManager({
245
+ initialize: async () => {
246
+ return {
247
+ supergraphSdl,
248
+ };
249
+ },
250
+ });
251
+ }
252
+ else if ((0, config_1.isLocalConfig)(this.config)) {
253
+ await this.initializeSupergraphManager(new supergraphManagers_1.LocalCompose({
254
+ localServiceList: this.config.localServiceList,
255
+ logger: this.logger,
256
+ }));
257
+ }
258
+ else if ((0, config_1.isManuallyManagedSupergraphSdlGatewayConfig)(this.config)) {
259
+ const supergraphManager = typeof this.config.supergraphSdl === 'object'
260
+ ? this.config.supergraphSdl
261
+ : { initialize: this.config.supergraphSdl };
262
+ await this.initializeSupergraphManager(supergraphManager);
263
+ }
264
+ else if ('experimental_updateServiceDefinitions' in this.config ||
265
+ 'experimental_updateSupergraphSdl' in this.config) {
266
+ const updateServiceDefinitions = 'experimental_updateServiceDefinitions' in this.config
267
+ ? this.config.experimental_updateServiceDefinitions
268
+ : this.config.experimental_updateSupergraphSdl;
269
+ await this.initializeSupergraphManager(new supergraphManagers_1.LegacyFetcher({
270
+ logger: this.logger,
271
+ gatewayConfig: this.config,
272
+ updateServiceDefinitions,
273
+ pollIntervalInMs: this.pollIntervalInMs,
274
+ subgraphHealthCheck: this.config.serviceHealthCheck,
275
+ }));
276
+ }
277
+ else if ((0, config_1.isServiceListConfig)(this.config)) {
278
+ this.logger.warn('The `serviceList` option is deprecated and will be removed in a future version of `@apollo/gateway`. Please migrate to its replacement `IntrospectAndCompose`. More information on `IntrospectAndCompose` can be found in the documentation.');
279
+ await this.initializeSupergraphManager(new supergraphManagers_1.IntrospectAndCompose({
280
+ subgraphs: this.config.serviceList,
281
+ pollIntervalInMs: this.pollIntervalInMs,
282
+ logger: this.logger,
283
+ subgraphHealthCheck: this.config.serviceHealthCheck,
284
+ introspectionHeaders: this.config.introspectionHeaders,
285
+ }));
286
+ }
287
+ else {
288
+ const canUseManagedConfig = ((_a = this.apolloConfig) === null || _a === void 0 ? void 0 : _a.graphRef) && ((_b = this.apolloConfig) === null || _b === void 0 ? void 0 : _b.keyHash);
289
+ if (!canUseManagedConfig) {
290
+ throw new Error('When a manual configuration is not provided, gateway requires an Apollo ' +
291
+ 'configuration. See https://www.apollographql.com/docs/apollo-server/federation/managed-federation/ ' +
292
+ 'for more information. Manual configuration options include: ' +
293
+ '`serviceList`, `supergraphSdl`, and `experimental_updateServiceDefinitions`.');
294
+ }
295
+ const uplinkEndpoints = this.getUplinkEndpoints(this.config);
296
+ await this.initializeSupergraphManager(new supergraphManagers_1.UplinkFetcher({
297
+ graphRef: this.apolloConfig.graphRef,
298
+ apiKey: this.apolloConfig.key,
299
+ uplinkEndpoints,
300
+ maxRetries: (_c = this.config.uplinkMaxRetries) !== null && _c !== void 0 ? _c : uplinkEndpoints.length * 3,
301
+ subgraphHealthCheck: this.config.serviceHealthCheck,
302
+ fetcher: this.fetcher,
303
+ logger: this.logger,
304
+ pollIntervalInMs: (_d = this.pollIntervalInMs) !== null && _d !== void 0 ? _d : 10000,
305
+ }));
306
+ }
267
307
  const mode = (0, config_1.isManagedConfig)(this.config) ? 'managed' : 'unmanaged';
268
308
  this.logger.info(`Gateway successfully loaded schema.\n\t* Mode: ${mode}${this.apolloConfig && this.apolloConfig.graphRef
269
309
  ? `\n\t* Service: ${this.apolloConfig.graphRef}`
@@ -273,92 +313,97 @@ class ApolloGateway {
273
313
  executor: this.executor,
274
314
  };
275
315
  }
276
- loadStatic(config) {
277
- let schema;
278
- let supergraphSdl;
279
- try {
280
- ({ schema, supergraphSdl } = (0, config_1.isLocalConfig)(config)
281
- ? this.createSchemaFromServiceList(config.localServiceList)
282
- : this.createSchemaFromSupergraphSdl(config.supergraphSdl));
283
- this.supergraphSdl = supergraphSdl;
284
- this.updateWithSchemaAndNotify(schema, supergraphSdl, true);
285
- }
286
- catch (e) {
287
- this.state = { phase: 'failed to load' };
288
- throw e;
289
- }
290
- this.state = { phase: 'loaded' };
316
+ getUplinkEndpoints(config) {
317
+ var _a, _b, _c, _d;
318
+ const rawEndpointsString = process.env.APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT;
319
+ const envEndpoints = (_a = rawEndpointsString === null || rawEndpointsString === void 0 ? void 0 : rawEndpointsString.split(',')) !== null && _a !== void 0 ? _a : null;
320
+ return ((_d = (_c = (_b = config.uplinkEndpoints) !== null && _b !== void 0 ? _b : (config.schemaConfigDeliveryEndpoint
321
+ ? [config.schemaConfigDeliveryEndpoint]
322
+ : null)) !== null && _c !== void 0 ? _c : envEndpoints) !== null && _d !== void 0 ? _d : [
323
+ 'https://uplink.api.apollographql.com/',
324
+ 'https://aws.uplink.api.apollographql.com/',
325
+ ]);
291
326
  }
292
- async loadDynamic(unrefTimer) {
327
+ getIdForSupergraphSdl(supergraphSdl) {
328
+ return (0, createHash_1.createHash)('sha256').update(supergraphSdl).digest('hex');
329
+ }
330
+ async initializeSupergraphManager(supergraphManager) {
293
331
  try {
294
- await this.updateSchema();
332
+ const result = await supergraphManager.initialize({
333
+ update: this.externalSupergraphUpdateCallback.bind(this),
334
+ healthCheck: this.externalSubgraphHealthCheckCallback.bind(this),
335
+ getDataSource: this.externalGetDataSourceCallback.bind(this),
336
+ });
337
+ if (!(result === null || result === void 0 ? void 0 : result.supergraphSdl)) {
338
+ throw new Error('Provided `supergraphSdl` function did not return an object containing a `supergraphSdl` property');
339
+ }
340
+ if (result === null || result === void 0 ? void 0 : result.cleanup) {
341
+ if (typeof result.cleanup === 'function') {
342
+ this.toDispose.push(result.cleanup);
343
+ }
344
+ else {
345
+ this.logger.error('Provided `supergraphSdl` function returned an invalid `cleanup` property (must be a function)');
346
+ }
347
+ }
348
+ this.externalSupergraphUpdateCallback(result.supergraphSdl);
295
349
  }
296
350
  catch (e) {
297
351
  this.state = { phase: 'failed to load' };
352
+ await this.performCleanupAndLogErrors();
298
353
  throw e;
299
354
  }
300
355
  this.state = { phase: 'loaded' };
301
- if (this.shouldBeginPolling()) {
302
- this.pollServices(unrefTimer);
303
- }
304
356
  }
305
- shouldBeginPolling() {
306
- return (0, config_1.isManagedConfig)(this.config) || this.experimental_pollInterval;
307
- }
308
- async updateSchema() {
309
- this.logger.debug('Checking for composition updates...');
310
- const result = await this.updateServiceDefinitions(this.config);
311
- if ((0, config_1.isSupergraphSdlUpdate)(result)) {
312
- await this.updateWithSupergraphSdl(result);
357
+ externalSupergraphUpdateCallback(supergraphSdl) {
358
+ switch (this.state.phase) {
359
+ case 'failed to load':
360
+ throw new Error("Can't call `update` callback after gateway failed to load.");
361
+ case 'updating schema':
362
+ throw new Error("Can't call `update` callback while supergraph update is in progress.");
363
+ case 'stopped':
364
+ throw new Error("Can't call `update` callback after gateway has been stopped.");
365
+ case 'stopping':
366
+ throw new Error("Can't call `update` callback while gateway is stopping.");
367
+ case 'loaded':
368
+ case 'initialized':
369
+ break;
370
+ default:
371
+ throw new UnreachableCaseError(this.state);
313
372
  }
314
- else if ((0, config_1.isServiceDefinitionUpdate)(result)) {
315
- await this.updateByComposition(result);
373
+ this.state = { phase: 'updating schema' };
374
+ try {
375
+ this.updateWithSupergraphSdl({
376
+ supergraphSdl,
377
+ id: this.getIdForSupergraphSdl(supergraphSdl),
378
+ });
316
379
  }
317
- else {
318
- throw new Error('Programming error: unexpected result type from `updateServiceDefinitions`');
380
+ finally {
381
+ this.state = { phase: 'loaded' };
319
382
  }
320
383
  }
321
- async updateByComposition(result) {
322
- if (!result.serviceDefinitions ||
323
- JSON.stringify(this.serviceDefinitions) ===
324
- JSON.stringify(result.serviceDefinitions)) {
325
- this.logger.debug('No change in service definitions since last check.');
326
- return;
327
- }
328
- const previousSchema = this.schema;
329
- const previousServiceDefinitions = this.serviceDefinitions;
330
- const previousCompositionMetadata = this.compositionMetadata;
331
- if (previousSchema) {
332
- this.logger.info('New service definitions were found.');
333
- }
334
- await this.maybePerformServiceHealthCheck(result);
335
- this.compositionMetadata = result.compositionMetadata;
336
- this.serviceDefinitions = result.serviceDefinitions;
337
- const { schema, supergraphSdl } = this.createSchemaFromServiceList(result.serviceDefinitions);
338
- if (!supergraphSdl) {
339
- this.logger.error("A valid schema couldn't be composed. Falling back to previous schema.");
384
+ async externalSubgraphHealthCheckCallback(supergraphSdl) {
385
+ const serviceList = this.serviceListFromSupergraphSdl(supergraphSdl);
386
+ const serviceMap = serviceList.reduce((serviceMap, serviceDef) => {
387
+ serviceMap[serviceDef.name] = {
388
+ url: serviceDef.url,
389
+ dataSource: this.createDataSource(serviceDef),
390
+ };
391
+ return serviceMap;
392
+ }, Object.create(null));
393
+ try {
394
+ await this.serviceHealthCheck(serviceMap);
340
395
  }
341
- else {
342
- this.updateWithSchemaAndNotify(schema, supergraphSdl);
343
- if (this.experimental_didUpdateComposition) {
344
- this.experimental_didUpdateComposition({
345
- serviceDefinitions: result.serviceDefinitions,
346
- schema: schema.toGraphQLJSSchema(),
347
- ...(this.compositionMetadata && {
348
- compositionMetadata: this.compositionMetadata,
349
- }),
350
- }, previousServiceDefinitions &&
351
- previousSchema && {
352
- serviceDefinitions: previousServiceDefinitions,
353
- schema: previousSchema,
354
- ...(previousCompositionMetadata && {
355
- compositionMetadata: previousCompositionMetadata,
356
- }),
357
- });
358
- }
396
+ catch (e) {
397
+ throw new Error('The gateway subgraphs health check failed. Updating to the provided ' +
398
+ '`supergraphSdl` will likely result in future request failures to ' +
399
+ 'subgraphs. The following error occurred during the health check:\n' +
400
+ e.message);
359
401
  }
360
402
  }
361
- async updateWithSupergraphSdl(result) {
403
+ externalGetDataSourceCallback({ name, url, }) {
404
+ return this.getOrCreateDataSource({ name, url });
405
+ }
406
+ updateWithSupergraphSdl(result) {
362
407
  if (result.id === this.compositionId) {
363
408
  this.logger.debug('No change in composition since last check.');
364
409
  return;
@@ -370,18 +415,17 @@ class ApolloGateway {
370
415
  if (previousSchema) {
371
416
  this.logger.info('Updated Supergraph SDL was found.');
372
417
  }
373
- await this.maybePerformServiceHealthCheck(result);
374
418
  this.compositionId = result.id;
375
- this.supergraphSdl = result.supergraphSdl;
419
+ this.supergraphSdl = supergraphSdl;
376
420
  if (!supergraphSdl) {
377
421
  this.logger.error("A valid schema couldn't be composed. Falling back to previous schema.");
378
422
  }
379
423
  else {
380
424
  this.updateWithSchemaAndNotify(schema, supergraphSdl);
381
- if (this.experimental_didUpdateComposition) {
382
- this.experimental_didUpdateComposition({
425
+ if (this.experimental_didUpdateSupergraph) {
426
+ this.experimental_didUpdateSupergraph({
383
427
  compositionId: result.id,
384
- supergraphSdl: result.supergraphSdl,
428
+ supergraphSdl,
385
429
  schema: schema.toGraphQLJSSchema(),
386
430
  }, previousCompositionId && previousSupergraphSdl && previousSchema
387
431
  ? {
@@ -425,31 +469,6 @@ class ApolloGateway {
425
469
  }
426
470
  });
427
471
  }
428
- async maybePerformServiceHealthCheck(update) {
429
- if (this.config.serviceHealthCheck) {
430
- const serviceList = (0, config_1.isSupergraphSdlUpdate)(update)
431
- ?
432
- this.serviceListFromSupergraphSdl(update.supergraphSdl)
433
- :
434
- update.serviceDefinitions;
435
- const serviceMap = serviceList.reduce((serviceMap, serviceDef) => {
436
- serviceMap[serviceDef.name] = {
437
- url: serviceDef.url,
438
- dataSource: this.createDataSource(serviceDef),
439
- };
440
- return serviceMap;
441
- }, Object.create(null));
442
- try {
443
- await this.serviceHealthCheck(serviceMap);
444
- }
445
- catch (e) {
446
- throw new Error('The gateway did not update its schema due to failed service health checks. ' +
447
- 'The gateway will continue to operate with the previous schema and reattempt updates. ' +
448
- 'The following error occurred during the health check:\n' +
449
- e.message);
450
- }
451
- }
452
- }
453
472
  serviceHealthCheck(serviceMap = this.serviceMap) {
454
473
  return Promise.all(Object.entries(serviceMap).map(([name, { dataSource }]) => dataSource
455
474
  .process({
@@ -462,34 +481,6 @@ class ApolloGateway {
462
481
  throw new Error(`[${name}]: ${e.message}`);
463
482
  })));
464
483
  }
465
- createSchemaFromServiceList(serviceList) {
466
- this.logger.debug(`Composing schema from service list: \n${serviceList
467
- .map(({ name, url }) => ` ${url || 'local'}: ${name}`)
468
- .join('\n')}`);
469
- const compositionResult = (0, composition_1.composeServices)(serviceList);
470
- const errors = compositionResult.errors;
471
- if (errors) {
472
- if (this.experimental_didFailComposition) {
473
- this.experimental_didFailComposition({
474
- errors,
475
- serviceList,
476
- ...(this.compositionMetadata && {
477
- compositionMetadata: this.compositionMetadata,
478
- }),
479
- });
480
- }
481
- throw Error("A valid schema couldn't be composed. The following composition errors were found:\n" +
482
- errors.map((e) => '\t' + e.message).join('\n'));
483
- }
484
- else {
485
- this.createServices(serviceList);
486
- this.logger.debug('Schema loaded and ready for execution');
487
- return {
488
- schema: compositionResult.schema,
489
- supergraphSdl: compositionResult.supergraphSdl,
490
- };
491
- }
492
- }
493
484
  serviceListFromSupergraphSdl(supergraphSdl) {
494
485
  return (0, federation_internals_1.buildSupergraphSchema)(supergraphSdl)[1];
495
486
  }
@@ -513,63 +504,11 @@ class ApolloGateway {
513
504
  this.onSchemaLoadOrUpdateListeners.delete(callback);
514
505
  };
515
506
  }
516
- async pollServices(unrefTimer) {
517
- switch (this.state.phase) {
518
- case 'stopping':
519
- case 'stopped':
520
- case 'failed to load':
521
- return;
522
- case 'initialized':
523
- throw Error('pollServices should not be called before load!');
524
- case 'polling':
525
- throw Error('pollServices should not be called while in the middle of polling!');
526
- case 'waiting to poll':
527
- throw Error('pollServices should not be called while already waiting to poll!');
528
- case 'loaded':
529
- break;
530
- default:
531
- throw new UnreachableCaseError(this.state);
532
- }
533
- await new Promise((doneWaiting) => {
534
- this.state = {
535
- phase: 'waiting to poll',
536
- doneWaiting,
537
- pollWaitTimer: setTimeout(() => {
538
- if (this.state.phase == 'waiting to poll') {
539
- this.state.doneWaiting();
540
- }
541
- }, this.experimental_pollInterval || 10000),
542
- };
543
- if (unrefTimer) {
544
- this.state.pollWaitTimer.unref();
545
- }
546
- });
547
- if (this.state.phase !== 'waiting to poll') {
548
- return;
549
- }
550
- let pollingDone;
551
- this.state = {
552
- phase: 'polling',
553
- pollingDonePromise: new Promise((res) => {
554
- pollingDone = res;
555
- }),
556
- };
557
- try {
558
- await this.updateSchema();
559
- }
560
- catch (err) {
561
- this.logger.error((err && err.message) || err);
562
- }
563
- if (this.state.phase === 'polling') {
564
- this.state = { phase: 'loaded' };
565
- setImmediate(() => this.pollServices(unrefTimer));
566
- }
567
- pollingDone();
568
- }
569
- createAndCacheDataSource(serviceDef) {
507
+ getOrCreateDataSource(serviceDef) {
570
508
  if (this.serviceMap[serviceDef.name] &&
571
- serviceDef.url === this.serviceMap[serviceDef.name].url)
509
+ serviceDef.url === this.serviceMap[serviceDef.name].url) {
572
510
  return this.serviceMap[serviceDef.name].dataSource;
511
+ }
573
512
  const dataSource = this.createDataSource(serviceDef);
574
513
  this.serviceMap[serviceDef.name] = { url: serviceDef.url, dataSource };
575
514
  return dataSource;
@@ -586,51 +525,7 @@ class ApolloGateway {
586
525
  }
587
526
  createServices(services) {
588
527
  for (const serviceDef of services) {
589
- this.createAndCacheDataSource(serviceDef);
590
- }
591
- }
592
- async loadServiceDefinitions(config) {
593
- var _a, _b;
594
- if ((0, config_1.isRemoteConfig)(config)) {
595
- const serviceList = config.serviceList.map((serviceDefinition) => ({
596
- ...serviceDefinition,
597
- dataSource: this.createAndCacheDataSource(serviceDefinition),
598
- }));
599
- return (0, loadServicesFromRemoteEndpoint_1.getServiceDefinitionsFromRemoteEndpoint)({
600
- serviceList,
601
- async getServiceIntrospectionHeaders(service) {
602
- return typeof config.introspectionHeaders === 'function'
603
- ? await config.introspectionHeaders(service)
604
- : config.introspectionHeaders;
605
- },
606
- serviceSdlCache: this.serviceSdlCache,
607
- });
608
- }
609
- const canUseManagedConfig = ((_a = this.apolloConfig) === null || _a === void 0 ? void 0 : _a.graphRef) && ((_b = this.apolloConfig) === null || _b === void 0 ? void 0 : _b.keyHash);
610
- if (!canUseManagedConfig) {
611
- throw new Error('When a manual configuration is not provided, gateway requires an Apollo ' +
612
- 'configuration. See https://www.apollographql.com/docs/apollo-server/federation/managed-federation/ ' +
613
- 'for more information. Manual configuration options include: ' +
614
- '`serviceList`, `supergraphSdl`, and `experimental_updateServiceDefinitions`.');
615
- }
616
- if ((0, config_1.isPrecomposedManagedConfig)(config)) {
617
- return (0, loadSupergraphSdlFromStorage_1.loadSupergraphSdlFromStorage)({
618
- graphRef: this.apolloConfig.graphRef,
619
- apiKey: this.apolloConfig.key,
620
- endpoint: this.schemaConfigDeliveryEndpoint,
621
- fetcher: this.fetcher,
622
- });
623
- }
624
- else if ((0, config_1.isLegacyManagedConfig)(config)) {
625
- return (0, legacyLoadServicesFromStorage_1.getServiceDefinitionsFromStorage)({
626
- graphRef: this.apolloConfig.graphRef,
627
- apiKeyHash: this.apolloConfig.keyHash,
628
- federationVersion: config.federationVersion || 1,
629
- fetcher: this.fetcher,
630
- });
631
- }
632
- else {
633
- throw new Error('Programming error: unhandled configuration');
528
+ this.getOrCreateDataSource(serviceDef);
634
529
  }
635
530
  }
636
531
  maybeWarnOnConflictingConfig() {
@@ -667,6 +562,16 @@ class ApolloGateway {
667
562
  }
668
563
  });
669
564
  }
565
+ async performCleanupAndLogErrors() {
566
+ if (this.toDispose.length === 0)
567
+ return;
568
+ await Promise.all(this.toDispose.map((p) => p().catch((e) => {
569
+ var _a;
570
+ this.logger.error('Error occured while calling user provided `cleanup` function: ' +
571
+ ((_a = e.message) !== null && _a !== void 0 ? _a : e));
572
+ })));
573
+ this.toDispose = [];
574
+ }
670
575
  async stop() {
671
576
  switch (this.state.phase) {
672
577
  case 'initialized':
@@ -681,33 +586,28 @@ class ApolloGateway {
681
586
  }
682
587
  return;
683
588
  case 'loaded':
684
- this.state = { phase: 'stopped' };
685
- return;
686
- case 'waiting to poll': {
687
- const doneWaiting = this.state.doneWaiting;
688
- clearTimeout(this.state.pollWaitTimer);
689
- this.state = { phase: 'stopped' };
690
- doneWaiting();
691
- return;
692
- }
693
- case 'polling': {
694
- const pollingDonePromise = this.state.pollingDonePromise;
695
- let stoppingDone;
589
+ const stoppingDonePromise = this.performCleanupAndLogErrors();
696
590
  this.state = {
697
591
  phase: 'stopping',
698
- stoppingDonePromise: new Promise((res) => {
699
- stoppingDone = res;
700
- }),
592
+ stoppingDonePromise,
701
593
  };
702
- await pollingDonePromise;
594
+ await stoppingDonePromise;
703
595
  this.state = { phase: 'stopped' };
704
- stoppingDone();
705
596
  return;
597
+ case 'updating schema': {
598
+ throw Error("`ApolloGateway.stop` shouldn't be called from inside a schema change listener");
706
599
  }
707
600
  default:
708
601
  throw new UnreachableCaseError(this.state);
709
602
  }
710
603
  }
604
+ __testing() {
605
+ return {
606
+ state: this.state,
607
+ compositionId: this.compositionId,
608
+ supergraphSdl: this.supergraphSdl,
609
+ };
610
+ }
711
611
  }
712
612
  exports.ApolloGateway = ApolloGateway;
713
613
  ApolloGateway.prototype.onSchemaChange = (0, util_1.deprecate)(ApolloGateway.prototype.onSchemaChange, `'ApolloGateway.prototype.onSchemaChange' is deprecated. Use 'ApolloGateway.prototype.onSchemaLoadOrUpdate' instead.`);