@apollo/gateway 2.1.0-alpha.1 → 2.1.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 (62) hide show
  1. package/dist/config.d.ts +4 -3
  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 +13 -17
  11. package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
  12. package/dist/datasources/types.d.ts +6 -6
  13. package/dist/datasources/types.d.ts.map +1 -1
  14. package/dist/executeQueryPlan.d.ts +2 -2
  15. package/dist/executeQueryPlan.d.ts.map +1 -1
  16. package/dist/executeQueryPlan.js +6 -0
  17. package/dist/executeQueryPlan.js.map +1 -1
  18. package/dist/index.d.ts +7 -8
  19. package/dist/index.d.ts.map +1 -1
  20. package/dist/index.js +15 -12
  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/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -1
  26. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -1
  27. package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts +3 -1
  28. package/dist/supergraphManagers/UplinkSupergraphManager/index.d.ts.map +1 -1
  29. package/dist/supergraphManagers/UplinkSupergraphManager/index.js +2 -1
  30. package/dist/supergraphManagers/UplinkSupergraphManager/index.js.map +1 -1
  31. package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.d.ts +1 -1
  32. package/dist/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.d.ts.map +1 -1
  33. package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.d.ts +1 -2
  34. package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.d.ts.map +1 -1
  35. package/dist/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.js.map +1 -1
  36. package/package.json +9 -11
  37. package/src/__tests__/buildQueryPlan.test.ts +6 -2
  38. package/src/__tests__/executeQueryPlan.test.ts +9 -7
  39. package/src/__tests__/execution-utils.ts +3 -3
  40. package/src/__tests__/gateway/executor.test.ts +2 -2
  41. package/src/__tests__/gateway/lifecycle-hooks.test.ts +1 -1
  42. package/src/__tests__/integration/abstract-types.test.ts +8 -6
  43. package/src/__tests__/integration/managed.test.ts +26 -0
  44. package/src/config.ts +5 -5
  45. package/src/datasources/LocalGraphQLDataSource.ts +2 -4
  46. package/src/datasources/RemoteGraphQLDataSource.ts +27 -42
  47. package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +2 -2
  48. package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +14 -19
  49. package/src/datasources/types.ts +6 -6
  50. package/src/executeQueryPlan.ts +19 -16
  51. package/src/index.ts +23 -43
  52. package/src/logger.ts +1 -1
  53. package/src/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts +2 -2
  54. package/src/supergraphManagers/UplinkSupergraphManager/__tests__/loadSupergraphSdlFromStorage.test.ts +46 -2
  55. package/src/supergraphManagers/UplinkSupergraphManager/index.ts +6 -2
  56. package/src/supergraphManagers/UplinkSupergraphManager/loadSupergraphSdlFromStorage.ts +5 -5
  57. package/src/supergraphManagers/UplinkSupergraphManager/outOfBandReporter.ts +1 -2
  58. package/dist/supergraphManagers/UplinkSupergraphManager/types.d.ts +0 -9
  59. package/dist/supergraphManagers/UplinkSupergraphManager/types.d.ts.map +0 -1
  60. package/dist/supergraphManagers/UplinkSupergraphManager/types.js +0 -5
  61. package/dist/supergraphManagers/UplinkSupergraphManager/types.js.map +0 -1
  62. package/src/supergraphManagers/UplinkSupergraphManager/types.ts +0 -10
@@ -1,17 +1,3 @@
1
- import {
2
- GraphQLRequestContext,
3
- GraphQLResponse,
4
- ValueOrPromise,
5
- GraphQLRequest,
6
- CacheHint,
7
- CacheScope,
8
- CachePolicy,
9
- } from 'apollo-server-types';
10
- import {
11
- ApolloError,
12
- AuthenticationError,
13
- ForbiddenError,
14
- } from 'apollo-server-errors';
15
1
  import { isObject } from '../utilities/predicates';
16
2
  import { GraphQLDataSource, GraphQLDataSourceProcessOptions, GraphQLDataSourceRequestKind } from './types';
17
3
  import { createHash } from '@apollo/utils.createhash';
@@ -19,6 +5,8 @@ import { parseCacheControlHeader } from './parseCacheControlHeader';
19
5
  import fetcher from 'make-fetch-happen';
20
6
  import { Headers as NodeFetchHeaders, Request as NodeFetchRequest } from 'node-fetch';
21
7
  import { Fetcher, FetcherRequestInit, FetcherResponse } from '@apollo/utils.fetcher';
8
+ import { GraphQLError, GraphQLErrorExtensions } from 'graphql';
9
+ import { GatewayCacheHint, GatewayCachePolicy, GatewayGraphQLRequest, GatewayGraphQLRequestContext, GatewayGraphQLResponse } from '@apollo/server-gateway-interface';
22
10
 
23
11
  export class RemoteGraphQLDataSource<
24
12
  TContext extends Record<string, any> = Record<string, any>,
@@ -77,7 +65,7 @@ export class RemoteGraphQLDataSource<
77
65
 
78
66
  async process(
79
67
  options: GraphQLDataSourceProcessOptions<TContext>,
80
- ): Promise<GraphQLResponse> {
68
+ ): Promise<GatewayGraphQLResponse> {
81
69
  const { request, context: originalContext } = options;
82
70
  // Deal with a bit of a hairy situation in typings: when doing health checks
83
71
  // and schema checks we always pass in `{}` as the context even though it's
@@ -160,7 +148,7 @@ export class RemoteGraphQLDataSource<
160
148
  // If APQ was enabled, we'll run the same request again, but add in the
161
149
  // previously omitted `query`. If APQ was NOT enabled, this is the first
162
150
  // request (non-APQ, all the way).
163
- const requestWithQuery: GraphQLRequest = {
151
+ const requestWithQuery: GatewayGraphQLRequest = {
164
152
  query,
165
153
  ...requestWithoutQuery,
166
154
  };
@@ -174,9 +162,9 @@ export class RemoteGraphQLDataSource<
174
162
  }
175
163
 
176
164
  private async sendRequest(
177
- request: GraphQLRequest,
165
+ request: GatewayGraphQLRequest,
178
166
  context: TContext,
179
- ): Promise<GraphQLResponse> {
167
+ ): Promise<GatewayGraphQLResponse> {
180
168
  // This would represent an internal programming error since this shouldn't
181
169
  // be possible in the way that this method is invoked right now.
182
170
  if (!request.http) {
@@ -229,7 +217,7 @@ export class RemoteGraphQLDataSource<
229
217
 
230
218
  public willSendRequest?(
231
219
  options: GraphQLDataSourceProcessOptions<TContext>,
232
- ): ValueOrPromise<void>;
220
+ ): void | Promise<void>;
233
221
 
234
222
  private async respond({
235
223
  response,
@@ -237,11 +225,11 @@ export class RemoteGraphQLDataSource<
237
225
  context,
238
226
  overallCachePolicy,
239
227
  }: {
240
- response: GraphQLResponse;
241
- request: GraphQLRequest;
228
+ response: GatewayGraphQLResponse;
229
+ request: GatewayGraphQLRequest;
242
230
  context: TContext;
243
- overallCachePolicy: CachePolicy | null;
244
- }): Promise<GraphQLResponse> {
231
+ overallCachePolicy: GatewayCachePolicy | null;
232
+ }): Promise<GatewayGraphQLResponse> {
245
233
  const processedResponse =
246
234
  typeof this.didReceiveResponse === 'function'
247
235
  ? await this.didReceiveResponse({ response, request, context })
@@ -256,16 +244,16 @@ export class RemoteGraphQLDataSource<
256
244
  // thus the overall response) is uncacheable. (If you don't like this, you
257
245
  // can tweak the `cache-control` header in your `didReceiveResponse`
258
246
  // method.)
259
- const hint: CacheHint = { maxAge: 0 };
247
+ const hint: GatewayCacheHint = { maxAge: 0 };
260
248
  const maxAge = parsed['max-age'];
261
249
  if (typeof maxAge === 'string' && maxAge.match(/^[0-9]+$/)) {
262
250
  hint.maxAge = +maxAge;
263
251
  }
264
252
  if (parsed['private'] === true) {
265
- hint.scope = CacheScope.Private;
253
+ hint.scope = 'PRIVATE';
266
254
  }
267
255
  if (parsed['public'] === true) {
268
- hint.scope = CacheScope.Public;
256
+ hint.scope = 'PUBLIC';
269
257
  }
270
258
  overallCachePolicy.restrict(hint);
271
259
  }
@@ -275,9 +263,9 @@ export class RemoteGraphQLDataSource<
275
263
 
276
264
  public didReceiveResponse?(
277
265
  requestContext: Required<
278
- Pick<GraphQLRequestContext<TContext>, 'request' | 'response' | 'context'>
266
+ Pick<GatewayGraphQLRequestContext<TContext>, 'request' | 'response' | 'context'>
279
267
  >,
280
- ): ValueOrPromise<GraphQLResponse>;
268
+ ): GatewayGraphQLResponse | Promise<GatewayGraphQLResponse>;
281
269
 
282
270
  public didEncounterError(
283
271
  error: Error,
@@ -302,28 +290,25 @@ export class RemoteGraphQLDataSource<
302
290
  }
303
291
 
304
292
  public async errorFromResponse(response: FetcherResponse) {
305
- const message = `${response.status}: ${response.statusText}`;
306
-
307
- let error: ApolloError;
308
- if (response.status === 401) {
309
- error = new AuthenticationError(message);
310
- } else if (response.status === 403) {
311
- error = new ForbiddenError(message);
312
- } else {
313
- error = new ApolloError(message);
314
- }
315
-
316
293
  const body = await this.parseBody(response);
317
294
 
318
- Object.assign(error.extensions, {
295
+ const extensions: GraphQLErrorExtensions = {
319
296
  response: {
320
297
  url: response.url,
321
298
  status: response.status,
322
299
  statusText: response.statusText,
323
300
  body,
324
301
  },
325
- });
302
+ };
303
+
304
+ if (response.status === 401) {
305
+ extensions.code = 'UNAUTHENTICATED';
306
+ } else if (response.status === 403) {
307
+ extensions.code = 'FORBIDDEN';
308
+ }
326
309
 
327
- return error;
310
+ return new GraphQLError(`${response.status}: ${response.statusText}`, {
311
+ extensions,
312
+ });
328
313
  }
329
314
  }
@@ -2,8 +2,8 @@ import { LocalGraphQLDataSource } from '../LocalGraphQLDataSource';
2
2
  import { buildSubgraphSchema } from '@apollo/subgraph';
3
3
  import gql from 'graphql-tag';
4
4
  import { GraphQLResolverMap } from '@apollo/subgraph/src/schema-helper';
5
- import { GraphQLRequestContext } from 'apollo-server-types';
6
5
  import { GraphQLDataSourceRequestKind } from '../types';
6
+ import { GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
7
7
 
8
8
  describe('constructing requests', () => {
9
9
  it('accepts context', async () => {
@@ -42,7 +42,7 @@ describe('constructing requests', () => {
42
42
  },
43
43
  incomingRequestContext: {
44
44
  context: { userId: 2 },
45
- } as GraphQLRequestContext<{userId: number}>,
45
+ } as GatewayGraphQLRequestContext<{userId: number}>,
46
46
  context: { userId: 2 },
47
47
  });
48
48
 
@@ -1,15 +1,10 @@
1
- import {
2
- ApolloError,
3
- AuthenticationError,
4
- ForbiddenError,
5
- } from 'apollo-server-errors';
6
-
7
1
  import { RemoteGraphQLDataSource } from '../RemoteGraphQLDataSource';
8
2
  import { Response, Headers } from 'node-fetch';
9
- import { GraphQLRequestContext } from 'apollo-server-types';
10
3
  import { GraphQLDataSourceRequestKind } from '../types';
11
4
  import { nockBeforeEach, nockAfterEach } from '../../__tests__/nockAssertions';
12
5
  import nock from 'nock';
6
+ import { GraphQLError } from 'graphql';
7
+ import { GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
13
8
 
14
9
  beforeEach(nockBeforeEach);
15
10
  afterEach(nockAfterEach);
@@ -322,7 +317,7 @@ describe('didReceiveResponse', () => {
322
317
  response,
323
318
  }: Required<
324
319
  Pick<
325
- GraphQLRequestContext<MyContext>,
320
+ GatewayGraphQLRequestContext<MyContext>,
326
321
  'request' | 'response' | 'context'
327
322
  >
328
323
  >) {
@@ -367,7 +362,7 @@ describe('didReceiveResponse', () => {
367
362
  response,
368
363
  }: Required<
369
364
  Pick<
370
- GraphQLRequestContext<MyContext>,
365
+ GatewayGraphQLRequestContext<MyContext>,
371
366
  'request' | 'response' | 'context'
372
367
  >
373
368
  >) {
@@ -403,7 +398,7 @@ describe('didReceiveResponse', () => {
403
398
  response,
404
399
  }: Required<
405
400
  Pick<
406
- GraphQLRequestContext<MyContext>,
401
+ GatewayGraphQLRequestContext<MyContext>,
407
402
  'request' | 'response' | 'context'
408
403
  >
409
404
  >) {
@@ -457,11 +452,11 @@ describe('didEncounterError', () => {
457
452
  },
458
453
  incomingRequestContext: {
459
454
  context,
460
- } as GraphQLRequestContext<MyContext>,
455
+ } as GatewayGraphQLRequestContext<MyContext>,
461
456
  context,
462
457
  });
463
458
 
464
- await expect(result).rejects.toThrow(AuthenticationError);
459
+ await expect(result).rejects.toThrow(GraphQLError);
465
460
  expect(context).toMatchObject({
466
461
  timingData: [{ time: 1616446845234 }],
467
462
  });
@@ -469,7 +464,7 @@ describe('didEncounterError', () => {
469
464
  });
470
465
 
471
466
  describe('error handling', () => {
472
- it('throws an AuthenticationError when the response status is 401', async () => {
467
+ it('throws error with code UNAUTHENTICATED when the response status is 401', async () => {
473
468
  const DataSource = new RemoteGraphQLDataSource({
474
469
  url: 'https://api.example.com/foo',
475
470
  });
@@ -480,7 +475,7 @@ describe('error handling', () => {
480
475
  ...defaultProcessOptions,
481
476
  request: { query: '{ me { name } }' },
482
477
  });
483
- await expect(result).rejects.toThrow(AuthenticationError);
478
+ await expect(result).rejects.toThrow(GraphQLError);
484
479
  await expect(result).rejects.toMatchObject({
485
480
  extensions: {
486
481
  code: 'UNAUTHENTICATED',
@@ -492,7 +487,7 @@ describe('error handling', () => {
492
487
  });
493
488
  });
494
489
 
495
- it('throws a ForbiddenError when the response status is 403', async () => {
490
+ it('throws an error with code FORBIDDEN when the response status is 403', async () => {
496
491
  const DataSource = new RemoteGraphQLDataSource({
497
492
  url: 'https://api.example.com/foo',
498
493
  });
@@ -503,7 +498,7 @@ describe('error handling', () => {
503
498
  ...defaultProcessOptions,
504
499
  request: { query: '{ me { name } }' },
505
500
  });
506
- await expect(result).rejects.toThrow(ForbiddenError);
501
+ await expect(result).rejects.toThrow(GraphQLError);
507
502
  await expect(result).rejects.toMatchObject({
508
503
  extensions: {
509
504
  code: 'FORBIDDEN',
@@ -515,7 +510,7 @@ describe('error handling', () => {
515
510
  });
516
511
  });
517
512
 
518
- it('throws an ApolloError when the response status is 500', async () => {
513
+ it('throws a GraphQLError when the response status is 500', async () => {
519
514
  const DataSource = new RemoteGraphQLDataSource({
520
515
  url: 'https://api.example.com/foo',
521
516
  });
@@ -526,7 +521,7 @@ describe('error handling', () => {
526
521
  ...defaultProcessOptions,
527
522
  request: { query: '{ me { name } }' },
528
523
  });
529
- await expect(result).rejects.toThrow(ApolloError);
524
+ await expect(result).rejects.toThrow(GraphQLError);
530
525
  await expect(result).rejects.toMatchObject({
531
526
  extensions: {
532
527
  response: {
@@ -560,7 +555,7 @@ describe('error handling', () => {
560
555
  ...defaultProcessOptions,
561
556
  request: { query: '{ me { name } }' },
562
557
  });
563
- await expect(result).rejects.toThrow(ApolloError);
558
+ await expect(result).rejects.toThrow(GraphQLError);
564
559
  await expect(result).rejects.toMatchObject({
565
560
  extensions: {
566
561
  response: {
@@ -1,11 +1,11 @@
1
- import { GraphQLResponse, GraphQLRequestContext } from 'apollo-server-types';
1
+ import { GatewayGraphQLResponse, GatewayGraphQLRequestContext } from '@apollo/server-gateway-interface';
2
2
 
3
3
  export interface GraphQLDataSource<
4
4
  TContext extends Record<string, any> = Record<string, any>,
5
5
  > {
6
6
  process(
7
7
  options: GraphQLDataSourceProcessOptions<TContext>,
8
- ): Promise<GraphQLResponse>;
8
+ ): Promise<GatewayGraphQLResponse>;
9
9
  }
10
10
 
11
11
  export enum GraphQLDataSourceRequestKind {
@@ -20,7 +20,7 @@ export type GraphQLDataSourceProcessOptions<
20
20
  /**
21
21
  * The request to send to the subgraph.
22
22
  */
23
- request: GraphQLRequestContext<TContext>['request'];
23
+ request: GatewayGraphQLRequestContext<TContext>['request'];
24
24
  } & (
25
25
  | {
26
26
  kind: GraphQLDataSourceRequestKind.INCOMING_OPERATION;
@@ -29,7 +29,7 @@ export type GraphQLDataSourceProcessOptions<
29
29
  * one of the strings if this operation is generated by the gateway without an
30
30
  * incoming request.
31
31
  */
32
- incomingRequestContext: GraphQLRequestContext<TContext>;
32
+ incomingRequestContext: GatewayGraphQLRequestContext<TContext>;
33
33
  /**
34
34
  * Equivalent to incomingRequestContext.context (provided here for
35
35
  * backwards compatibility): the object created by the Apollo Server
@@ -38,11 +38,11 @@ export type GraphQLDataSourceProcessOptions<
38
38
  * @deprecated Use `incomingRequestContext.context` instead (after
39
39
  * checking `kind`).
40
40
  */
41
- context: GraphQLRequestContext<TContext>['context'];
41
+ context: GatewayGraphQLRequestContext<TContext>['context'];
42
42
  /**
43
43
  * The document representation of the request's query being sent to the subgraph, if available.
44
44
  */
45
- document?: GraphQLRequestContext<TContext>['document'];
45
+ document?: GatewayGraphQLRequestContext<TContext>['document'];
46
46
  }
47
47
  | {
48
48
  kind:
@@ -1,7 +1,3 @@
1
- import {
2
- GraphQLExecutionResult,
3
- GraphQLRequestContext,
4
- } from 'apollo-server-types';
5
1
  import { Headers } from 'node-fetch';
6
2
  import {
7
3
  execute,
@@ -33,7 +29,8 @@ import { deepMerge } from './utilities/deepMerge';
33
29
  import { isNotNullOrUndefined } from './utilities/array';
34
30
  import { SpanStatusCode } from "@opentelemetry/api";
35
31
  import { OpenTelemetrySpanNames, tracer } from "./utilities/opentelemetry";
36
- import { defaultRootName, errorCodeDef, ERRORS } from '@apollo/federation-internals';
32
+ import { assert, defaultRootName, errorCodeDef, ERRORS } from '@apollo/federation-internals';
33
+ import { GatewayGraphQLRequestContext, GatewayExecutionResult } from '@apollo/server-gateway-interface';
37
34
 
38
35
  export type ServiceMap = {
39
36
  [serviceName: string]: GraphQLDataSource;
@@ -41,22 +38,22 @@ export type ServiceMap = {
41
38
 
42
39
  type ResultMap = Record<string, any>;
43
40
 
44
- interface ExecutionContext<TContext> {
41
+ interface ExecutionContext {
45
42
  queryPlan: QueryPlan;
46
43
  operationContext: OperationContext;
47
44
  serviceMap: ServiceMap;
48
- requestContext: GraphQLRequestContext<TContext>;
45
+ requestContext: GatewayGraphQLRequestContext;
49
46
  supergraphSchema: GraphQLSchema;
50
47
  errors: GraphQLError[];
51
48
  }
52
49
 
53
- export async function executeQueryPlan<TContext>(
50
+ export async function executeQueryPlan(
54
51
  queryPlan: QueryPlan,
55
52
  serviceMap: ServiceMap,
56
- requestContext: GraphQLRequestContext<TContext>,
53
+ requestContext: GatewayGraphQLRequestContext,
57
54
  operationContext: OperationContext,
58
55
  supergraphSchema: GraphQLSchema,
59
- ): Promise<GraphQLExecutionResult> {
56
+ ): Promise<GatewayExecutionResult> {
60
57
 
61
58
  const logger = requestContext.logger || console;
62
59
 
@@ -64,7 +61,7 @@ export async function executeQueryPlan<TContext>(
64
61
  try {
65
62
  const errors: GraphQLError[] = [];
66
63
 
67
- const context: ExecutionContext<TContext> = {
64
+ const context: ExecutionContext = {
68
65
  queryPlan,
69
66
  operationContext,
70
67
  serviceMap,
@@ -170,8 +167,8 @@ export async function executeQueryPlan<TContext>(
170
167
  // we're going to ignore it, because it makes the code much simpler and more
171
168
  // typesafe. However, it doesn't actually ask for traces from the backend
172
169
  // service unless we are capturing traces for Studio.
173
- async function executeNode<TContext>(
174
- context: ExecutionContext<TContext>,
170
+ async function executeNode(
171
+ context: ExecutionContext,
175
172
  node: PlanNode,
176
173
  results: ResultMap | ResultMap[],
177
174
  path: ResponsePath,
@@ -251,11 +248,17 @@ async function executeNode<TContext>(
251
248
  }
252
249
  return new Trace.QueryPlanNode({ fetch: traceNode });
253
250
  }
251
+ case 'Defer': {
252
+ assert(false, `@defer support is not available in the gateway`);
253
+ }
254
+ case 'Condition': {
255
+ assert(false, `Condition nodes are not available in the gateway`);
256
+ }
254
257
  }
255
258
  }
256
259
 
257
- async function executeFetch<TContext>(
258
- context: ExecutionContext<TContext>,
260
+ async function executeFetch(
261
+ context: ExecutionContext,
259
262
  fetch: FetchNode,
260
263
  results: ResultMap | (ResultMap | null | undefined)[],
261
264
  _path: ResponsePath,
@@ -379,7 +382,7 @@ async function executeFetch<TContext>(
379
382
  }
380
383
  });
381
384
  async function sendOperation(
382
- context: ExecutionContext<TContext>,
385
+ context: ExecutionContext,
383
386
  source: string,
384
387
  variables: Record<string, any>,
385
388
  operationName: string | undefined,
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<
@@ -196,14 +192,14 @@ export class ApolloGateway implements GraphQLService {
196
192
  }
197
193
 
198
194
  private initQueryPlanStore(approximateQueryPlanStoreMiB?: number) {
199
- return new InMemoryLRUCache<QueryPlan>({
195
+ return new LRUCache<string, QueryPlan>({
200
196
  // Create ~about~ a 30MiB InMemoryLRUCache. This is less than precise
201
197
  // since the technique to calculate the size of a DocumentNode is
202
198
  // only using JSON.stringify on the DocumentNode (and thus doesn't account
203
199
  // for unicode characters, etc.), but it should do a reasonable job at
204
200
  // providing a caching document store for most operations.
205
201
  maxSize: Math.pow(2, 20) * (approximateQueryPlanStoreMiB || 30),
206
- sizeCalculator: approximateObjectSize,
202
+ sizeCalculation: approximateObjectSize,
207
203
  });
208
204
  }
209
205
 
@@ -360,6 +356,7 @@ export class ApolloGateway implements GraphQLService {
360
356
  uplinkEndpoints:
361
357
  this.config.uplinkEndpoints ?? schemaDeliveryEndpoints,
362
358
  maxRetries: this.config.uplinkMaxRetries,
359
+ fetcher: this.config.fetcher,
363
360
  logger: this.logger,
364
361
  fallbackPollIntervalInMs: this.pollIntervalInMs,
365
362
  }),
@@ -558,12 +555,12 @@ export class ApolloGateway implements GraphQLService {
558
555
  // Once we remove the deprecated onSchemaChange() method, we can remove this.
559
556
  legacyDontNotifyOnSchemaChangeListeners: boolean = false,
560
557
  ): void {
561
- if (this.queryPlanStore) this.queryPlanStore.flush();
558
+ this.queryPlanStore.clear();
562
559
  this.apiSchema = coreSchema.toAPISchema();
563
560
  this.schema = addExtensions(
564
561
  wrapSchemaWithAliasResolver(this.apiSchema.toGraphQLJSSchema()),
565
562
  );
566
- this.queryPlanner = new QueryPlanner(coreSchema);
563
+ this.queryPlanner = new QueryPlanner(coreSchema, this.config.queryPlannerConfig);
567
564
 
568
565
  // Notify onSchemaChange listeners of the updated schema
569
566
  if (!legacyDontNotifyOnSchemaChangeListeners) {
@@ -650,7 +647,7 @@ export class ApolloGateway implements GraphQLService {
650
647
  */
651
648
  public onSchemaChange(
652
649
  callback: (schema: GraphQLSchema) => void,
653
- ): Unsubscriber {
650
+ ): GatewayUnsubscriber {
654
651
  this.onSchemaChangeListeners.add(callback);
655
652
 
656
653
  return () => {
@@ -663,7 +660,7 @@ export class ApolloGateway implements GraphQLService {
663
660
  apiSchema: GraphQLSchema;
664
661
  coreSupergraphSdl: string;
665
662
  }) => void,
666
- ): Unsubscriber {
663
+ ): GatewayUnsubscriber {
667
664
  this.onSchemaLoadOrUpdateListeners.add(callback);
668
665
 
669
666
  return () => {
@@ -743,9 +740,9 @@ export class ApolloGateway implements GraphQLService {
743
740
  // ApolloServerPluginUsageReporting) assumes that. In fact, errors talking to backends
744
741
  // are unlikely to show up as GraphQLErrors. Do we need to use
745
742
  // formatApolloErrors or something?
746
- public executor = async <TContext>(
747
- requestContext: GraphQLRequestContextExecutionDidStart<TContext>,
748
- ): Promise<GraphQLExecutionResult> => {
743
+ public executor = async (
744
+ requestContext: GatewayGraphQLRequestContext,
745
+ ): Promise<GatewayExecutionResult> => {
749
746
  const spanAttributes = requestContext.operationName
750
747
  ? { operationName: requestContext.operationName }
751
748
  : {};
@@ -774,10 +771,7 @@ export class ApolloGateway implements GraphQLService {
774
771
  span.setStatus({ code: SpanStatusCode.ERROR });
775
772
  return { errors: validationErrors };
776
773
  }
777
- let queryPlan: QueryPlan | undefined;
778
- if (this.queryPlanStore) {
779
- queryPlan = await this.queryPlanStore.get(queryPlanStoreKey);
780
- }
774
+ let queryPlan = this.queryPlanStore.get(queryPlanStoreKey);
781
775
 
782
776
  if (!queryPlan) {
783
777
  queryPlan = tracer.startActiveSpan(
@@ -800,25 +794,11 @@ export class ApolloGateway implements GraphQLService {
800
794
  },
801
795
  );
802
796
 
803
- if (this.queryPlanStore) {
804
- // The underlying cache store behind the `documentStore` returns a
805
- // `Promise` which is resolved (or rejected), eventually, based on the
806
- // success or failure (respectively) of the cache save attempt. While
807
- // it's certainly possible to `await` this `Promise`, we don't care about
808
- // whether or not it's successful at this point. We'll instead proceed
809
- // to serve the rest of the request and just hope that this works out.
810
- // If it doesn't work, the next request will have another opportunity to
811
- // try again. Errors will surface as warnings, as appropriate.
812
- //
813
- // While it shouldn't normally be necessary to wrap this `Promise` in a
814
- // `Promise.resolve` invocation, it seems that the underlying cache store
815
- // is returning a non-native `Promise` (e.g. Bluebird, etc.).
816
- Promise.resolve(
817
- this.queryPlanStore.set(queryPlanStoreKey, queryPlan),
818
- ).catch((err) =>
819
- this.logger.warn(
820
- 'Could not store queryPlan' + ((err && err.message) || err),
821
- ),
797
+ try {
798
+ this.queryPlanStore.set(queryPlanStoreKey, queryPlan);
799
+ } catch (err) {
800
+ this.logger.warn(
801
+ 'Could not store queryPlan' + ((err && err.message) || err),
822
802
  );
823
803
  }
824
804
  }
@@ -840,7 +820,7 @@ export class ApolloGateway implements GraphQLService {
840
820
  });
841
821
  }
842
822
 
843
- const response = await executeQueryPlan<TContext>(
823
+ const response = await executeQueryPlan(
844
824
  queryPlan,
845
825
  serviceMap,
846
826
  requestContext,
@@ -898,8 +878,8 @@ export class ApolloGateway implements GraphQLService {
898
878
  );
899
879
  };
900
880
 
901
- private validateIncomingRequest<TContext>(
902
- requestContext: GraphQLRequestContextExecutionDidStart<TContext>,
881
+ private validateIncomingRequest(
882
+ requestContext: GatewayGraphQLRequestContext,
903
883
  operationContext: OperationContext,
904
884
  ) {
905
885
  return tracer.startActiveSpan(OpenTelemetrySpanNames.VALIDATE, (span) => {
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,10 +1,10 @@
1
- import { GraphQLRequest } from 'apollo-server-types';
2
1
  import { parse } from 'graphql';
3
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,