@apollo/gateway 2.0.0-preview.9 → 2.0.2-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.
- package/dist/config.d.ts +2 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js.map +1 -1
- package/dist/datasources/RemoteGraphQLDataSource.js +2 -2
- package/dist/datasources/RemoteGraphQLDataSource.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -3
- package/dist/index.js.map +1 -1
- package/dist/schema-helper/addExtensions.d.ts +3 -0
- package/dist/schema-helper/addExtensions.d.ts.map +1 -0
- package/dist/schema-helper/addExtensions.js +20 -0
- package/dist/schema-helper/addExtensions.js.map +1 -0
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts +1 -1
- package/dist/supergraphManagers/IntrospectAndCompose/index.d.ts.map +1 -1
- package/dist/supergraphManagers/LegacyFetcher/index.d.ts +1 -1
- package/dist/supergraphManagers/LegacyFetcher/index.d.ts.map +1 -1
- package/dist/supergraphManagers/LocalCompose/index.d.ts +1 -1
- package/dist/supergraphManagers/LocalCompose/index.d.ts.map +1 -1
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts +1 -1
- package/dist/supergraphManagers/UplinkFetcher/index.d.ts.map +1 -1
- package/dist/typings/graphql.d.ts +34 -0
- package/dist/typings/graphql.d.ts.map +1 -0
- package/dist/{schema-helper/resolverMap.js → typings/graphql.js} +1 -1
- package/dist/typings/graphql.js.map +1 -0
- package/package.json +10 -8
- package/src/__tests__/executeQueryPlan.test.ts +4 -1
- package/src/__tests__/execution-utils.ts +5 -4
- package/src/__tests__/gateway/endToEnd.test.ts +364 -41
- package/src/__tests__/gateway/executor.test.ts +1 -1
- package/src/__tests__/gateway/extensions.test.ts +37 -0
- package/src/__tests__/gateway/lifecycle-hooks.test.ts +2 -2
- package/src/__tests__/gateway/reporting.test.ts +1 -1
- package/src/__tests__/gateway/supergraphSdl.test.ts +2 -2
- package/src/__tests__/integration/configuration.test.ts +1 -1
- package/src/__tests__/integration/logger.test.ts +1 -1
- package/src/__tests__/integration/networkRequests.test.ts +1 -1
- package/src/config.ts +2 -4
- package/src/datasources/RemoteGraphQLDataSource.ts +2 -2
- package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
- package/src/index.ts +13 -5
- package/src/schema-helper/__tests__/addExtensions.test.ts +11 -0
- package/src/schema-helper/addExtensions.ts +17 -0
- package/src/supergraphManagers/IntrospectAndCompose/__tests__/IntrospectAndCompose.test.ts +1 -1
- package/src/supergraphManagers/IntrospectAndCompose/index.ts +1 -1
- package/src/supergraphManagers/LegacyFetcher/index.ts +1 -1
- package/src/supergraphManagers/LocalCompose/index.ts +1 -1
- package/src/supergraphManagers/UplinkFetcher/index.ts +1 -1
- package/src/typings/graphql.ts +43 -0
- package/dist/schema-helper/addResolversToSchema.d.ts +0 -4
- package/dist/schema-helper/addResolversToSchema.d.ts.map +0 -1
- package/dist/schema-helper/addResolversToSchema.js +0 -62
- package/dist/schema-helper/addResolversToSchema.js.map +0 -1
- package/dist/schema-helper/error.d.ts +0 -6
- package/dist/schema-helper/error.d.ts.map +0 -1
- package/dist/schema-helper/error.js +0 -14
- package/dist/schema-helper/error.js.map +0 -1
- package/dist/schema-helper/index.d.ts +0 -4
- package/dist/schema-helper/index.d.ts.map +0 -1
- package/dist/schema-helper/index.js +0 -20
- package/dist/schema-helper/index.js.map +0 -1
- package/dist/schema-helper/resolverMap.d.ts +0 -16
- package/dist/schema-helper/resolverMap.d.ts.map +0 -1
- package/dist/schema-helper/resolverMap.js.map +0 -1
- package/dist/utilities/createHash.d.ts +0 -2
- package/dist/utilities/createHash.d.ts.map +0 -1
- package/dist/utilities/createHash.js +0 -15
- package/dist/utilities/createHash.js.map +0 -1
- package/dist/utilities/isNodeLike.d.ts +0 -3
- package/dist/utilities/isNodeLike.d.ts.map +0 -1
- package/dist/utilities/isNodeLike.js +0 -8
- package/dist/utilities/isNodeLike.js.map +0 -1
- package/src/schema-helper/addResolversToSchema.ts +0 -83
- package/src/schema-helper/error.ts +0 -11
- package/src/schema-helper/index.ts +0 -3
- package/src/schema-helper/resolverMap.ts +0 -23
- package/src/utilities/createHash.ts +0 -10
- package/src/utilities/isNodeLike.ts +0 -11
|
@@ -1,10 +1,13 @@
|
|
|
1
1
|
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
2
2
|
import { ApolloServer } from 'apollo-server';
|
|
3
|
-
import fetch from 'node-fetch';
|
|
3
|
+
import fetch, { Response } from 'node-fetch';
|
|
4
4
|
import { ApolloGateway } from '../..';
|
|
5
5
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
6
6
|
import { ApolloServerPluginInlineTrace } from 'apollo-server-core';
|
|
7
|
-
import { GraphQLSchemaModule } from '
|
|
7
|
+
import { GraphQLSchemaModule } from '@apollo/subgraph/src/schema-helper';
|
|
8
|
+
import { buildSchema, ObjectType, ServiceDefinition } from '@apollo/federation-internals';
|
|
9
|
+
import gql from 'graphql-tag';
|
|
10
|
+
import { printSchema } from 'graphql';
|
|
8
11
|
|
|
9
12
|
async function startFederatedServer(modules: GraphQLSchemaModule[]) {
|
|
10
13
|
const schema = buildSubgraphSchema(modules);
|
|
@@ -17,34 +20,49 @@ async function startFederatedServer(modules: GraphQLSchemaModule[]) {
|
|
|
17
20
|
return { url, server };
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
23
|
+
let backendServers: ApolloServer[];
|
|
24
|
+
let gateway: ApolloGateway;
|
|
25
|
+
let gatewayServer: ApolloServer;
|
|
26
|
+
let gatewayUrl: string;
|
|
24
27
|
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
});
|
|
38
|
-
({ url: gatewayUrl } = await gatewayServer.listen({ port: 0 }));
|
|
28
|
+
async function startServicesAndGateway(servicesDefs: ServiceDefinition[]) {
|
|
29
|
+
backendServers = [];
|
|
30
|
+
const serviceList = [];
|
|
31
|
+
for (const serviceDef of servicesDefs) {
|
|
32
|
+
const { server, url } = await startFederatedServer([serviceDef]);
|
|
33
|
+
backendServers.push(server);
|
|
34
|
+
serviceList.push({ name: serviceDef.name, url });
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
gateway = new ApolloGateway({ serviceList });
|
|
38
|
+
gatewayServer = new ApolloServer({
|
|
39
|
+
gateway,
|
|
39
40
|
});
|
|
41
|
+
({ url: gatewayUrl } = await gatewayServer.listen({ port: 0 }));
|
|
42
|
+
}
|
|
40
43
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
44
|
+
async function queryGateway(query: string): Promise<Response> {
|
|
45
|
+
return fetch(gatewayUrl, {
|
|
46
|
+
method: 'POST',
|
|
47
|
+
headers: {
|
|
48
|
+
'Content-Type': 'application/json',
|
|
49
|
+
},
|
|
50
|
+
body: JSON.stringify({ query }),
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
afterEach(async () => {
|
|
55
|
+
for (const server of backendServers) {
|
|
56
|
+
await server.stop();
|
|
57
|
+
}
|
|
58
|
+
if (gatewayServer) {
|
|
59
|
+
await gatewayServer.stop();
|
|
60
|
+
}
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
describe('caching', () => {
|
|
64
|
+
beforeEach(async () => {
|
|
65
|
+
await startServicesAndGateway(fixtures);
|
|
48
66
|
});
|
|
49
67
|
|
|
50
68
|
it(`cache control`, async () => {
|
|
@@ -62,13 +80,7 @@ describe('end-to-end', () => {
|
|
|
62
80
|
}
|
|
63
81
|
`;
|
|
64
82
|
|
|
65
|
-
const response = await
|
|
66
|
-
method: 'POST',
|
|
67
|
-
headers: {
|
|
68
|
-
'Content-Type': 'application/json',
|
|
69
|
-
},
|
|
70
|
-
body: JSON.stringify({ query }),
|
|
71
|
-
});
|
|
83
|
+
const response = await queryGateway(query);
|
|
72
84
|
const result = await response.json();
|
|
73
85
|
expect(result).toMatchInlineSnapshot(`
|
|
74
86
|
Object {
|
|
@@ -122,13 +134,7 @@ describe('end-to-end', () => {
|
|
|
122
134
|
}
|
|
123
135
|
`;
|
|
124
136
|
|
|
125
|
-
const response = await
|
|
126
|
-
method: 'POST',
|
|
127
|
-
headers: {
|
|
128
|
-
'Content-Type': 'application/json',
|
|
129
|
-
},
|
|
130
|
-
body: JSON.stringify({ query }),
|
|
131
|
-
});
|
|
137
|
+
const response = await queryGateway(query);
|
|
132
138
|
const result = await response.json();
|
|
133
139
|
expect(result).toMatchInlineSnapshot(`
|
|
134
140
|
Object {
|
|
@@ -164,3 +170,320 @@ describe('end-to-end', () => {
|
|
|
164
170
|
expect(response.headers.get('cache-control')).toBe(null);
|
|
165
171
|
});
|
|
166
172
|
});
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Tests for a number of specific features end-to-end.
|
|
176
|
+
* Note that those features have (or at least should have) much thorough test coverage in the various places
|
|
177
|
+
* that handle them more directly, and those tests largely duplicate other test, but it's meant to ensure we
|
|
178
|
+
* have basic end-to-end testing, thus ensuring those feature don't break in places we didn't expect.
|
|
179
|
+
*/
|
|
180
|
+
describe('end-to-end features', () => {
|
|
181
|
+
it('@tag renaming', async () => {
|
|
182
|
+
const subgraphA = {
|
|
183
|
+
name: 'A',
|
|
184
|
+
url: 'https://A',
|
|
185
|
+
typeDefs: gql`
|
|
186
|
+
extend schema
|
|
187
|
+
@link(
|
|
188
|
+
url: "https://specs.apollo.dev/federation/v2.0",
|
|
189
|
+
import: [ "@key", { name: "@tag", as: "@federationTag"} ]
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
type Query {
|
|
193
|
+
t: T
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
type T @key(fields: "k") {
|
|
197
|
+
k: ID
|
|
198
|
+
x: Int @federationTag(name: "Important")
|
|
199
|
+
}
|
|
200
|
+
`,
|
|
201
|
+
resolvers: {
|
|
202
|
+
Query: {
|
|
203
|
+
t: () => ({
|
|
204
|
+
k: 42,
|
|
205
|
+
x: 1
|
|
206
|
+
}),
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const subgraphB = {
|
|
212
|
+
name: 'B',
|
|
213
|
+
url: 'https://B',
|
|
214
|
+
typeDefs: gql`
|
|
215
|
+
extend schema
|
|
216
|
+
@link(
|
|
217
|
+
url: "https://specs.apollo.dev/federation/v2.0",
|
|
218
|
+
import: [ "@key", { name: "@tag", as: "@federationTag"} ]
|
|
219
|
+
)
|
|
220
|
+
|
|
221
|
+
type T @key(fields: "k") {
|
|
222
|
+
k: ID
|
|
223
|
+
y: Int @federationTag(name: "Less Important")
|
|
224
|
+
}
|
|
225
|
+
`,
|
|
226
|
+
resolvers: {
|
|
227
|
+
T: {
|
|
228
|
+
__resolveReference: ({ k }: { k: string }) => {
|
|
229
|
+
return k === '42' ? ({ y: 2 }) : undefined;
|
|
230
|
+
},
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
await startServicesAndGateway([subgraphA, subgraphB]);
|
|
236
|
+
|
|
237
|
+
const query = `
|
|
238
|
+
{
|
|
239
|
+
t {
|
|
240
|
+
x
|
|
241
|
+
y
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
`;
|
|
245
|
+
|
|
246
|
+
const response = await queryGateway(query);
|
|
247
|
+
const result = await response.json();
|
|
248
|
+
expect(result).toMatchInlineSnapshot(`
|
|
249
|
+
Object {
|
|
250
|
+
"data": Object {
|
|
251
|
+
"t": Object {
|
|
252
|
+
"x": 1,
|
|
253
|
+
"y": 2,
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
}
|
|
257
|
+
`);
|
|
258
|
+
|
|
259
|
+
const supergraphSdl = gateway.__testing().supergraphSdl;
|
|
260
|
+
expect(supergraphSdl).toBeDefined();
|
|
261
|
+
const supergraph = buildSchema(supergraphSdl!);
|
|
262
|
+
const typeT = supergraph.type('T') as ObjectType;
|
|
263
|
+
expect(typeT.field('x')?.appliedDirectivesOf('federationTag').toString()).toStrictEqual('@federationTag(name: "Important")');
|
|
264
|
+
expect(typeT.field('y')?.appliedDirectivesOf('federationTag').toString()).toStrictEqual('@federationTag(name: "Less Important")');
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
it('handles fed1 schema', async () => {
|
|
268
|
+
const subgraphA = {
|
|
269
|
+
name: 'A',
|
|
270
|
+
url: 'https://A',
|
|
271
|
+
typeDefs: gql`
|
|
272
|
+
type Query {
|
|
273
|
+
t: T
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
type T @key(fields: "k") {
|
|
277
|
+
k: ID
|
|
278
|
+
x: Int
|
|
279
|
+
}
|
|
280
|
+
`,
|
|
281
|
+
resolvers: {
|
|
282
|
+
Query: {
|
|
283
|
+
t: () => ({
|
|
284
|
+
k: 42,
|
|
285
|
+
x: 1
|
|
286
|
+
}),
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
};
|
|
290
|
+
|
|
291
|
+
const subgraphB = {
|
|
292
|
+
name: 'B',
|
|
293
|
+
url: 'https://B',
|
|
294
|
+
typeDefs: gql`
|
|
295
|
+
type T @key(fields: "k") {
|
|
296
|
+
k: ID
|
|
297
|
+
y: Int
|
|
298
|
+
}
|
|
299
|
+
`,
|
|
300
|
+
resolvers: {
|
|
301
|
+
T: {
|
|
302
|
+
__resolveReference: ({ k }: { k: string }) => {
|
|
303
|
+
return k === '42' ? ({ y: 2 }) : undefined;
|
|
304
|
+
},
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
|
|
309
|
+
await startServicesAndGateway([subgraphA, subgraphB]);
|
|
310
|
+
|
|
311
|
+
const query = `
|
|
312
|
+
{
|
|
313
|
+
t {
|
|
314
|
+
x
|
|
315
|
+
y
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
`;
|
|
319
|
+
|
|
320
|
+
const response = await queryGateway(query);
|
|
321
|
+
const result = await response.json();
|
|
322
|
+
expect(result).toMatchInlineSnapshot(`
|
|
323
|
+
Object {
|
|
324
|
+
"data": Object {
|
|
325
|
+
"t": Object {
|
|
326
|
+
"x": 1,
|
|
327
|
+
"y": 2,
|
|
328
|
+
},
|
|
329
|
+
},
|
|
330
|
+
}
|
|
331
|
+
`);
|
|
332
|
+
});
|
|
333
|
+
|
|
334
|
+
it('removals of @inaccessible', async () => {
|
|
335
|
+
const subgraphA = {
|
|
336
|
+
name: 'A',
|
|
337
|
+
url: 'https://A',
|
|
338
|
+
typeDefs: gql`
|
|
339
|
+
extend schema
|
|
340
|
+
@link(
|
|
341
|
+
url: "https://specs.apollo.dev/federation/v2.0",
|
|
342
|
+
import: [ "@key", "@shareable", "@inaccessible"]
|
|
343
|
+
)
|
|
344
|
+
|
|
345
|
+
type Query {
|
|
346
|
+
t: T
|
|
347
|
+
f(e: E): Int
|
|
348
|
+
}
|
|
349
|
+
|
|
350
|
+
enum E {
|
|
351
|
+
FOO
|
|
352
|
+
BAR @inaccessible
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
type T @key(fields: "k") {
|
|
356
|
+
k: ID
|
|
357
|
+
a: Int @inaccessible
|
|
358
|
+
b: Int
|
|
359
|
+
c: String @shareable
|
|
360
|
+
}
|
|
361
|
+
`,
|
|
362
|
+
resolvers: {
|
|
363
|
+
Query: {
|
|
364
|
+
t: () => ({
|
|
365
|
+
k: 42,
|
|
366
|
+
a: 1,
|
|
367
|
+
b: 2,
|
|
368
|
+
c: 3,
|
|
369
|
+
}),
|
|
370
|
+
f: (_: any, args: any) => {
|
|
371
|
+
return args.e === 'FOO' ? 0 : 1;
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const subgraphB = {
|
|
378
|
+
name: 'B',
|
|
379
|
+
url: 'https://B',
|
|
380
|
+
typeDefs: gql`
|
|
381
|
+
extend schema
|
|
382
|
+
@link(
|
|
383
|
+
url: "https://specs.apollo.dev/federation/v2.0",
|
|
384
|
+
import: [ "@key", "@shareable", "@inaccessible" ]
|
|
385
|
+
)
|
|
386
|
+
|
|
387
|
+
type T @key(fields: "k") {
|
|
388
|
+
k: ID
|
|
389
|
+
c: String @shareable @inaccessible
|
|
390
|
+
d: String
|
|
391
|
+
}
|
|
392
|
+
`,
|
|
393
|
+
resolvers: {
|
|
394
|
+
T: {
|
|
395
|
+
__resolveReference: ({ k }: { k: string }) => {
|
|
396
|
+
return k === '42' ? ({ c: 'foo', d: 'bar' }) : undefined;
|
|
397
|
+
},
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
|
|
402
|
+
await startServicesAndGateway([subgraphA, subgraphB]);
|
|
403
|
+
|
|
404
|
+
const q1 = `
|
|
405
|
+
{
|
|
406
|
+
t {
|
|
407
|
+
b
|
|
408
|
+
d
|
|
409
|
+
}
|
|
410
|
+
f(e: FOO)
|
|
411
|
+
}
|
|
412
|
+
`;
|
|
413
|
+
|
|
414
|
+
const resp1 = await queryGateway(q1);
|
|
415
|
+
const res1 = await resp1.json();
|
|
416
|
+
expect(res1).toMatchInlineSnapshot(`
|
|
417
|
+
Object {
|
|
418
|
+
"data": Object {
|
|
419
|
+
"f": 0,
|
|
420
|
+
"t": Object {
|
|
421
|
+
"b": 2,
|
|
422
|
+
"d": "bar",
|
|
423
|
+
},
|
|
424
|
+
},
|
|
425
|
+
}
|
|
426
|
+
`);
|
|
427
|
+
|
|
428
|
+
// Make sure the exposed API doesn't have any @inaccessible elements.
|
|
429
|
+
expect(printSchema(gateway.schema!)).toMatchInlineSnapshot(`
|
|
430
|
+
"enum E {
|
|
431
|
+
FOO
|
|
432
|
+
}
|
|
433
|
+
|
|
434
|
+
type Query {
|
|
435
|
+
t: T
|
|
436
|
+
f(e: E): Int
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
type T {
|
|
440
|
+
k: ID
|
|
441
|
+
b: Int
|
|
442
|
+
d: String
|
|
443
|
+
}"
|
|
444
|
+
`);
|
|
445
|
+
|
|
446
|
+
// Lastly, make sure querying inaccessible things is rejected
|
|
447
|
+
const q2 = `
|
|
448
|
+
{
|
|
449
|
+
f(e: BAR)
|
|
450
|
+
}
|
|
451
|
+
`;
|
|
452
|
+
const resp2 = await queryGateway(q2);
|
|
453
|
+
const res2 = await resp2.json();
|
|
454
|
+
expect(res2).toMatchInlineSnapshot(`
|
|
455
|
+
Object {
|
|
456
|
+
"errors": Array [
|
|
457
|
+
Object {
|
|
458
|
+
"extensions": Object {
|
|
459
|
+
"code": "GRAPHQL_VALIDATION_FAILED",
|
|
460
|
+
},
|
|
461
|
+
"message": "Value \\"BAR\\" does not exist in \\"E\\" enum.",
|
|
462
|
+
},
|
|
463
|
+
],
|
|
464
|
+
}
|
|
465
|
+
`);
|
|
466
|
+
|
|
467
|
+
const q3 = `
|
|
468
|
+
{
|
|
469
|
+
t {
|
|
470
|
+
a
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
`;
|
|
474
|
+
const resp3 = await queryGateway(q3);
|
|
475
|
+
const res3 = await resp3.json();
|
|
476
|
+
expect(res3).toMatchInlineSnapshot(`
|
|
477
|
+
Object {
|
|
478
|
+
"errors": Array [
|
|
479
|
+
Object {
|
|
480
|
+
"extensions": Object {
|
|
481
|
+
"code": "GRAPHQL_VALIDATION_FAILED",
|
|
482
|
+
},
|
|
483
|
+
"message": "Cannot query field \\"a\\" on type \\"T\\". Did you mean \\"b\\", \\"d\\", or \\"k\\"?",
|
|
484
|
+
},
|
|
485
|
+
],
|
|
486
|
+
}
|
|
487
|
+
`);
|
|
488
|
+
});
|
|
489
|
+
})
|
|
@@ -2,7 +2,7 @@ import { fetch } from '../../__mocks__/make-fetch-happen-fetcher';
|
|
|
2
2
|
import gql from 'graphql-tag';
|
|
3
3
|
import { ApolloGateway } from '../../';
|
|
4
4
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
5
|
-
import { Logger } from 'apollo
|
|
5
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
6
6
|
|
|
7
7
|
let logger: {
|
|
8
8
|
warn: jest.MockedFunction<Logger['warn']>,
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { ApolloGateway } from '@apollo/gateway';
|
|
2
|
+
import resolvable from '@josephg/resolvable';
|
|
3
|
+
import { GraphQLSchema } from 'graphql';
|
|
4
|
+
import { getTestingSupergraphSdl } from '../execution-utils';
|
|
5
|
+
const { version } = require('../../../package.json');
|
|
6
|
+
|
|
7
|
+
describe('addExtensions', () => {
|
|
8
|
+
let gateway: ApolloGateway;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
gateway = new ApolloGateway({
|
|
12
|
+
supergraphSdl: getTestingSupergraphSdl(),
|
|
13
|
+
});
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
afterEach(async () => {
|
|
17
|
+
await gateway.stop();
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
it('has extensions on loaded schemas', async () => {
|
|
21
|
+
const { schema } = await gateway.load();
|
|
22
|
+
expect(schema.extensions).toEqual({ apollo: { gateway: { version: version } } });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
it('has extensions on schema updates', async () => {
|
|
26
|
+
const schemaChangeBlocker = resolvable<{
|
|
27
|
+
apiSchema: GraphQLSchema;
|
|
28
|
+
coreSupergraphSdl: string;
|
|
29
|
+
}>();
|
|
30
|
+
|
|
31
|
+
gateway.onSchemaLoadOrUpdate(schemaChangeBlocker.resolve);
|
|
32
|
+
gateway.load();
|
|
33
|
+
|
|
34
|
+
const { apiSchema } = await schemaChangeBlocker;
|
|
35
|
+
expect(apiSchema.extensions).toEqual({ apollo: { gateway: { version: version } } });
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -16,9 +16,9 @@ import {
|
|
|
16
16
|
fixtures,
|
|
17
17
|
fixturesWithUpdate,
|
|
18
18
|
} from 'apollo-federation-integration-testsuite';
|
|
19
|
-
import {
|
|
19
|
+
import { createHash } from '@apollo/utils.createhash';
|
|
20
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
20
21
|
import resolvable from '@josephg/resolvable';
|
|
21
|
-
import { createHash } from '../../utilities/createHash';
|
|
22
22
|
import { getTestingSupergraphSdl } from '../execution-utils';
|
|
23
23
|
|
|
24
24
|
// The order of this was specified to preserve existing test coverage. Typically
|
|
@@ -13,7 +13,7 @@ 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
15
|
import { nockAfterEach, nockBeforeEach } from '../nockAssertions';
|
|
16
|
-
import { GraphQLSchemaModule } from '
|
|
16
|
+
import { GraphQLSchemaModule } from '@apollo/subgraph/src/schema-helper';
|
|
17
17
|
import resolvable, { Resolvable } from '@josephg/resolvable';
|
|
18
18
|
|
|
19
19
|
// Normalize specific fields that change often (eg timestamps) to static values,
|
|
@@ -5,9 +5,9 @@ import {
|
|
|
5
5
|
SupergraphSdlUpdateFunction,
|
|
6
6
|
} from '@apollo/gateway';
|
|
7
7
|
import { fixturesWithUpdate } from 'apollo-federation-integration-testsuite';
|
|
8
|
-
import { createHash } from '
|
|
8
|
+
import { createHash } from '@apollo/utils.createhash';
|
|
9
9
|
import { ApolloServer } from 'apollo-server';
|
|
10
|
-
import { Logger } from 'apollo
|
|
10
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
11
11
|
import { fetch } from '../../__mocks__/make-fetch-happen-fetcher';
|
|
12
12
|
import { getTestingSupergraphSdl } from '../execution-utils';
|
|
13
13
|
import { mockAllServicesHealthCheckSuccess } from '../integration/nockMocks';
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import gql from 'graphql-tag';
|
|
2
2
|
import http from 'http';
|
|
3
3
|
import mockedEnv from 'mocked-env';
|
|
4
|
-
import { Logger } from 'apollo
|
|
4
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
5
5
|
import { ApolloGateway } from '../..';
|
|
6
6
|
import {
|
|
7
7
|
mockSdlQuerySuccess,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import gql from 'graphql-tag';
|
|
2
2
|
import { GraphQLObjectType, GraphQLSchema } from 'graphql';
|
|
3
3
|
import mockedEnv from 'mocked-env';
|
|
4
|
-
import { Logger } from 'apollo
|
|
4
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
5
5
|
import { ApolloGateway } from '../..';
|
|
6
6
|
import {
|
|
7
7
|
mockSdlQuerySuccess,
|
package/src/config.ts
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { GraphQLError, GraphQLSchema } from 'graphql';
|
|
2
2
|
import { HeadersInit } from 'node-fetch';
|
|
3
3
|
import { fetch } from 'apollo-server-env';
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
Logger,
|
|
7
|
-
} from 'apollo-server-types';
|
|
4
|
+
import { GraphQLRequestContextExecutionDidStart } from 'apollo-server-types';
|
|
5
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
8
6
|
import { GraphQLDataSource } from './datasources/types';
|
|
9
7
|
import { QueryPlan } from '@apollo/query-planner';
|
|
10
8
|
import { OperationContext } from './operationContext';
|
|
@@ -15,7 +15,7 @@ import {
|
|
|
15
15
|
import { fetch, Request, Headers, Response } from 'apollo-server-env';
|
|
16
16
|
import { isObject } from '../utilities/predicates';
|
|
17
17
|
import { GraphQLDataSource, GraphQLDataSourceProcessOptions, GraphQLDataSourceRequestKind } from './types';
|
|
18
|
-
import
|
|
18
|
+
import { createHash } from '@apollo/utils.createhash';
|
|
19
19
|
import { parseCacheControlHeader } from './parseCacheControlHeader';
|
|
20
20
|
import fetcher from 'make-fetch-happen';
|
|
21
21
|
export class RemoteGraphQLDataSource<
|
|
@@ -112,7 +112,7 @@ export class RemoteGraphQLDataSource<
|
|
|
112
112
|
: null;
|
|
113
113
|
|
|
114
114
|
if (this.apq) {
|
|
115
|
-
const apqHash =
|
|
115
|
+
const apqHash = createHash('sha256').update(request.query).digest('hex');
|
|
116
116
|
|
|
117
117
|
// Take the original extensions and extend them with
|
|
118
118
|
// the necessary "extensions" for APQ handshaking.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { LocalGraphQLDataSource } from '../LocalGraphQLDataSource';
|
|
2
2
|
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
3
3
|
import gql from 'graphql-tag';
|
|
4
|
-
import { GraphQLResolverMap } from '
|
|
4
|
+
import { GraphQLResolverMap } from '@apollo/subgraph/src/schema-helper';
|
|
5
5
|
import { GraphQLRequestContext } from 'apollo-server-types';
|
|
6
6
|
import { GraphQLDataSourceRequestKind } from '../types';
|
|
7
7
|
|
package/src/index.ts
CHANGED
|
@@ -2,9 +2,10 @@ import { deprecate } from 'util';
|
|
|
2
2
|
import { GraphQLService, Unsubscriber } from 'apollo-server-core';
|
|
3
3
|
import {
|
|
4
4
|
GraphQLExecutionResult,
|
|
5
|
-
Logger,
|
|
6
5
|
GraphQLRequestContextExecutionDidStart,
|
|
7
6
|
} from 'apollo-server-types';
|
|
7
|
+
import { createHash } from '@apollo/utils.createhash';
|
|
8
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
8
9
|
import { InMemoryLRUCache } from 'apollo-server-caching';
|
|
9
10
|
import {
|
|
10
11
|
isObjectType,
|
|
@@ -53,14 +54,19 @@ import {
|
|
|
53
54
|
} from './config';
|
|
54
55
|
import { SpanStatusCode } from '@opentelemetry/api';
|
|
55
56
|
import { OpenTelemetrySpanNames, tracer } from './utilities/opentelemetry';
|
|
56
|
-
import {
|
|
57
|
+
import { addExtensions } from './schema-helper/addExtensions';
|
|
57
58
|
import {
|
|
58
59
|
IntrospectAndCompose,
|
|
59
60
|
UplinkFetcher,
|
|
60
61
|
LegacyFetcher,
|
|
61
62
|
LocalCompose,
|
|
62
63
|
} from './supergraphManagers';
|
|
63
|
-
import {
|
|
64
|
+
import {
|
|
65
|
+
buildSupergraphSchema,
|
|
66
|
+
operationFromDocument,
|
|
67
|
+
Schema,
|
|
68
|
+
ServiceDefinition,
|
|
69
|
+
} from '@apollo/federation-internals';
|
|
64
70
|
|
|
65
71
|
type DataSourceMap = {
|
|
66
72
|
[serviceName: string]: { url?: string; dataSource: GraphQLDataSource };
|
|
@@ -426,6 +432,8 @@ export class ApolloGateway implements GraphQLService {
|
|
|
426
432
|
}`,
|
|
427
433
|
);
|
|
428
434
|
|
|
435
|
+
addExtensions(this.schema!);
|
|
436
|
+
|
|
429
437
|
return {
|
|
430
438
|
schema: this.schema!,
|
|
431
439
|
executor: this.executor,
|
|
@@ -635,8 +643,8 @@ export class ApolloGateway implements GraphQLService {
|
|
|
635
643
|
): void {
|
|
636
644
|
if (this.queryPlanStore) this.queryPlanStore.flush();
|
|
637
645
|
this.apiSchema = coreSchema.toAPISchema();
|
|
638
|
-
this.schema =
|
|
639
|
-
this.apiSchema.toGraphQLJSSchema(),
|
|
646
|
+
this.schema = addExtensions(
|
|
647
|
+
wrapSchemaWithAliasResolver(this.apiSchema.toGraphQLJSSchema()),
|
|
640
648
|
);
|
|
641
649
|
this.queryPlanner = new QueryPlanner(coreSchema);
|
|
642
650
|
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { buildSchema } from 'graphql';
|
|
2
|
+
import { addExtensions } from '../addExtensions';
|
|
3
|
+
const { version } = require('../../../package.json');
|
|
4
|
+
|
|
5
|
+
describe('addExtensions', () => {
|
|
6
|
+
it('adds gateway extensions to a schema', async () => {
|
|
7
|
+
const schema = buildSchema('type Query { hello: String }');
|
|
8
|
+
expect(schema.extensions).toEqual({});
|
|
9
|
+
expect(addExtensions(schema).extensions).toEqual({ apollo: { gateway: { version: version } } });
|
|
10
|
+
});
|
|
11
|
+
});
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { GraphQLSchema } from 'graphql';
|
|
2
|
+
const { version } = require('../../package.json');
|
|
3
|
+
|
|
4
|
+
export function addExtensions(schema: GraphQLSchema): GraphQLSchema {
|
|
5
|
+
schema.extensions = {
|
|
6
|
+
...schema.extensions,
|
|
7
|
+
apollo: {
|
|
8
|
+
...schema.extensions.apollo,
|
|
9
|
+
gateway: {
|
|
10
|
+
...schema.extensions.apollo?.gateway,
|
|
11
|
+
version,
|
|
12
|
+
}
|
|
13
|
+
},
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
return schema;
|
|
17
|
+
}
|
|
@@ -9,7 +9,7 @@ import { IntrospectAndCompose } from '..';
|
|
|
9
9
|
import { mockAllServicesSdlQuerySuccess } from '../../../__tests__/integration/nockMocks';
|
|
10
10
|
import { getTestingSupergraphSdl, wait } from '../../../__tests__/execution-utils';
|
|
11
11
|
import resolvable from '@josephg/resolvable';
|
|
12
|
-
import { Logger } from 'apollo
|
|
12
|
+
import type { Logger } from '@apollo/utils.logger';
|
|
13
13
|
|
|
14
14
|
describe('IntrospectAndCompose', () => {
|
|
15
15
|
beforeEach(nockBeforeEach);
|