@apollo/gateway 0.45.1 → 0.48.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 (124) hide show
  1. package/dist/config.d.ts +42 -16
  2. package/dist/config.d.ts.map +1 -1
  3. package/dist/config.js +28 -18
  4. package/dist/config.js.map +1 -1
  5. package/dist/datasources/LocalGraphQLDataSource.js.map +1 -1
  6. package/dist/datasources/RemoteGraphQLDataSource.d.ts.map +1 -1
  7. package/dist/datasources/RemoteGraphQLDataSource.js +4 -1
  8. package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
  9. package/dist/executeQueryPlan.d.ts.map +1 -1
  10. package/dist/executeQueryPlan.js +1 -1
  11. package/dist/executeQueryPlan.js.map +1 -1
  12. package/dist/index.d.ts +35 -23
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +205 -308
  15. package/dist/index.js.map +1 -1
  16. package/dist/schema-helper/addResolversToSchema.d.ts +4 -0
  17. package/dist/schema-helper/addResolversToSchema.d.ts.map +1 -0
  18. package/dist/schema-helper/addResolversToSchema.js +62 -0
  19. package/dist/schema-helper/addResolversToSchema.js.map +1 -0
  20. package/dist/schema-helper/error.d.ts +6 -0
  21. package/dist/schema-helper/error.d.ts.map +1 -0
  22. package/dist/schema-helper/error.js +14 -0
  23. package/dist/schema-helper/error.js.map +1 -0
  24. package/dist/schema-helper/index.d.ts +4 -0
  25. package/dist/schema-helper/index.d.ts.map +1 -0
  26. package/dist/schema-helper/index.js +16 -0
  27. package/dist/schema-helper/index.js.map +1 -0
  28. package/dist/schema-helper/resolverMap.d.ts +16 -0
  29. package/dist/schema-helper/resolverMap.d.ts.map +1 -0
  30. package/dist/schema-helper/resolverMap.js +3 -0
  31. package/dist/schema-helper/resolverMap.js.map +1 -0
  32. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +31 -0
  33. package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -0
  34. package/dist/supergraphManagers/IntrospectAndCompose/index.js +112 -0
  35. package/dist/supergraphManagers/IntrospectAndCompose/index.js.map +1 -0
  36. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts +12 -0
  37. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.d.ts.map +1 -0
  38. package/dist/{loadServicesFromRemoteEndpoint.js → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js} +6 -6
  39. package/dist/supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.js.map +1 -0
  40. package/dist/supergraphManagers/LegacyFetcher/index.d.ts +33 -0
  41. package/dist/supergraphManagers/LegacyFetcher/index.d.ts.map +1 -0
  42. package/dist/supergraphManagers/LegacyFetcher/index.js +149 -0
  43. package/dist/supergraphManagers/LegacyFetcher/index.js.map +1 -0
  44. package/dist/supergraphManagers/LocalCompose/index.d.ts +19 -0
  45. package/dist/supergraphManagers/LocalCompose/index.d.ts.map +1 -0
  46. package/dist/supergraphManagers/LocalCompose/index.js +55 -0
  47. package/dist/supergraphManagers/LocalCompose/index.js.map +1 -0
  48. package/dist/supergraphManagers/UplinkFetcher/index.d.ts +32 -0
  49. package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -0
  50. package/dist/supergraphManagers/UplinkFetcher/index.js +96 -0
  51. package/dist/supergraphManagers/UplinkFetcher/index.js.map +1 -0
  52. package/dist/{loadSupergraphSdlFromStorage.d.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts} +1 -1
  53. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.d.ts.map +1 -0
  54. package/dist/{loadSupergraphSdlFromStorage.js → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js} +1 -1
  55. package/dist/supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.js.map +1 -0
  56. package/dist/{outOfBandReporter.d.ts → supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts} +0 -0
  57. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.d.ts.map +1 -0
  58. package/dist/{outOfBandReporter.js → supergraphManagers/UplinkFetcher/outOfBandReporter.js} +2 -2
  59. package/dist/supergraphManagers/UplinkFetcher/outOfBandReporter.js.map +1 -0
  60. package/dist/supergraphManagers/index.d.ts +5 -0
  61. package/dist/supergraphManagers/index.d.ts.map +1 -0
  62. package/dist/supergraphManagers/index.js +12 -0
  63. package/dist/supergraphManagers/index.js.map +1 -0
  64. package/dist/utilities/createHash.d.ts +2 -0
  65. package/dist/utilities/createHash.d.ts.map +1 -0
  66. package/dist/utilities/createHash.js +15 -0
  67. package/dist/utilities/createHash.js.map +1 -0
  68. package/dist/utilities/isNodeLike.d.ts +3 -0
  69. package/dist/utilities/isNodeLike.d.ts.map +1 -0
  70. package/dist/utilities/isNodeLike.js +8 -0
  71. package/dist/utilities/isNodeLike.js.map +1 -0
  72. package/package.json +12 -10
  73. package/src/__mocks__/make-fetch-happen-fetcher.ts +3 -1
  74. package/src/__tests__/executeQueryPlan.test.ts +1 -1
  75. package/src/__tests__/execution-utils.ts +3 -3
  76. package/src/__tests__/gateway/buildService.test.ts +3 -3
  77. package/src/__tests__/gateway/endToEnd.test.ts +1 -1
  78. package/src/__tests__/gateway/executor.test.ts +1 -1
  79. package/src/__tests__/gateway/lifecycle-hooks.test.ts +58 -99
  80. package/src/__tests__/gateway/opentelemetry.test.ts +8 -3
  81. package/src/__tests__/gateway/queryPlanCache.test.ts +25 -9
  82. package/src/__tests__/gateway/reporting.test.ts +44 -19
  83. package/src/__tests__/gateway/supergraphSdl.test.ts +394 -0
  84. package/src/__tests__/integration/aliases.test.ts +9 -3
  85. package/src/__tests__/integration/configuration.test.ts +109 -12
  86. package/src/__tests__/integration/logger.test.ts +1 -1
  87. package/src/__tests__/integration/networkRequests.test.ts +81 -118
  88. package/src/__tests__/integration/nockMocks.ts +15 -8
  89. package/src/config.ts +149 -40
  90. package/src/datasources/LocalGraphQLDataSource.ts +1 -1
  91. package/src/datasources/RemoteGraphQLDataSource.ts +8 -2
  92. package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
  93. package/src/datasources/__tests__/RemoteGraphQLDataSource.test.ts +4 -4
  94. package/src/executeQueryPlan.ts +3 -1
  95. package/src/index.ts +314 -485
  96. package/src/schema-helper/addResolversToSchema.ts +83 -0
  97. package/src/schema-helper/error.ts +11 -0
  98. package/src/schema-helper/index.ts +3 -0
  99. package/src/schema-helper/resolverMap.ts +23 -0
  100. package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +370 -0
  101. package/src/{__tests__ → supergraphManagers/IntrospectAndCompose/__tests__}/loadServicesFromRemoteEndpoint.test.ts +5 -5
  102. package/src/supergraphManagers/IntrospectAndCompose/__tests__/tsconfig.json +8 -0
  103. package/src/supergraphManagers/IntrospectAndCompose/index.ts +163 -0
  104. package/src/{loadServicesFromRemoteEndpoint.ts → supergraphManagers/IntrospectAndCompose/loadServicesFromRemoteEndpoint.ts} +6 -6
  105. package/src/supergraphManagers/LegacyFetcher/index.ts +229 -0
  106. package/src/supergraphManagers/LocalCompose/index.ts +83 -0
  107. package/src/{__tests__ → supergraphManagers/UplinkFetcher/__tests__}/loadSupergraphSdlFromStorage.test.ts +4 -4
  108. package/src/supergraphManagers/UplinkFetcher/__tests__/tsconfig.json +8 -0
  109. package/src/supergraphManagers/UplinkFetcher/index.ts +128 -0
  110. package/src/{loadSupergraphSdlFromStorage.ts → supergraphManagers/UplinkFetcher/loadSupergraphSdlFromStorage.ts} +3 -3
  111. package/src/{outOfBandReporter.ts → supergraphManagers/UplinkFetcher/outOfBandReporter.ts} +2 -2
  112. package/src/supergraphManagers/index.ts +4 -0
  113. package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +5 -5
  114. package/src/utilities/createHash.ts +10 -0
  115. package/src/utilities/isNodeLike.ts +11 -0
  116. package/CHANGELOG.md +0 -474
  117. package/dist/loadServicesFromRemoteEndpoint.d.ts +0 -13
  118. package/dist/loadServicesFromRemoteEndpoint.d.ts.map +0 -1
  119. package/dist/loadServicesFromRemoteEndpoint.js.map +0 -1
  120. package/dist/loadSupergraphSdlFromStorage.d.ts.map +0 -1
  121. package/dist/loadSupergraphSdlFromStorage.js.map +0 -1
  122. package/dist/outOfBandReporter.d.ts.map +0 -1
  123. package/dist/outOfBandReporter.js.map +0 -1
  124. package/src/__tests__/gateway/composedSdl.test.ts +0 -44
@@ -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']>,
@@ -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,39 +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
- const gateway = new ApolloGateway({
74
- async experimental_updateServiceDefinitions() {
75
- return {
76
- serviceDefinitions: [serviceDefinitions[0]],
77
- compositionMetadata: {
78
- formatVersion: 1,
79
- id: 'abc',
80
- implementingServiceLocations: [],
81
- schemaHash: 'abc',
82
- },
83
- isNewSchema: true,
84
- };
85
- },
86
- serviceList: [],
87
- experimental_didFailComposition,
88
- logger,
89
- });
90
-
91
- await expect(gateway.load()).rejects.toThrowError();
92
-
93
- const callbackArgs = experimental_didFailComposition.mock.calls[0][0];
94
- expect(callbackArgs.serviceList).toHaveLength(1);
95
- expect(callbackArgs.errors[0]).toMatchInlineSnapshot(
96
- `[GraphQLError: [product] Book -> \`Book\` is an extension type, but \`Book\` is not defined in any service]`,
97
- );
98
- expect(callbackArgs.compositionMetadata.id).toEqual('abc');
99
- expect(experimental_didFailComposition).toBeCalled();
100
- });
101
-
102
- it('calls experimental_didUpdateComposition on schema update', async () => {
73
+ it('calls experimental_didUpdateSupergraph on schema update', async () => {
103
74
  const compositionMetadata = {
104
75
  formatVersion: 1,
105
76
  id: 'abc',
@@ -107,81 +78,73 @@ describe('lifecycle hooks', () => {
107
78
  schemaHash: 'hash1',
108
79
  };
109
80
 
110
- const update: Experimental_UpdateServiceDefinitions = async () => ({
111
- serviceDefinitions,
112
- isNewSchema: true,
113
- compositionMetadata: {
114
- ...compositionMetadata,
115
- id: '123',
116
- schemaHash: 'hash2',
117
- },
118
- });
119
-
120
- // This is the simplest way I could find to achieve mocked functions that leverage our types
121
- const mockUpdate = jest.fn(update);
122
-
123
- // We want to return a different composition across two ticks, so we mock it
124
- // slightly differenty
125
- mockUpdate.mockImplementationOnce(async () => {
126
- const services = serviceDefinitions.filter(s => s.name !== 'books');
127
- return {
128
- serviceDefinitions: [
129
- ...services,
130
- {
131
- name: 'book',
132
- typeDefs: books.typeDefs,
133
- 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',
134
91
  },
135
- ],
136
- isNewSchema: true,
137
- compositionMetadata,
138
- };
139
- });
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
+ });
140
103
 
141
104
  const mockDidUpdate = jest.fn();
142
105
 
143
106
  const gateway = new ApolloGateway({
144
107
  experimental_updateServiceDefinitions: mockUpdate,
145
- experimental_didUpdateComposition: mockDidUpdate,
108
+ experimental_didUpdateSupergraph: mockDidUpdate,
146
109
  logger,
147
110
  });
148
- // @ts-ignore for testing purposes, a short pollInterval is ideal so we'll override here
149
- gateway.experimental_pollInterval = 100;
111
+ // for testing purposes, a short pollInterval is ideal so we'll override here
112
+ gateway['pollIntervalInMs'] = 100;
150
113
 
151
- let resolve1: Function;
152
- let resolve2: Function;
153
- const schemaChangeBlocker1 = new Promise(res => (resolve1 = res));
154
- const schemaChangeBlocker2 = new Promise(res => (resolve2 = res));
114
+ const schemaChangeBlocker1 = resolvable();
115
+ const schemaChangeBlocker2 = resolvable();
155
116
 
156
- gateway.onSchemaChange(
117
+ gateway.onSchemaLoadOrUpdate(
157
118
  jest
158
119
  .fn()
159
- .mockImplementationOnce(() => resolve1())
160
- .mockImplementationOnce(() => resolve2()),
120
+ .mockImplementationOnce(() => schemaChangeBlocker1.resolve())
121
+ .mockImplementationOnce(() => schemaChangeBlocker2.resolve()),
161
122
  );
162
123
 
163
124
  await gateway.load();
164
125
 
165
126
  await schemaChangeBlocker1;
127
+
166
128
  expect(mockUpdate).toBeCalledTimes(1);
167
129
  expect(mockDidUpdate).toBeCalledTimes(1);
168
130
 
169
131
  await schemaChangeBlocker2;
132
+
170
133
  expect(mockUpdate).toBeCalledTimes(2);
171
134
  expect(mockDidUpdate).toBeCalledTimes(2);
172
135
 
173
136
  const [firstCall, secondCall] = mockDidUpdate.mock.calls;
174
137
 
175
- expect(firstCall[0]!.schema).toBeDefined();
176
- expect(firstCall[0].compositionMetadata!.schemaHash).toEqual('hash1');
138
+ const expectedFirstId = '562c22b3382b56b1651944a96e89a361fe847b9b32660eae5ecbd12adc20bf8b'
139
+ expect(firstCall[0]!.compositionId).toEqual(expectedFirstId);
177
140
  // first call should have no second "previous" argument
178
141
  expect(firstCall[1]).toBeUndefined();
179
142
 
180
- expect(secondCall[0].schema).toBeDefined();
181
- expect(secondCall[0].compositionMetadata!.schemaHash).toEqual('hash2');
143
+ expect(secondCall[0]!.compositionId).toEqual(
144
+ '0ced02894592ade4376276d11735b46723eb84850c32765cb78502ba5c29a563',
145
+ );
182
146
  // second call should have previous info in the second arg
183
- expect(secondCall[1]!.schema).toBeDefined();
184
- expect(secondCall[1]!.compositionMetadata!.schemaHash).toEqual('hash1');
147
+ expect(secondCall[1]!.compositionId).toEqual(expectedFirstId);
185
148
 
186
149
  await gateway.stop();
187
150
  });
@@ -206,34 +169,31 @@ describe('lifecycle hooks', () => {
206
169
  it('warns when polling on the default fetcher', async () => {
207
170
  new ApolloGateway({
208
171
  serviceList: serviceDefinitions,
209
- experimental_pollInterval: 10,
172
+ pollIntervalInMs: 10,
210
173
  logger,
211
174
  });
212
- expect(logger.warn).toHaveBeenCalledTimes(1);
213
175
  expect(logger.warn).toHaveBeenCalledWith(
214
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.',
215
177
  );
216
178
  });
217
179
 
218
180
  it('registers schema change callbacks when experimental_pollInterval is set for unmanaged configs', async () => {
219
- const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions = jest.fn(
220
- async (_config) => {
181
+ const experimental_updateServiceDefinitions: Experimental_UpdateServiceDefinitions =
182
+ jest.fn(async (_config) => {
221
183
  return { serviceDefinitions, isNewSchema: true };
222
- },
223
- );
184
+ });
224
185
 
225
186
  const gateway = new ApolloGateway({
226
187
  serviceList: [{ name: 'book', url: 'http://localhost:32542' }],
227
188
  experimental_updateServiceDefinitions,
228
- experimental_pollInterval: 100,
189
+ pollIntervalInMs: 100,
229
190
  logger,
230
191
  });
231
192
 
232
- let resolve: Function;
233
- const schemaChangeBlocker = new Promise(res => (resolve = res));
234
- const schemaChangeCallback = jest.fn(() => resolve());
193
+ const schemaChangeBlocker = resolvable();
194
+ const schemaChangeCallback = jest.fn(() => schemaChangeBlocker.resolve());
235
195
 
236
- gateway.onSchemaChange(schemaChangeCallback);
196
+ gateway.onSchemaLoadOrUpdate(schemaChangeCallback);
237
197
  await gateway.load();
238
198
 
239
199
  await schemaChangeBlocker;
@@ -243,12 +203,11 @@ describe('lifecycle hooks', () => {
243
203
  });
244
204
 
245
205
  it('calls experimental_didResolveQueryPlan when executor is called', async () => {
246
- const experimental_didResolveQueryPlan: Experimental_DidResolveQueryPlanCallback = jest.fn()
206
+ const experimental_didResolveQueryPlan: Experimental_DidResolveQueryPlanCallback =
207
+ jest.fn();
247
208
 
248
209
  const gateway = new ApolloGateway({
249
- localServiceList: [
250
- books
251
- ],
210
+ localServiceList: [books],
252
211
  experimental_didResolveQueryPlan,
253
212
  });
254
213
 
@@ -258,7 +217,7 @@ describe('lifecycle hooks', () => {
258
217
  { book(isbn: "0262510871") { year } }
259
218
  `;
260
219
 
261
- // @ts-ignore
220
+ // @ts-ignore
262
221
  await executor({
263
222
  source,
264
223
  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,18 +1,16 @@
1
1
  import { gunzipSync } from 'zlib';
2
2
  import nock from 'nock';
3
- import { GraphQLSchemaModule } from 'apollo-graphql';
4
- import gql from 'graphql-tag';
5
3
  import { buildSubgraphSchema } from '@apollo/subgraph';
6
4
  import { ApolloServer } from 'apollo-server';
7
5
  import { ApolloServerPluginUsageReporting } from 'apollo-server-core';
8
- import { execute, toPromise } from 'apollo-link';
9
- import { createHttpLink } from 'apollo-link-http';
10
6
  import fetch from 'node-fetch';
11
7
  import { ApolloGateway } from '../..';
12
8
  import { Plugin, Config, Refs } from 'pretty-format';
13
9
  import { Report, Trace } from 'apollo-reporting-protobuf';
14
10
  import { fixtures } from 'apollo-federation-integration-testsuite';
15
11
  import { nockAfterEach, nockBeforeEach } from '../nockAssertions';
12
+ import resolvable, { Resolvable } from '@josephg/resolvable';
13
+ import { GraphQLSchemaModule } from '../../schema-helper';
16
14
 
17
15
  // Normalize specific fields that change often (eg timestamps) to static values,
18
16
  // to make snapshot testing viable. (If these helpers are more generally
@@ -89,19 +87,16 @@ describe('reporting', () => {
89
87
  let backendServers: ApolloServer[];
90
88
  let gatewayServer: ApolloServer;
91
89
  let gatewayUrl: string;
92
- let reportPromise: Promise<any>;
90
+ let reportPromise: Resolvable<any>;
93
91
 
94
92
  beforeEach(async () => {
95
- let reportResolver: (report: any) => void;
96
- reportPromise = new Promise<any>((resolve) => {
97
- reportResolver = resolve;
98
- });
93
+ reportPromise = resolvable();
99
94
 
100
95
  nockBeforeEach();
101
96
  nock('https://usage-reporting.api.apollographql.com')
102
97
  .post('/api/ingress/traces')
103
98
  .reply(200, (_: any, requestBody: string) => {
104
- reportResolver(requestBody);
99
+ reportPromise.resolve(requestBody);
105
100
  return 'ok';
106
101
  });
107
102
 
@@ -143,7 +138,7 @@ describe('reporting', () => {
143
138
  });
144
139
 
145
140
  it(`queries three services`, async () => {
146
- const query = gql`
141
+ const query = `#graphql
147
142
  query {
148
143
  me {
149
144
  name {
@@ -157,12 +152,15 @@ describe('reporting', () => {
157
152
  }
158
153
  `;
159
154
 
160
- const result = await toPromise(
161
- execute(createHttpLink({ uri: gatewayUrl, fetch: fetch as any }), {
162
- query,
163
- }),
164
- );
165
- expect(result).toMatchInlineSnapshot(`
155
+ const result = await fetch(gatewayUrl, {
156
+ method: 'POST',
157
+ headers: {
158
+ 'Content-Type': 'application/json',
159
+ },
160
+ body: JSON.stringify({ query }),
161
+ });
162
+ const body = await result.json();
163
+ expect(body).toMatchInlineSnapshot(`
166
164
  Object {
167
165
  "data": Object {
168
166
  "me": Object {
@@ -233,6 +231,34 @@ describe('reporting', () => {
233
231
  "tracesPerQuery": Object {
234
232
  "# -
235
233
  {me{name{first last}}topProducts{name}}": Object {
234
+ "referencedFieldsByType": Object {
235
+ "Name": Object {
236
+ "fieldNames": Array [
237
+ "first",
238
+ "last",
239
+ ],
240
+ "isInterface": false,
241
+ },
242
+ "Product": Object {
243
+ "fieldNames": Array [
244
+ "name",
245
+ ],
246
+ "isInterface": true,
247
+ },
248
+ "Query": Object {
249
+ "fieldNames": Array [
250
+ "me",
251
+ "topProducts",
252
+ ],
253
+ "isInterface": false,
254
+ },
255
+ "User": Object {
256
+ "fieldNames": Array [
257
+ "name",
258
+ ],
259
+ "isInterface": false,
260
+ },
261
+ },
236
262
  "trace": Array [
237
263
  Object {
238
264
  "cachePolicy": Object {
@@ -240,14 +266,13 @@ describe('reporting', () => {
240
266
  "scope": "PRIVATE",
241
267
  },
242
268
  "clientName": "",
243
- "clientReferenceId": "",
244
269
  "clientVersion": "",
245
- "details": Object {},
246
270
  "durationNs": 12345,
247
271
  "endTime": Object {
248
272
  "nanos": 123000000,
249
273
  "seconds": "1562203363",
250
274
  },
275
+ "fieldExecutionWeight": 1,
251
276
  "forbiddenOperation": false,
252
277
  "fullQueryCacheHit": false,
253
278
  "http": Object {