@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
@@ -2,11 +2,8 @@ import {
2
2
  GraphQLSchemaValidationError,
3
3
  GraphQLSchemaModule,
4
4
  GraphQLResolverMap,
5
- } from 'apollo-graphql';
5
+ } from '../schema-helper';
6
6
  import { GraphQLRequest, GraphQLExecutionResult, Logger } from 'apollo-server-types';
7
- import {
8
- ServiceDefinition,
9
- } from '@apollo/federation';
10
7
  import { buildSubgraphSchema } from '@apollo/subgraph';
11
8
  import {
12
9
  executeQueryPlan,
@@ -20,7 +17,7 @@ import { queryPlanSerializer, astSerializer } from 'apollo-federation-integratio
20
17
  import gql from 'graphql-tag';
21
18
  import { fixtures } from 'apollo-federation-integration-testsuite';
22
19
  import { composeServices } from '@apollo/composition';
23
- import { buildSchema, operationFromDocument } from '@apollo/federation-internals';
20
+ import { buildSchema, operationFromDocument, ServiceDefinition } from '@apollo/federation-internals';
24
21
 
25
22
  const prettyFormat = require('pretty-format');
26
23
 
@@ -67,6 +64,7 @@ export async function execute(
67
64
  const result = await executeQueryPlan(
68
65
  queryPlan,
69
66
  serviceMap,
67
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
70
68
  // @ts-ignore
71
69
  {
72
70
  cache: undefined as any,
@@ -111,8 +109,8 @@ export function getTestingSupergraphSdl(services: typeof fixtures = fixtures) {
111
109
  throw new Error(`Testing fixtures don't compose properly!\nCauses:\n${compositionResult.errors.join('\n\n')}`);
112
110
  }
113
111
 
114
- export function wait(ms: number) {
115
- return new Promise(r => setTimeout(r, ms));
112
+ export function wait(ms: number, toResolveTo?: any) {
113
+ return new Promise((r) => setTimeout(() => r(toResolveTo), ms));
116
114
  }
117
115
 
118
116
  export function printPlan(queryPlan: QueryPlan): string {
@@ -1,4 +1,4 @@
1
- import { fetch } from '../../__mocks__/apollo-server-env';
1
+ import { fetch } from '../../__mocks__/make-fetch-happen-fetcher';
2
2
  import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
3
3
 
4
4
  import { RemoteGraphQLDataSource } from '../../datasources/RemoteGraphQLDataSource';
@@ -218,7 +218,7 @@ it('does not share service definition cache between gateways', async () => {
218
218
  url: 'https://api.example.com/repeat',
219
219
  },
220
220
  ],
221
- experimental_didUpdateComposition: updateObserver,
221
+ experimental_didUpdateSupergraph: updateObserver,
222
222
  });
223
223
 
224
224
  await gateway.load();
@@ -237,7 +237,7 @@ it('does not share service definition cache between gateways', async () => {
237
237
  url: 'https://api.example.com/repeat',
238
238
  },
239
239
  ],
240
- experimental_didUpdateComposition: updateObserver,
240
+ experimental_didUpdateSupergraph: updateObserver,
241
241
  });
242
242
 
243
243
  await gateway.load();
@@ -1,10 +1,10 @@
1
- import { GraphQLSchemaModule } from 'apollo-graphql';
2
1
  import { buildSubgraphSchema } from '@apollo/subgraph';
3
2
  import { ApolloServer } from 'apollo-server';
4
3
  import fetch from 'node-fetch';
5
4
  import { ApolloGateway } from '../..';
6
5
  import { fixtures } from 'apollo-federation-integration-testsuite';
7
6
  import { ApolloServerPluginInlineTrace } from 'apollo-server-core';
7
+ import { GraphQLSchemaModule } from '../../schema-helper';
8
8
 
9
9
  async function startFederatedServer(modules: GraphQLSchemaModule[]) {
10
10
  const schema = buildSubgraphSchema(modules);
@@ -1,8 +1,8 @@
1
+ import { fetch } from '../../__mocks__/make-fetch-happen-fetcher';
1
2
  import gql from 'graphql-tag';
2
3
  import { ApolloGateway } from '../../';
3
4
  import { fixtures } from 'apollo-federation-integration-testsuite';
4
5
  import { Logger } from 'apollo-server-types';
5
- import { fetch } from '../../__mocks__/apollo-server-env';
6
6
 
7
7
  let logger: {
8
8
  warn: jest.MockedFunction<Logger['warn']>,
@@ -38,6 +38,7 @@ describe('ApolloGateway executor', () => {
38
38
  }
39
39
  `;
40
40
 
41
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
41
42
  // @ts-ignore
42
43
  const { errors } = await executor({
43
44
  source,
@@ -73,6 +74,7 @@ describe('ApolloGateway executor', () => {
73
74
  }
74
75
  `;
75
76
 
77
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
76
78
  // @ts-ignore
77
79
  const { errors, data } = await executor({
78
80
  source,
@@ -1,8 +1,10 @@
1
1
  import gql from 'graphql-tag';
2
2
  import { ApolloGateway } from '../..';
3
3
  import {
4
+ DynamicGatewayConfig,
4
5
  Experimental_DidResolveQueryPlanCallback,
5
6
  Experimental_UpdateServiceDefinitions,
7
+ ServiceDefinitionUpdate,
6
8
  } from '../../config';
7
9
  import {
8
10
  product,
@@ -11,8 +13,11 @@ import {
11
13
  accounts,
12
14
  books,
13
15
  documents,
16
+ fixtures,
17
+ fixturesWithUpdate,
14
18
  } from 'apollo-federation-integration-testsuite';
15
19
  import { Logger } from 'apollo-server-types';
20
+ import resolvable from '@josephg/resolvable';
16
21
 
17
22
  // The order of this was specified to preserve existing test coverage. Typically
18
23
  // we would just import and use the `fixtures` array.
@@ -47,16 +52,14 @@ beforeEach(() => {
47
52
 
48
53
  describe('lifecycle hooks', () => {
49
54
  it('uses updateServiceDefinitions override', async () => {
50
- const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions = jest.fn(
51
- async () => {
55
+ const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
56
+ jest.fn(async () => {
52
57
  return { serviceDefinitions, isNewSchema: true };
53
- },
54
- );
58
+ });
55
59
 
56
60
  const gateway = new ApolloGateway({
57
61
  serviceList: serviceDefinitions,
58
62
  experimental_updateServiceDefinitions,
59
- experimental_didUpdateComposition: jest.fn(),
60
63
  logger,
61
64
  });
62
65
 
@@ -67,61 +70,7 @@ describe('lifecycle hooks', () => {
67
70
  await gateway.stop();
68
71
  });
69
72
 
70
- it('calls experimental_didFailComposition with a bad config', async () => {
71
- const experimental_didFailComposition = jest.fn();
72
-
73
- // Creating 2 subservices that clearly cannot composed.
74
- const s1 = {
75
- name: 'S1',
76
- url: 'http://S1',
77
- typeDefs: gql`
78
- type T {
79
- a: Int
80
- }
81
- `
82
- };
83
-
84
- const s2 = {
85
- name: 'S2',
86
- url: 'http://S2',
87
- typeDefs: gql`
88
- type T {
89
- a: String
90
- }
91
- `
92
- };
93
-
94
-
95
- const gateway = new ApolloGateway({
96
- async experimental_updateServiceDefinitions() {
97
- return {
98
- serviceDefinitions: [s1, s2],
99
- compositionMetadata: {
100
- formatVersion: 1,
101
- id: 'abc',
102
- implementingServiceLocations: [],
103
- schemaHash: 'abc',
104
- },
105
- isNewSchema: true,
106
- };
107
- },
108
- serviceList: [],
109
- experimental_didFailComposition,
110
- logger,
111
- });
112
-
113
- await expect(gateway.load()).rejects.toThrowError("A valid schema couldn't be composed");
114
-
115
- const callbackArgs = experimental_didFailComposition.mock.calls[0][0];
116
- expect(callbackArgs.serviceList).toHaveLength(2);
117
- expect(callbackArgs.errors[0]).toMatchInlineSnapshot(
118
- `[GraphQLError: Field "T.a" has incompatible types accross subgraphs: it has type "Int" in subgraph "S1" but type "String" in subgraph "S2"]`,
119
- );
120
- expect(callbackArgs.compositionMetadata.id).toEqual('abc');
121
- expect(experimental_didFailComposition).toBeCalled();
122
- });
123
-
124
- it('calls experimental_didUpdateComposition on schema update', async () => {
73
+ it('calls experimental_didUpdateSupergraph on schema update', async () => {
125
74
  const compositionMetadata = {
126
75
  formatVersion: 1,
127
76
  id: 'abc',
@@ -129,81 +78,73 @@ describe('lifecycle hooks', () => {
129
78
  schemaHash: 'hash1',
130
79
  };
131
80
 
132
- const update: Experimental_UpdateServiceDefinitions = async () => ({
133
- serviceDefinitions,
134
- isNewSchema: true,
135
- compositionMetadata: {
136
- ...compositionMetadata,
137
- id: '123',
138
- schemaHash: 'hash2',
139
- },
140
- });
141
-
142
- // This is the simplest way I could find to achieve mocked functions that leverage our types
143
- const mockUpdate = jest.fn(update);
144
-
145
- // We want to return a different composition across two ticks, so we mock it
146
- // slightly differenty
147
- mockUpdate.mockImplementationOnce(async () => {
148
- const services = serviceDefinitions.filter(s => s.name !== 'books');
149
- return {
150
- serviceDefinitions: [
151
- ...services,
152
- {
153
- name: 'book',
154
- typeDefs: books.typeDefs,
155
- url: 'http://localhost:32542',
81
+ const mockUpdate = jest
82
+ .fn<Promise<ServiceDefinitionUpdate>, [config: DynamicGatewayConfig]>()
83
+ .mockImplementationOnce(async () => {
84
+ return {
85
+ serviceDefinitions: fixtures,
86
+ isNewSchema: true,
87
+ compositionMetadata: {
88
+ ...compositionMetadata,
89
+ id: '123',
90
+ schemaHash: 'hash2',
156
91
  },
157
- ],
158
- isNewSchema: true,
159
- compositionMetadata,
160
- };
161
- });
92
+ };
93
+ })
94
+ // We want to return a different composition across two ticks, so we mock it
95
+ // slightly differently
96
+ .mockImplementationOnce(async () => {
97
+ return {
98
+ serviceDefinitions: fixturesWithUpdate,
99
+ isNewSchema: true,
100
+ compositionMetadata,
101
+ };
102
+ });
162
103
 
163
104
  const mockDidUpdate = jest.fn();
164
105
 
165
106
  const gateway = new ApolloGateway({
166
107
  experimental_updateServiceDefinitions: mockUpdate,
167
- experimental_didUpdateComposition: mockDidUpdate,
108
+ experimental_didUpdateSupergraph: mockDidUpdate,
168
109
  logger,
169
110
  });
170
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
171
- gateway.experimental_pollInterval = 100;
111
+ // for testing purposes, a short pollInterval is ideal so we'll override here
112
+ gateway['pollIntervalInMs'] = 100;
172
113
 
173
- let resolve1: Function;
174
- let resolve2: Function;
175
- const schemaChangeBlocker1 = new Promise(res => (resolve1 = res));
176
- const schemaChangeBlocker2 = new Promise(res => (resolve2 = res));
114
+ const schemaChangeBlocker1 = resolvable();
115
+ const schemaChangeBlocker2 = resolvable();
177
116
 
178
- gateway.onSchemaChange(
117
+ gateway.onSchemaLoadOrUpdate(
179
118
  jest
180
119
  .fn()
181
- .mockImplementationOnce(() => resolve1())
182
- .mockImplementationOnce(() => resolve2()),
120
+ .mockImplementationOnce(() => schemaChangeBlocker1.resolve())
121
+ .mockImplementationOnce(() => schemaChangeBlocker2.resolve()),
183
122
  );
184
123
 
185
124
  await gateway.load();
186
125
 
187
126
  await schemaChangeBlocker1;
127
+
188
128
  expect(mockUpdate).toBeCalledTimes(1);
189
129
  expect(mockDidUpdate).toBeCalledTimes(1);
190
130
 
191
131
  await schemaChangeBlocker2;
132
+
192
133
  expect(mockUpdate).toBeCalledTimes(2);
193
134
  expect(mockDidUpdate).toBeCalledTimes(2);
194
135
 
195
136
  const [firstCall, secondCall] = mockDidUpdate.mock.calls;
196
137
 
197
- expect(firstCall[0]!.schema).toBeDefined();
198
- expect(firstCall[0].compositionMetadata!.schemaHash).toEqual('hash1');
138
+ const expectedFirstId = 'd20cfb7a9c51179aa494ed9e98153f0042892bd225437af064bf1c1aa68eab86'
139
+ expect(firstCall[0]!.compositionId).toEqual(expectedFirstId);
199
140
  // first call should have no second "previous" argument
200
141
  expect(firstCall[1]).toBeUndefined();
201
142
 
202
- expect(secondCall[0].schema).toBeDefined();
203
- expect(secondCall[0].compositionMetadata!.schemaHash).toEqual('hash2');
143
+ expect(secondCall[0]!.compositionId).toEqual(
144
+ '7dad7ab8284165a86241b8973d71f0d6ac8cb142095c717dd23443522850c225',
145
+ );
204
146
  // second call should have previous info in the second arg
205
- expect(secondCall[1]!.schema).toBeDefined();
206
- expect(secondCall[1]!.compositionMetadata!.schemaHash).toEqual('hash1');
147
+ expect(secondCall[1]!.compositionId).toEqual(expectedFirstId);
207
148
 
208
149
  await gateway.stop();
209
150
  });
@@ -228,34 +169,31 @@ describe('lifecycle hooks', () => {
228
169
  it('warns when polling on the default fetcher', async () => {
229
170
  new ApolloGateway({
230
171
  serviceList: serviceDefinitions,
231
- experimental_pollInterval: 10,
172
+ pollIntervalInMs: 10,
232
173
  logger,
233
174
  });
234
- expect(logger.warn).toHaveBeenCalledTimes(1);
235
175
  expect(logger.warn).toHaveBeenCalledWith(
236
176
  'Polling running services is dangerous and not recommended in production. Polling should only be used against a registry. If you are polling running services, use with caution.',
237
177
  );
238
178
  });
239
179
 
240
180
  it('registers schema change callbacks when experimental_pollInterval is set for unmanaged configs', async () => {
241
- const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions = jest.fn(
242
- async (_config) => {
181
+ const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
182
+ jest.fn(async (_config) => {
243
183
  return { serviceDefinitions, isNewSchema: true };
244
- },
245
- );
184
+ });
246
185
 
247
186
  const gateway = new ApolloGateway({
248
187
  serviceList: [{ name: 'book', url: 'http://localhost:32542' }],
249
188
  experimental_updateServiceDefinitions,
250
- experimental_pollInterval: 100,
189
+ pollIntervalInMs: 100,
251
190
  logger,
252
191
  });
253
192
 
254
- let resolve: Function;
255
- const schemaChangeBlocker = new Promise(res => (resolve = res));
256
- const schemaChangeCallback = jest.fn(() => resolve());
193
+ const schemaChangeBlocker = resolvable();
194
+ const schemaChangeCallback = jest.fn(() => schemaChangeBlocker.resolve());
257
195
 
258
- gateway.onSchemaChange(schemaChangeCallback);
196
+ gateway.onSchemaLoadOrUpdate(schemaChangeCallback);
259
197
  await gateway.load();
260
198
 
261
199
  await schemaChangeBlocker;
@@ -265,12 +203,11 @@ describe('lifecycle hooks', () => {
265
203
  });
266
204
 
267
205
  it('calls experimental_didResolveQueryPlan when executor is called', async () => {
268
- const experimental_didResolveQueryPlan: Experimental_DidResolveQueryPlanCallback = jest.fn()
206
+ const experimental_didResolveQueryPlan: Experimental_DidResolveQueryPlanCallback =
207
+ jest.fn();
269
208
 
270
209
  const gateway = new ApolloGateway({
271
- localServiceList: [
272
- books
273
- ],
210
+ localServiceList: [books],
274
211
  experimental_didResolveQueryPlan,
275
212
  });
276
213
 
@@ -280,7 +217,8 @@ describe('lifecycle hooks', () => {
280
217
  { book(isbn: "0262510871") { year } }
281
218
  `;
282
219
 
283
- // @ts-ignore
220
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
221
+ // @ts-ignore
284
222
  await executor({
285
223
  source,
286
224
  document: gql(source),
@@ -36,11 +36,16 @@ describe('opentelemetry', () => {
36
36
  describe('with local data', () =>
37
37
  {
38
38
  async function gateway() {
39
+ const localDataSources = Object.fromEntries(
40
+ fixtures.map((f) => [
41
+ f.name,
42
+ new LocalGraphQLDataSource(buildSubgraphSchema(f)),
43
+ ]),
44
+ );
39
45
  const gateway = new ApolloGateway({
40
46
  localServiceList: fixtures,
41
- buildService: service => {
42
- // @ts-ignore
43
- return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
47
+ buildService(service) {
48
+ return localDataSources[service.name];
44
49
  },
45
50
  });
46
51
 
@@ -6,14 +6,20 @@ import { LocalGraphQLDataSource } from '../../datasources/LocalGraphQLDataSource
6
6
  import { ApolloGateway } from '../../';
7
7
  import { fixtures } from 'apollo-federation-integration-testsuite';
8
8
  import { QueryPlanner } from '@apollo/query-planner';
9
+
9
10
  it('caches the query plan for a request', async () => {
10
11
  const buildQueryPlanSpy = jest.spyOn(QueryPlanner.prototype, 'buildQueryPlan');
11
12
 
13
+ const localDataSources = Object.fromEntries(
14
+ fixtures.map((f) => [
15
+ f.name,
16
+ new LocalGraphQLDataSource(buildSubgraphSchema(f)),
17
+ ]),
18
+ );
12
19
  const gateway = new ApolloGateway({
13
20
  localServiceList: fixtures,
14
- buildService: service => {
15
- // @ts-ignore
16
- return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
21
+ buildService(service) {
22
+ return localDataSources[service.name];
17
23
  },
18
24
  });
19
25
 
@@ -65,11 +71,17 @@ it('supports multiple operations and operationName', async () => {
65
71
  }
66
72
  `;
67
73
 
74
+ const localDataSources = Object.fromEntries(
75
+ fixtures.map((f) => [
76
+ f.name,
77
+ new LocalGraphQLDataSource(buildSubgraphSchema(f)),
78
+ ]),
79
+ );
80
+
68
81
  const gateway = new ApolloGateway({
69
82
  localServiceList: fixtures,
70
- buildService: service => {
71
- // @ts-ignore
72
- return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
83
+ buildService(service) {
84
+ return localDataSources[service.name];
73
85
  },
74
86
  });
75
87
 
@@ -170,11 +182,15 @@ it('does not corrupt cached queryplan data across requests', async () => {
170
182
  },
171
183
  };
172
184
 
185
+ const dataSources: Record<string, LocalGraphQLDataSource> = {
186
+ a: new LocalGraphQLDataSource(buildSubgraphSchema(serviceA)),
187
+ b: new LocalGraphQLDataSource(buildSubgraphSchema(serviceB)),
188
+ };
189
+
173
190
  const gateway = new ApolloGateway({
174
191
  localServiceList: [serviceA, serviceB],
175
- buildService: service => {
176
- // @ts-ignore
177
- return new LocalGraphQLDataSource(buildSubgraphSchema([service]));
192
+ buildService(service) {
193
+ return dataSources[service.name];
178
194
  },
179
195
  });
180
196
 
@@ -1,17 +1,20 @@
1
1
  import { gunzipSync } from 'zlib';
2
2
  import nock from 'nock';
3
- import { GraphQLSchemaModule } from 'apollo-graphql';
4
3
  import gql from 'graphql-tag';
5
4
  import { buildSubgraphSchema } from '@apollo/subgraph';
6
5
  import { ApolloServer } from 'apollo-server';
7
6
  import { ApolloServerPluginUsageReporting } from 'apollo-server-core';
8
- import { execute, toPromise } from 'apollo-link';
9
- import { createHttpLink } from 'apollo-link-http';
7
+ import { execute } from '@apollo/client/link/core';
8
+ import { toPromise } from '@apollo/client/link/utils';
9
+ import { createHttpLink } from '@apollo/client/link/http';
10
10
  import fetch from 'node-fetch';
11
11
  import { ApolloGateway } from '../..';
12
12
  import { Plugin, Config, Refs } from 'pretty-format';
13
13
  import { Report, Trace } from 'apollo-reporting-protobuf';
14
14
  import { fixtures } from 'apollo-federation-integration-testsuite';
15
+ import { nockAfterEach, nockBeforeEach } from '../nockAssertions';
16
+ import { GraphQLSchemaModule } from '../../schema-helper';
17
+ import resolvable, { Resolvable } from '@josephg/resolvable';
15
18
 
16
19
  // Normalize specific fields that change often (eg timestamps) to static values,
17
20
  // to make snapshot testing viable. (If these helpers are more generally
@@ -88,19 +91,16 @@ describe('reporting', () => {
88
91
  let backendServers: ApolloServer[];
89
92
  let gatewayServer: ApolloServer;
90
93
  let gatewayUrl: string;
91
- let reportPromise: Promise<any>;
92
- let nockScope: nock.Scope;
94
+ let reportPromise: Resolvable<any>;
93
95
 
94
96
  beforeEach(async () => {
95
- let reportResolver: (report: any) => void;
96
- reportPromise = new Promise<any>((resolve) => {
97
- reportResolver = resolve;
98
- });
97
+ reportPromise = resolvable();
99
98
 
100
- nockScope = nock('https://usage-reporting.api.apollographql.com')
99
+ nockBeforeEach();
100
+ nock('https://usage-reporting.api.apollographql.com')
101
101
  .post('/api/ingress/traces')
102
102
  .reply(200, (_: any, requestBody: string) => {
103
- reportResolver(requestBody);
103
+ reportPromise.resolve(requestBody);
104
104
  return 'ok';
105
105
  });
106
106
 
@@ -137,7 +137,8 @@ describe('reporting', () => {
137
137
  if (gatewayServer) {
138
138
  await gatewayServer.stop();
139
139
  }
140
- nockScope.done();
140
+
141
+ nockAfterEach();
141
142
  });
142
143
 
143
144
  it(`queries three services`, async () => {
@@ -231,6 +232,34 @@ describe('reporting', () => {
231
232
  "tracesPerQuery": Object {
232
233
  "# -
233
234
  {me{name{first last}}topProducts{name}}": Object {
235
+ "referencedFieldsByType": Object {
236
+ "Name": Object {
237
+ "fieldNames": Array [
238
+ "first",
239
+ "last",
240
+ ],
241
+ "isInterface": false,
242
+ },
243
+ "Product": Object {
244
+ "fieldNames": Array [
245
+ "name",
246
+ ],
247
+ "isInterface": true,
248
+ },
249
+ "Query": Object {
250
+ "fieldNames": Array [
251
+ "me",
252
+ "topProducts",
253
+ ],
254
+ "isInterface": false,
255
+ },
256
+ "User": Object {
257
+ "fieldNames": Array [
258
+ "name",
259
+ ],
260
+ "isInterface": false,
261
+ },
262
+ },
234
263
  "trace": Array [
235
264
  Object {
236
265
  "cachePolicy": Object {
@@ -238,7 +267,6 @@ describe('reporting', () => {
238
267
  "scope": "PRIVATE",
239
268
  },
240
269
  "clientName": "",
241
- "clientReferenceId": "",
242
270
  "clientVersion": "",
243
271
  "details": Object {},
244
272
  "durationNs": 12345,
@@ -246,6 +274,7 @@ describe('reporting', () => {
246
274
  "nanos": 123000000,
247
275
  "seconds": "1562203363",
248
276
  },
277
+ "fieldExecutionWeight": 1,
249
278
  "forbiddenOperation": false,
250
279
  "fullQueryCacheHit": false,
251
280
  "http": Object {