@apollo/gateway 0.43.0 → 0.45.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/CHANGELOG.md +14 -2
  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 +3 -8
  6. package/dist/config.d.ts.map +1 -1
  7. package/dist/config.js +6 -17
  8. package/dist/config.js.map +1 -1
  9. package/dist/index.d.ts +4 -3
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +33 -33
  12. package/dist/index.js.map +1 -1
  13. package/dist/loadSupergraphSdlFromStorage.d.ts +15 -6
  14. package/dist/loadSupergraphSdlFromStorage.d.ts.map +1 -1
  15. package/dist/loadSupergraphSdlFromStorage.js +40 -9
  16. package/dist/loadSupergraphSdlFromStorage.js.map +1 -1
  17. package/dist/outOfBandReporter.d.ts +10 -12
  18. package/dist/outOfBandReporter.d.ts.map +1 -1
  19. package/dist/outOfBandReporter.js +70 -73
  20. package/dist/outOfBandReporter.js.map +1 -1
  21. package/package.json +8 -8
  22. package/src/__generated__/graphqlTypes.ts +13 -11
  23. package/src/__tests__/gateway/reporting.test.ts +5 -3
  24. package/src/__tests__/integration/configuration.test.ts +32 -11
  25. package/src/__tests__/integration/networkRequests.test.ts +44 -32
  26. package/src/__tests__/integration/nockMocks.ts +42 -8
  27. package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +129 -375
  28. package/src/__tests__/nockAssertions.ts +20 -0
  29. package/src/config.ts +10 -43
  30. package/src/index.ts +43 -54
  31. package/src/loadSupergraphSdlFromStorage.ts +61 -12
  32. package/src/outOfBandReporter.ts +87 -89
  33. package/dist/legacyLoadServicesFromStorage.d.ts +0 -20
  34. package/dist/legacyLoadServicesFromStorage.d.ts.map +0 -1
  35. package/dist/legacyLoadServicesFromStorage.js +0 -62
  36. package/dist/legacyLoadServicesFromStorage.js.map +0 -1
  37. package/src/__tests__/integration/legacyNetworkRequests.test.ts +0 -279
  38. package/src/__tests__/integration/legacyNockMocks.ts +0 -113
  39. package/src/legacyLoadServicesFromStorage.ts +0 -170
@@ -0,0 +1,20 @@
1
+ import nock from 'nock';
2
+
3
+ // Ensures an active and clean nock before every test
4
+ export function nockBeforeEach() {
5
+ if (!nock.isActive()) {
6
+ nock.activate();
7
+ }
8
+ // Cleaning _before_ each test ensures that any mocks from a previous test
9
+ // which failed don't affect the current test.
10
+ nock.cleanAll();
11
+ }
12
+
13
+ // Ensures a test is complete (all expected requests were run) and a clean
14
+ // global state after each test.
15
+ export function nockAfterEach() {
16
+ // unmock HTTP interceptor
17
+ nock.restore();
18
+ // effectively nock.isDone() but with more helpful messages in test failures
19
+ expect(nock.activeMocks()).toEqual([]);
20
+ };
package/src/config.ts CHANGED
@@ -135,31 +135,16 @@ export interface RemoteGatewayConfig extends GatewayConfigBase {
135
135
  | ((service: ServiceEndpointDefinition) => Promise<HeadersInit> | HeadersInit);
136
136
  }
137
137
 
138
- // TODO(trevor:cloudconfig): This type goes away
139
- export interface LegacyManagedGatewayConfig extends GatewayConfigBase {
140
- federationVersion?: number;
141
- /**
142
- * Setting this to null will cause the gateway to use the old mechanism for
143
- * managed federation via GCS + composition.
144
- */
145
- schemaConfigDeliveryEndpoint: null;
146
- }
147
-
148
- // TODO(trevor:cloudconfig): This type becomes the only managed config
149
- export interface PrecomposedManagedGatewayConfig extends GatewayConfigBase {
138
+ export interface ManagedGatewayConfig extends GatewayConfigBase {
150
139
  /**
151
140
  * This configuration option shouldn't be used unless by recommendation from
152
- * Apollo staff. This can also be set to `null` (see above) in order to revert
153
- * to the previous mechanism for managed federation.
141
+ * Apollo staff.
154
142
  */
155
- schemaConfigDeliveryEndpoint?: string;
143
+ schemaConfigDeliveryEndpoint?: string; // deprecated
144
+ uplinkEndpoints?: string[];
145
+ uplinkMaxRetries?: number;
156
146
  }
157
147
 
158
- // TODO(trevor:cloudconfig): This union is no longer needed
159
- export type ManagedGatewayConfig =
160
- | LegacyManagedGatewayConfig
161
- | PrecomposedManagedGatewayConfig;
162
-
163
148
  interface ManuallyManagedServiceDefsGatewayConfig extends GatewayConfigBase {
164
149
  experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions;
165
150
  }
@@ -216,30 +201,12 @@ export function isManuallyManagedConfig(
216
201
  export function isManagedConfig(
217
202
  config: GatewayConfig,
218
203
  ): config is ManagedGatewayConfig {
219
- return isPrecomposedManagedConfig(config) || isLegacyManagedConfig(config);
220
- }
221
-
222
- // TODO(trevor:cloudconfig): This merges with `isManagedConfig`
223
- export function isPrecomposedManagedConfig(
224
- config: GatewayConfig,
225
- ): config is PrecomposedManagedGatewayConfig {
226
- return (
227
- !isLegacyManagedConfig(config) &&
228
- (('schemaConfigDeliveryEndpoint' in config &&
229
- typeof config.schemaConfigDeliveryEndpoint === 'string') ||
230
- (!isRemoteConfig(config) &&
231
- !isLocalConfig(config) &&
232
- !isSupergraphSdlConfig(config) &&
233
- !isManuallyManagedConfig(config)))
234
- );
235
- }
236
-
237
- export function isLegacyManagedConfig(
238
- config: GatewayConfig,
239
- ): config is LegacyManagedGatewayConfig {
240
204
  return (
241
- 'schemaConfigDeliveryEndpoint' in config &&
242
- config.schemaConfigDeliveryEndpoint === null
205
+ 'schemaConfigDeliveryEndpoint' in config ||
206
+ (!isRemoteConfig(config) &&
207
+ !isLocalConfig(config) &&
208
+ !isSupergraphSdlConfig(config) &&
209
+ !isManuallyManagedConfig(config))
243
210
  );
244
211
  }
245
212
 
package/src/index.ts CHANGED
@@ -66,12 +66,9 @@ import {
66
66
  ServiceDefinitionUpdate,
67
67
  SupergraphSdlUpdate,
68
68
  CompositionUpdate,
69
- isPrecomposedManagedConfig,
70
- isLegacyManagedConfig,
71
69
  } from './config';
72
- import { loadSupergraphSdlFromStorage } from './loadSupergraphSdlFromStorage';
73
- import { getServiceDefinitionsFromStorage } from './legacyLoadServicesFromStorage';
74
70
  import { buildComposedSchema } from '@apollo/query-planner';
71
+ import { loadSupergraphSdlFromUplinks } from './loadSupergraphSdlFromStorage';
75
72
  import { SpanStatusCode } from '@opentelemetry/api';
76
73
  import { OpenTelemetrySpanNames, tracer } from './utilities/opentelemetry';
77
74
  import { CoreSchema } from '@apollo/core-schema';
@@ -114,20 +111,6 @@ export function getDefaultFetcher() {
114
111
  });
115
112
  }
116
113
 
117
- /**
118
- * TODO(trevor:cloudconfig): Stop exporting this
119
- * @deprecated This will be removed in a future version of @apollo/gateway
120
- */
121
- export const getDefaultGcsFetcher = deprecate(
122
- getDefaultFetcher,
123
- `'getDefaultGcsFetcher' is deprecated. Use 'getDefaultFetcher' instead.`,
124
- );
125
- /**
126
- * TODO(trevor:cloudconfig): Stop exporting this
127
- * @deprecated This will be removed in a future version of @apollo/gateway
128
- */
129
- export const GCS_RETRY_COUNT = 5;
130
-
131
114
  export const HEALTH_CHECK_QUERY =
132
115
  'query __ApolloServiceHealthCheck__ { __typename }';
133
116
  export const SERVICE_DEFINITION_QUERY =
@@ -193,10 +176,13 @@ export class ApolloGateway implements GraphQLService {
193
176
  private serviceSdlCache = new Map<string, string>();
194
177
  private warnedStates: WarnedStates = Object.create(null);
195
178
  private queryPlanner?: QueryPlanner;
179
+ private supergraphSdl?: string;
196
180
  private parsedSupergraphSdl?: DocumentNode;
197
181
  private fetcher: typeof fetch;
198
182
  private compositionId?: string;
199
183
  private state: GatewayState;
184
+ private errorReportingEndpoint: string | undefined =
185
+ process.env.APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT ?? undefined;
200
186
 
201
187
  // Observe query plan, service info, and operation info prior to execution.
202
188
  // The information made available here will give insight into the resulting
@@ -214,12 +200,11 @@ export class ApolloGateway implements GraphQLService {
214
200
  private updateServiceDefinitions: Experimental_UpdateComposition;
215
201
  // how often service defs should be loaded/updated (in ms)
216
202
  private experimental_pollInterval?: number;
217
- // Configure the endpoint by which gateway will access its precomposed schema.
218
- // * `string` means use that endpoint
219
- // * `null` will revert the gateway to legacy mode (polling GCS and composing the schema itself).
203
+ // Configure the endpoints by which gateway will access its precomposed schema.
204
+ // * An array of URLs means use these endpoints to obtain schema, if one is unavailable then try the next.
220
205
  // * `undefined` means the gateway is not using managed federation
221
- // TODO(trevor:cloudconfig): `null` should be disallowed in the future.
222
- private schemaConfigDeliveryEndpoint?: string | null;
206
+ private uplinkEndpoints?: string[];
207
+ private uplinkMaxRetries?: number;
223
208
 
224
209
  constructor(config?: GatewayConfig) {
225
210
  this.config = {
@@ -247,19 +232,23 @@ export class ApolloGateway implements GraphQLService {
247
232
  this.experimental_pollInterval = config?.experimental_pollInterval;
248
233
 
249
234
  // 1. If config is set to a `string`, use it
250
- // 2. If config is explicitly set to `null`, fallback to GCS
251
- // 3. If the env var is set, use that
252
- // 4. If config is `undefined`, use the default uplink URL
253
-
254
- // This if case unobviously handles 1, 2, and 4.
255
- if (isPrecomposedManagedConfig(this.config)) {
256
- const envEndpoint = process.env.APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT;
257
- this.schemaConfigDeliveryEndpoint =
258
- this.config.schemaConfigDeliveryEndpoint ??
259
- envEndpoint ??
260
- 'https://uplink.api.apollographql.com/';
261
- } else if (isLegacyManagedConfig(this.config)) {
262
- this.schemaConfigDeliveryEndpoint = null;
235
+ // 2. If the env var is set, use that
236
+ // 3. If config is `undefined`, use the default uplink URLs
237
+ if (isManagedConfig(this.config)) {
238
+ const rawEndpointsString = process.env.APOLLO_SCHEMA_CONFIG_DELIVERY_ENDPOINT;
239
+ const envEndpoints = rawEndpointsString?.split(",") ?? null;
240
+
241
+ if (this.config.schemaConfigDeliveryEndpoint && !this.config.uplinkEndpoints) {
242
+ this.uplinkEndpoints = [this.config.schemaConfigDeliveryEndpoint];
243
+ } else {
244
+ this.uplinkEndpoints = this.config.uplinkEndpoints ??
245
+ envEndpoints ?? [
246
+ 'https://uplink.api.apollographql.com/',
247
+ 'https://aws.uplink.api.apollographql.com/'
248
+ ];
249
+ }
250
+
251
+ this.uplinkMaxRetries = this.config.uplinkMaxRetries ?? this.uplinkEndpoints.length * 3;
263
252
  }
264
253
 
265
254
  if (isManuallyManagedConfig(this.config)) {
@@ -447,6 +436,7 @@ export class ApolloGateway implements GraphQLService {
447
436
  : this.createSchemaFromSupergraphSdl(config.supergraphSdl));
448
437
  // TODO(trevor): #580 redundant parse
449
438
  this.parsedSupergraphSdl = parse(supergraphSdl);
439
+ this.supergraphSdl = supergraphSdl;
450
440
  this.updateWithSchemaAndNotify(schema, supergraphSdl, true);
451
441
  } catch (e) {
452
442
  this.state = { phase: 'failed to load' };
@@ -576,6 +566,7 @@ export class ApolloGateway implements GraphQLService {
576
566
  await this.maybePerformServiceHealthCheck(result);
577
567
 
578
568
  this.compositionId = result.id;
569
+ this.supergraphSdl = result.supergraphSdl;
579
570
  this.parsedSupergraphSdl = parsedSupergraphSdl;
580
571
 
581
572
  const { schema, supergraphSdl } = this.createSchemaFromSupergraphSdl(
@@ -977,24 +968,22 @@ export class ApolloGateway implements GraphQLService {
977
968
  );
978
969
  }
979
970
 
980
- // TODO(trevor:cloudconfig): This condition goes away completely
981
- if (isPrecomposedManagedConfig(config)) {
982
- return loadSupergraphSdlFromStorage({
983
- graphRef: this.apolloConfig!.graphRef!,
984
- apiKey: this.apolloConfig!.key!,
985
- endpoint: this.schemaConfigDeliveryEndpoint!,
986
- fetcher: this.fetcher,
987
- });
988
- } else if (isLegacyManagedConfig(config)) {
989
- return getServiceDefinitionsFromStorage({
990
- graphRef: this.apolloConfig!.graphRef!,
991
- apiKeyHash: this.apolloConfig!.keyHash!,
992
- federationVersion: config.federationVersion || 1,
993
- fetcher: this.fetcher,
994
- });
995
- } else {
996
- throw new Error('Programming error: unhandled configuration');
997
- }
971
+ const result = await loadSupergraphSdlFromUplinks({
972
+ graphRef: this.apolloConfig!.graphRef!,
973
+ apiKey: this.apolloConfig!.key!,
974
+ endpoints: this.uplinkEndpoints!,
975
+ errorReportingEndpoint: this.errorReportingEndpoint,
976
+ fetcher: this.fetcher,
977
+ compositionId: this.compositionId ?? null,
978
+ maxRetries: this.uplinkMaxRetries!,
979
+ });
980
+
981
+ return (
982
+ result ?? {
983
+ id: this.compositionId!,
984
+ supergraphSdl: this.supergraphSdl!,
985
+ }
986
+ );
998
987
  }
999
988
 
1000
989
  private maybeWarnOnConflictingConfig() {
@@ -1,12 +1,13 @@
1
1
  import { fetch, Response, Request } from 'apollo-server-env';
2
2
  import { GraphQLError } from 'graphql';
3
- import { OutOfBandReporter } from './outOfBandReporter';
3
+ import { SupergraphSdlUpdate } from './config';
4
+ import { submitOutOfBandReportIfConfigured } from './outOfBandReporter';
4
5
  import { SupergraphSdlQuery } from './__generated__/graphqlTypes';
5
6
 
6
7
  // Magic /* GraphQL */ comment below is for codegen, do not remove
7
8
  export const SUPERGRAPH_SDL_QUERY = /* GraphQL */`#graphql
8
- query SupergraphSdl($apiKey: String!, $ref: String!) {
9
- routerConfig(ref: $ref, apiKey: $apiKey) {
9
+ query SupergraphSdl($apiKey: String!, $ref: String!, $ifAfterId: ID) {
10
+ routerConfig(ref: $ref, apiKey: $apiKey, ifAfterId: $ifAfterId) {
10
11
  __typename
11
12
  ... on RouterConfigResult {
12
13
  id
@@ -38,17 +39,63 @@ const { name, version } = require('../package.json');
38
39
 
39
40
  const fetchErrorMsg = "An error occurred while fetching your schema from Apollo: ";
40
41
 
42
+ let fetchCounter = 0;
43
+
44
+ export async function loadSupergraphSdlFromUplinks({
45
+ graphRef,
46
+ apiKey,
47
+ endpoints,
48
+ errorReportingEndpoint,
49
+ fetcher,
50
+ compositionId,
51
+ maxRetries,
52
+ }: {
53
+ graphRef: string;
54
+ apiKey: string;
55
+ endpoints: string[];
56
+ errorReportingEndpoint: string | undefined,
57
+ fetcher: typeof fetch;
58
+ compositionId: string | null;
59
+ maxRetries: number
60
+ }) : Promise<SupergraphSdlUpdate | null> {
61
+ let retries = 0;
62
+ let lastException = null;
63
+ let result: SupergraphSdlUpdate | null = null;
64
+ while (retries++ <= maxRetries && result == null) {
65
+ try {
66
+ result = await loadSupergraphSdlFromStorage({
67
+ graphRef,
68
+ apiKey,
69
+ endpoint: endpoints[fetchCounter++ % endpoints.length],
70
+ errorReportingEndpoint,
71
+ fetcher,
72
+ compositionId
73
+ });
74
+ } catch (e) {
75
+ lastException = e;
76
+ }
77
+ }
78
+ if (result === null && lastException !== null) {
79
+ throw lastException;
80
+ }
81
+ return result;
82
+ }
83
+
41
84
  export async function loadSupergraphSdlFromStorage({
42
85
  graphRef,
43
86
  apiKey,
44
87
  endpoint,
88
+ errorReportingEndpoint,
45
89
  fetcher,
90
+ compositionId,
46
91
  }: {
47
92
  graphRef: string;
48
93
  apiKey: string;
49
94
  endpoint: string;
95
+ errorReportingEndpoint?: string;
50
96
  fetcher: typeof fetch;
51
- }) {
97
+ compositionId: string | null;
98
+ }) : Promise<SupergraphSdlUpdate | null> {
52
99
  let result: Response;
53
100
  const requestDetails = {
54
101
  method: 'POST',
@@ -57,6 +104,7 @@ export async function loadSupergraphSdlFromStorage({
57
104
  variables: {
58
105
  ref: graphRef,
59
106
  apiKey,
107
+ ifAfterId: compositionId,
60
108
  },
61
109
  }),
62
110
  headers: {
@@ -69,19 +117,19 @@ export async function loadSupergraphSdlFromStorage({
69
117
 
70
118
  const request: Request = new Request(endpoint, requestDetails);
71
119
 
72
- const OOBReport = new OutOfBandReporter();
73
- const startTime = new Date()
120
+ const startTime = new Date();
74
121
  try {
75
122
  result = await fetcher(endpoint, requestDetails);
76
123
  } catch (e) {
77
124
  const endTime = new Date();
78
125
 
79
- await OOBReport.submitOutOfBandReportIfConfigured({
126
+ await submitOutOfBandReportIfConfigured({
80
127
  error: e,
81
128
  request,
129
+ endpoint: errorReportingEndpoint,
82
130
  startedAt: startTime,
83
131
  endedAt: endTime,
84
- fetcher
132
+ fetcher,
85
133
  });
86
134
 
87
135
  throw new Error(fetchErrorMsg + (e.message ?? e));
@@ -106,13 +154,14 @@ export async function loadSupergraphSdlFromStorage({
106
154
  );
107
155
  }
108
156
  } else {
109
- await OOBReport.submitOutOfBandReportIfConfigured({
157
+ await submitOutOfBandReportIfConfigured({
110
158
  error: new Error(fetchErrorMsg + result.status + ' ' + result.statusText),
111
159
  request,
160
+ endpoint: errorReportingEndpoint,
112
161
  response: result,
113
162
  startedAt: startTime,
114
163
  endedAt: endTime,
115
- fetcher
164
+ fetcher,
116
165
  });
117
166
  throw new Error(fetchErrorMsg + result.status + ' ' + result.statusText);
118
167
  }
@@ -124,13 +173,13 @@ export async function loadSupergraphSdlFromStorage({
124
173
  supergraphSdl,
125
174
  // messages,
126
175
  } = routerConfig;
127
-
128
- // `supergraphSdl` should not be nullable in the schema, but it currently is
129
176
  return { id, supergraphSdl: supergraphSdl! };
130
177
  } else if (routerConfig.__typename === 'FetchError') {
131
178
  // FetchError case
132
179
  const { code, message } = routerConfig;
133
180
  throw new Error(`${code}: ${message}`);
181
+ } else if (routerConfig.__typename === 'Unchanged') {
182
+ return null;
134
183
  } else {
135
184
  throw new Error('Programming error: unhandled response failure');
136
185
  }
@@ -27,102 +27,100 @@ interface OobReportMutationFailure {
27
27
  data?: OobReportMutation;
28
28
  errors: GraphQLError[];
29
29
  }
30
- export class OutOfBandReporter {
31
- static endpoint: string | null = process.env.APOLLO_OUT_OF_BAND_REPORTER_ENDPOINT || null;
32
30
 
33
- async submitOutOfBandReportIfConfigured({
34
- error,
35
- request,
36
- response,
37
- startedAt,
38
- endedAt,
39
- tags,
40
- fetcher,
41
- }: {
42
- error: Error;
43
- request: Request;
44
- response?: Response;
45
- startedAt: Date;
46
- endedAt: Date;
47
- tags?: string[];
48
- fetcher: typeof fetch;
49
- }) {
50
-
51
- // don't send report if the endpoint url is not configured
52
- if (!OutOfBandReporter.endpoint) {
53
- return;
54
- }
31
+ export async function submitOutOfBandReportIfConfigured({
32
+ error,
33
+ request,
34
+ endpoint,
35
+ response,
36
+ startedAt,
37
+ endedAt,
38
+ tags,
39
+ fetcher,
40
+ }: {
41
+ error: Error;
42
+ request: Request;
43
+ endpoint: string | undefined;
44
+ response?: Response;
45
+ startedAt: Date;
46
+ endedAt: Date;
47
+ tags?: string[];
48
+ fetcher: typeof fetch;
49
+ }) {
50
+ // don't send report if the endpoint url is not configured
51
+ if (!endpoint) {
52
+ return;
53
+ }
55
54
 
56
- let errorCode: ErrorCode;
57
- if (!response) {
58
- errorCode = ErrorCode.ConnectionFailed;
59
- } else {
60
- // possible error situations to check against
61
- switch (response.status) {
62
- case 400:
63
- case 413:
64
- case 422:
65
- errorCode = ErrorCode.InvalidBody;
66
- break;
67
- case 408:
68
- case 504:
69
- errorCode = ErrorCode.Timeout;
70
- break;
71
- case 502:
72
- case 503:
73
- errorCode = ErrorCode.ConnectionFailed;
74
- break;
75
- default:
76
- errorCode = ErrorCode.Other;
77
- }
55
+ let errorCode: ErrorCode;
56
+ if (!response) {
57
+ errorCode = ErrorCode.ConnectionFailed;
58
+ } else {
59
+ // possible error situations to check against
60
+ switch (response.status) {
61
+ case 400:
62
+ case 413:
63
+ case 422:
64
+ errorCode = ErrorCode.InvalidBody;
65
+ break;
66
+ case 408:
67
+ case 504:
68
+ errorCode = ErrorCode.Timeout;
69
+ break;
70
+ case 502:
71
+ case 503:
72
+ errorCode = ErrorCode.ConnectionFailed;
73
+ break;
74
+ default:
75
+ errorCode = ErrorCode.Other;
78
76
  }
77
+ }
79
78
 
80
- const responseBody: string | undefined = await response?.text();
79
+ const responseBody: string | undefined = await response?.text();
81
80
 
82
- const variables: OobReportMutationVariables = {
83
- input: {
84
- error: {
85
- code: errorCode,
86
- message: error.message,
87
- },
88
- request: {
89
- url: request.url,
90
- body: await request.text(),
91
- },
92
- response: response
93
- ? {
94
- httpStatusCode: response.status,
95
- body: responseBody,
96
- }
97
- : null,
98
- startedAt: startedAt.toISOString(),
99
- endedAt: endedAt.toISOString(),
100
- tags: tags,
81
+ const variables: OobReportMutationVariables = {
82
+ input: {
83
+ error: {
84
+ code: errorCode,
85
+ message: error.message,
86
+ },
87
+ request: {
88
+ url: request.url,
89
+ body: await request.text(),
101
90
  },
102
- };
91
+ response: response
92
+ ? {
93
+ httpStatusCode: response.status,
94
+ body: responseBody,
95
+ }
96
+ : null,
97
+ startedAt: startedAt.toISOString(),
98
+ endedAt: endedAt.toISOString(),
99
+ tags: tags,
100
+ },
101
+ };
103
102
 
104
- try {
105
- const oobResponse = await fetcher(OutOfBandReporter.endpoint, {
106
- method: 'POST',
107
- body: JSON.stringify({
108
- query: OUT_OF_BAND_REPORTER_QUERY,
109
- variables,
110
- }),
111
- headers: {
112
- 'apollographql-client-name': name,
113
- 'apollographql-client-version': version,
114
- 'user-agent': `${name}/${version}`,
115
- 'content-type': 'application/json',
116
- },
117
- });
118
- const parsedResponse: OobReportMutationResult = await oobResponse.json();
119
- if (!parsedResponse?.data?.reportError) {
120
- throw new Error(
121
- `Out-of-band error reporting failed: ${oobResponse.status} ${oobResponse.statusText}`,
122
- );
123
- }
124
- } catch (e) {
125
- throw new Error(`Out-of-band error reporting failed: ${e.message ?? e}`);
103
+ try {
104
+ const oobResponse = await fetcher(endpoint, {
105
+ method: 'POST',
106
+ body: JSON.stringify({
107
+ query: OUT_OF_BAND_REPORTER_QUERY,
108
+ variables,
109
+ }),
110
+ headers: {
111
+ 'apollographql-client-name': name,
112
+ 'apollographql-client-version': version,
113
+ 'user-agent': `${name}/${version}`,
114
+ 'content-type': 'application/json',
115
+ },
116
+ });
117
+ const parsedResponse: OobReportMutationResult = await oobResponse.json();
118
+ if (!parsedResponse?.data?.reportError) {
119
+ throw new Error(
120
+ `Out-of-band error reporting failed: ${oobResponse.status} ${oobResponse.statusText}`,
121
+ );
126
122
  }
123
+ } catch (e) {
124
+ throw new Error(`Out-of-band error reporting failed: ${e.message ?? e}`);
127
125
  }
128
126
  }
@@ -1,20 +0,0 @@
1
- import { fetch } from 'apollo-server-env';
2
- import { ServiceDefinitionUpdate } from './config';
3
- interface ImplementingServiceLocation {
4
- name: string;
5
- path: string;
6
- }
7
- export interface CompositionMetadata {
8
- formatVersion: number;
9
- id: string;
10
- implementingServiceLocations: ImplementingServiceLocation[];
11
- schemaHash: string;
12
- }
13
- export declare function getServiceDefinitionsFromStorage({ graphRef, apiKeyHash, federationVersion, fetcher, }: {
14
- graphRef: string;
15
- apiKeyHash: string;
16
- federationVersion: number;
17
- fetcher: typeof fetch;
18
- }): Promise<ServiceDefinitionUpdate>;
19
- export {};
20
- //# sourceMappingURL=legacyLoadServicesFromStorage.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"legacyLoadServicesFromStorage.d.ts","sourceRoot":"","sources":["../src/legacyLoadServicesFromStorage.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AAE1C,OAAO,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAiBnD,UAAU,2BAA2B;IACnC,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,mBAAmB;IAClC,aAAa,EAAE,MAAM,CAAC;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,4BAA4B,EAAE,2BAA2B,EAAE,CAAC;IAC5D,UAAU,EAAE,MAAM,CAAC;CACpB;AAsED,wBAAsB,gCAAgC,CAAC,EACrD,QAAQ,EACR,UAAU,EACV,iBAAiB,EACjB,OAAO,GACR,EAAE;IACD,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,OAAO,KAAK,CAAC;CACvB,GAAG,OAAO,CAAC,uBAAuB,CAAC,CA4DnC"}
@@ -1,62 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.getServiceDefinitionsFromStorage = void 0;
4
- const graphql_1 = require("graphql");
5
- const envOverridePartialSchemaBaseUrl = 'APOLLO_PARTIAL_SCHEMA_BASE_URL';
6
- const envOverrideStorageSecretBaseUrl = 'APOLLO_STORAGE_SECRET_BASE_URL';
7
- const urlFromEnvOrDefault = (envKey, fallback) => (process.env[envKey] || fallback).replace(/\/$/, '');
8
- const urlPartialSchemaBase = urlFromEnvOrDefault(envOverridePartialSchemaBaseUrl, 'https://federation.api.apollographql.com/');
9
- const urlStorageSecretBase = urlFromEnvOrDefault(envOverrideStorageSecretBaseUrl, 'https://storage-secrets.api.apollographql.com/');
10
- function getStorageSecretUrl(graphId, apiKeyHash) {
11
- return `${urlStorageSecretBase}/${graphId}/storage-secret/${apiKeyHash}.json`;
12
- }
13
- function fetchApolloGcs(fetcher, ...args) {
14
- const [input, init] = args;
15
- const url = (typeof input === 'object' && input.url) || input;
16
- return fetcher(input, init)
17
- .catch((fetchError) => {
18
- throw new Error('Cannot access Apollo storage: ' + fetchError);
19
- })
20
- .then(async (response) => {
21
- if (response.ok || response.status === 304) {
22
- return response;
23
- }
24
- const body = await response.text();
25
- if (response.status === 403 && body.includes('AccessDenied')) {
26
- throw new Error('Unable to authenticate with Apollo storage ' +
27
- 'while fetching ' +
28
- url +
29
- '. Ensure that the API key is ' +
30
- 'configured properly and that a federated service has been ' +
31
- 'pushed. For details, see ' +
32
- 'https://go.apollo.dev/g/resolve-access-denied.');
33
- }
34
- throw new Error('Could not communicate with Apollo storage: ' + body);
35
- });
36
- }
37
- async function getServiceDefinitionsFromStorage({ graphRef, apiKeyHash, federationVersion, fetcher, }) {
38
- const at = graphRef.indexOf('@');
39
- const graphId = at === -1 ? graphRef : graphRef.substring(0, at);
40
- const graphVariant = at === -1 ? 'current' : graphRef.substring(at + 1);
41
- const storageSecretUrl = getStorageSecretUrl(graphId, apiKeyHash);
42
- const secret = await fetchApolloGcs(fetcher, storageSecretUrl).then((res) => res.json());
43
- const baseUrl = `${urlPartialSchemaBase}/${secret}/${graphVariant}/v${federationVersion}`;
44
- const compositionConfigResponse = await fetchApolloGcs(fetcher, `${baseUrl}/composition-config-link`);
45
- if (compositionConfigResponse.status === 304) {
46
- return { isNewSchema: false };
47
- }
48
- const linkFileResult = await compositionConfigResponse.json();
49
- const compositionMetadata = await fetchApolloGcs(fetcher, `${urlPartialSchemaBase}/${linkFileResult.configPath}`).then((res) => res.json());
50
- const serviceDefinitions = await Promise.all(compositionMetadata.implementingServiceLocations.map(async ({ name, path }) => {
51
- const { url, partialSchemaPath } = await fetcher(`${urlPartialSchemaBase}/${path}`).then((response) => response.json());
52
- const sdl = await fetcher(`${urlPartialSchemaBase}/${partialSchemaPath}`).then((response) => response.text());
53
- return { name, url, typeDefs: (0, graphql_1.parse)(sdl) };
54
- }));
55
- return {
56
- serviceDefinitions,
57
- compositionMetadata,
58
- isNewSchema: true,
59
- };
60
- }
61
- exports.getServiceDefinitionsFromStorage = getServiceDefinitionsFromStorage;
62
- //# sourceMappingURL=legacyLoadServicesFromStorage.js.map