@apollo/gateway 0.42.0 → 0.43.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/CHANGELOG.md +26 -2
- package/dist/__generated__/graphqlTypes.d.ts +27 -21
- package/dist/__generated__/graphqlTypes.d.ts.map +1 -1
- package/dist/__generated__/graphqlTypes.js +6 -6
- package/dist/__generated__/graphqlTypes.js.map +1 -1
- package/dist/executeQueryPlan.d.ts +3 -2
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +26 -1
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/utilities/graphql.d.ts +1 -2
- package/dist/utilities/graphql.d.ts.map +1 -1
- package/dist/utilities/graphql.js +1 -9
- package/dist/utilities/graphql.js.map +1 -1
- package/package.json +5 -5
- package/src/__generated__/graphqlTypes.ts +27 -22
- package/src/__tests__/buildQueryPlan.test.ts +277 -26
- package/src/__tests__/executeQueryPlan.test.ts +264 -7
- package/src/__tests__/execution-utils.ts +6 -3
- package/src/__tests__/gateway/endToEnd.test.ts +2 -2
- package/src/__tests__/gateway/opentelemetry.test.ts +1 -1
- package/src/__tests__/gateway/queryPlanCache.test.ts +1 -1
- package/src/__tests__/gateway/reporting.test.ts +1 -1
- package/src/__tests__/integration/aliases.test.ts +1 -1
- package/src/datasources/__tests__/LocalGraphQLDataSource.test.ts +1 -1
- package/src/executeQueryPlan.ts +41 -11
- package/src/index.ts +10 -1
- package/src/utilities/graphql.ts +0 -22
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
} from 'graphql';
|
|
8
8
|
import { addResolversToSchema, GraphQLResolverMap } from 'apollo-graphql';
|
|
9
9
|
import gql from 'graphql-tag';
|
|
10
|
-
import { GraphQLRequestContext } from 'apollo-server-types';
|
|
10
|
+
import { GraphQLRequestContext, VariableValues } from 'apollo-server-types';
|
|
11
11
|
import { AuthenticationError } from 'apollo-server-core';
|
|
12
12
|
import { buildOperationContext } from '../operationContext';
|
|
13
13
|
import { executeQueryPlan } from '../executeQueryPlan';
|
|
@@ -26,10 +26,6 @@ expect.addSnapshotSerializer(astSerializer);
|
|
|
26
26
|
expect.addSnapshotSerializer(queryPlanSerializer);
|
|
27
27
|
|
|
28
28
|
describe('executeQueryPlan', () => {
|
|
29
|
-
let serviceMap: {
|
|
30
|
-
[serviceName: string]: LocalGraphQLDataSource;
|
|
31
|
-
};
|
|
32
|
-
|
|
33
29
|
function overrideResolversInService(
|
|
34
30
|
serviceName: string,
|
|
35
31
|
resolvers: GraphQLResolverMap,
|
|
@@ -44,6 +40,9 @@ describe('executeQueryPlan', () => {
|
|
|
44
40
|
return jest.spyOn(entitiesField, 'resolve');
|
|
45
41
|
}
|
|
46
42
|
|
|
43
|
+
let serviceMap: {
|
|
44
|
+
[serviceName: string]: LocalGraphQLDataSource;
|
|
45
|
+
};
|
|
47
46
|
let schema: GraphQLSchema;
|
|
48
47
|
let queryPlanner: QueryPlanner;
|
|
49
48
|
beforeEach(() => {
|
|
@@ -53,13 +52,15 @@ describe('executeQueryPlan', () => {
|
|
|
53
52
|
).not.toThrow();
|
|
54
53
|
});
|
|
55
54
|
|
|
56
|
-
function buildRequestContext(
|
|
55
|
+
function buildRequestContext(
|
|
56
|
+
variables: VariableValues = {},
|
|
57
|
+
): GraphQLRequestContext {
|
|
57
58
|
// @ts-ignore
|
|
58
59
|
return {
|
|
59
60
|
cache: undefined as any,
|
|
60
61
|
context: {},
|
|
61
62
|
request: {
|
|
62
|
-
variables
|
|
63
|
+
variables,
|
|
63
64
|
},
|
|
64
65
|
};
|
|
65
66
|
}
|
|
@@ -1379,4 +1380,260 @@ describe('executeQueryPlan', () => {
|
|
|
1379
1380
|
// `);
|
|
1380
1381
|
});
|
|
1381
1382
|
});
|
|
1383
|
+
|
|
1384
|
+
describe('top-level @skip / @include usages', () => {
|
|
1385
|
+
it(`top-level skip never calls reviews service (using literals)`, async () => {
|
|
1386
|
+
const operationDocument = gql`
|
|
1387
|
+
query {
|
|
1388
|
+
topReviews @skip(if: true) {
|
|
1389
|
+
body
|
|
1390
|
+
}
|
|
1391
|
+
}
|
|
1392
|
+
`;
|
|
1393
|
+
|
|
1394
|
+
const operationContext = buildOperationContext({
|
|
1395
|
+
schema,
|
|
1396
|
+
operationDocument,
|
|
1397
|
+
});
|
|
1398
|
+
|
|
1399
|
+
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1400
|
+
|
|
1401
|
+
const response = await executeQueryPlan(
|
|
1402
|
+
queryPlan,
|
|
1403
|
+
serviceMap,
|
|
1404
|
+
buildRequestContext(),
|
|
1405
|
+
operationContext,
|
|
1406
|
+
);
|
|
1407
|
+
|
|
1408
|
+
expect(response.data).toMatchInlineSnapshot(`Object {}`);
|
|
1409
|
+
expect(queryPlan).not.toCallService('reviews');
|
|
1410
|
+
});
|
|
1411
|
+
|
|
1412
|
+
it(`top-level skip never calls reviews service (using variables)`, async () => {
|
|
1413
|
+
const operationDocument = gql`
|
|
1414
|
+
query TopReviews($shouldSkip: Boolean!) {
|
|
1415
|
+
topReviews @skip(if: $shouldSkip) {
|
|
1416
|
+
body
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
`;
|
|
1420
|
+
|
|
1421
|
+
const operationContext = buildOperationContext({
|
|
1422
|
+
schema,
|
|
1423
|
+
operationDocument,
|
|
1424
|
+
});
|
|
1425
|
+
|
|
1426
|
+
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1427
|
+
|
|
1428
|
+
const variables = { shouldSkip: true };
|
|
1429
|
+
const response = await executeQueryPlan(
|
|
1430
|
+
queryPlan,
|
|
1431
|
+
serviceMap,
|
|
1432
|
+
buildRequestContext(variables),
|
|
1433
|
+
operationContext,
|
|
1434
|
+
);
|
|
1435
|
+
|
|
1436
|
+
expect(response.data).toMatchInlineSnapshot(`Object {}`);
|
|
1437
|
+
expect({ queryPlan, variables }).not.toCallService('reviews');
|
|
1438
|
+
});
|
|
1439
|
+
|
|
1440
|
+
it(`call to service isn't skipped unless all top-level fields are skipped`, async () => {
|
|
1441
|
+
const operationDocument = gql`
|
|
1442
|
+
query {
|
|
1443
|
+
user(id: "1") @skip(if: true) {
|
|
1444
|
+
username
|
|
1445
|
+
}
|
|
1446
|
+
me @include(if: true) {
|
|
1447
|
+
username
|
|
1448
|
+
}
|
|
1449
|
+
}
|
|
1450
|
+
`;
|
|
1451
|
+
|
|
1452
|
+
const operationContext = buildOperationContext({
|
|
1453
|
+
schema,
|
|
1454
|
+
operationDocument,
|
|
1455
|
+
});
|
|
1456
|
+
|
|
1457
|
+
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1458
|
+
|
|
1459
|
+
const response = await executeQueryPlan(
|
|
1460
|
+
queryPlan,
|
|
1461
|
+
serviceMap,
|
|
1462
|
+
buildRequestContext(),
|
|
1463
|
+
operationContext,
|
|
1464
|
+
);
|
|
1465
|
+
|
|
1466
|
+
expect(response.data).toMatchObject({
|
|
1467
|
+
me: {
|
|
1468
|
+
username: '@ada',
|
|
1469
|
+
},
|
|
1470
|
+
});
|
|
1471
|
+
expect(queryPlan).toCallService('accounts');
|
|
1472
|
+
});
|
|
1473
|
+
|
|
1474
|
+
it(`call to service is skipped when all top-level fields are skipped`, async () => {
|
|
1475
|
+
const operationDocument = gql`
|
|
1476
|
+
query {
|
|
1477
|
+
user(id: "1") @skip(if: true) {
|
|
1478
|
+
username
|
|
1479
|
+
}
|
|
1480
|
+
me @include(if: false) {
|
|
1481
|
+
username
|
|
1482
|
+
}
|
|
1483
|
+
}
|
|
1484
|
+
`;
|
|
1485
|
+
|
|
1486
|
+
const operationContext = buildOperationContext({
|
|
1487
|
+
schema,
|
|
1488
|
+
operationDocument,
|
|
1489
|
+
});
|
|
1490
|
+
|
|
1491
|
+
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1492
|
+
|
|
1493
|
+
const response = await executeQueryPlan(
|
|
1494
|
+
queryPlan,
|
|
1495
|
+
serviceMap,
|
|
1496
|
+
buildRequestContext(),
|
|
1497
|
+
operationContext,
|
|
1498
|
+
);
|
|
1499
|
+
|
|
1500
|
+
expect(response.data).toMatchObject({});
|
|
1501
|
+
expect(queryPlan).not.toCallService('accounts');
|
|
1502
|
+
});
|
|
1503
|
+
|
|
1504
|
+
describe('@skip and @include combinations', () => {
|
|
1505
|
+
it(`include: false, skip: false`, async () => {
|
|
1506
|
+
const operationDocument = gql`
|
|
1507
|
+
query TopReviews($shouldInclude: Boolean!, $shouldSkip: Boolean!) {
|
|
1508
|
+
topReviews @include(if: $shouldInclude) @skip(if: $shouldSkip) {
|
|
1509
|
+
body
|
|
1510
|
+
}
|
|
1511
|
+
}
|
|
1512
|
+
`;
|
|
1513
|
+
|
|
1514
|
+
const operationContext = buildOperationContext({
|
|
1515
|
+
schema,
|
|
1516
|
+
operationDocument,
|
|
1517
|
+
});
|
|
1518
|
+
|
|
1519
|
+
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1520
|
+
|
|
1521
|
+
const variables = { shouldInclude: false, shouldSkip: false };
|
|
1522
|
+
const response = await executeQueryPlan(
|
|
1523
|
+
queryPlan,
|
|
1524
|
+
serviceMap,
|
|
1525
|
+
buildRequestContext(variables),
|
|
1526
|
+
operationContext,
|
|
1527
|
+
);
|
|
1528
|
+
|
|
1529
|
+
expect(response.data).toMatchObject({});
|
|
1530
|
+
expect({ queryPlan, variables }).not.toCallService('reviews');
|
|
1531
|
+
});
|
|
1532
|
+
|
|
1533
|
+
it(`include: false, skip: true`, async () => {
|
|
1534
|
+
const operationDocument = gql`
|
|
1535
|
+
query TopReviews($shouldInclude: Boolean!, $shouldSkip: Boolean!) {
|
|
1536
|
+
topReviews @include(if: $shouldInclude) @skip(if: $shouldSkip) {
|
|
1537
|
+
body
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
`;
|
|
1541
|
+
|
|
1542
|
+
const operationContext = buildOperationContext({
|
|
1543
|
+
schema,
|
|
1544
|
+
operationDocument,
|
|
1545
|
+
});
|
|
1546
|
+
|
|
1547
|
+
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1548
|
+
|
|
1549
|
+
const variables = { shouldInclude: false, shouldSkip: true };
|
|
1550
|
+
const response = await executeQueryPlan(
|
|
1551
|
+
queryPlan,
|
|
1552
|
+
serviceMap,
|
|
1553
|
+
buildRequestContext(variables),
|
|
1554
|
+
operationContext,
|
|
1555
|
+
);
|
|
1556
|
+
|
|
1557
|
+
expect(response.data).toMatchObject({});
|
|
1558
|
+
expect({ queryPlan, variables }).not.toCallService('reviews');
|
|
1559
|
+
});
|
|
1560
|
+
|
|
1561
|
+
it(`include: true, skip: false`, async () => {
|
|
1562
|
+
const operationDocument = gql`
|
|
1563
|
+
query TopReviews($shouldInclude: Boolean!, $shouldSkip: Boolean!) {
|
|
1564
|
+
topReviews(first: 2) @include(if: $shouldInclude) @skip(if: $shouldSkip) {
|
|
1565
|
+
body
|
|
1566
|
+
}
|
|
1567
|
+
}
|
|
1568
|
+
`;
|
|
1569
|
+
|
|
1570
|
+
const operationContext = buildOperationContext({
|
|
1571
|
+
schema,
|
|
1572
|
+
operationDocument,
|
|
1573
|
+
});
|
|
1574
|
+
|
|
1575
|
+
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1576
|
+
|
|
1577
|
+
const variables = { shouldInclude: true, shouldSkip: false };
|
|
1578
|
+
const response = await executeQueryPlan(
|
|
1579
|
+
queryPlan,
|
|
1580
|
+
serviceMap,
|
|
1581
|
+
buildRequestContext(variables),
|
|
1582
|
+
operationContext,
|
|
1583
|
+
);
|
|
1584
|
+
|
|
1585
|
+
expect(response.data).toMatchObject({
|
|
1586
|
+
topReviews: [
|
|
1587
|
+
{
|
|
1588
|
+
body: 'Love it!',
|
|
1589
|
+
},
|
|
1590
|
+
{
|
|
1591
|
+
body: 'Too expensive.',
|
|
1592
|
+
},
|
|
1593
|
+
],
|
|
1594
|
+
});
|
|
1595
|
+
expect({ queryPlan, variables }).toCallService('reviews');
|
|
1596
|
+
});
|
|
1597
|
+
|
|
1598
|
+
it(`include: true, skip: true`, async () => {
|
|
1599
|
+
const operationDocument = gql`
|
|
1600
|
+
query TopReviews($shouldInclude: Boolean!, $shouldSkip: Boolean!) {
|
|
1601
|
+
topReviews @include(if: $shouldInclude) @skip(if: $shouldSkip) {
|
|
1602
|
+
body
|
|
1603
|
+
}
|
|
1604
|
+
}
|
|
1605
|
+
`;
|
|
1606
|
+
|
|
1607
|
+
const operationContext = buildOperationContext({
|
|
1608
|
+
schema,
|
|
1609
|
+
operationDocument,
|
|
1610
|
+
});
|
|
1611
|
+
|
|
1612
|
+
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1613
|
+
|
|
1614
|
+
const variables = { shouldInclude: true, shouldSkip: true };
|
|
1615
|
+
const response = await executeQueryPlan(
|
|
1616
|
+
queryPlan,
|
|
1617
|
+
serviceMap,
|
|
1618
|
+
buildRequestContext(variables),
|
|
1619
|
+
operationContext,
|
|
1620
|
+
);
|
|
1621
|
+
|
|
1622
|
+
expect(response.data).toMatchObject({});
|
|
1623
|
+
expect(queryPlan).toMatchInlineSnapshot(`
|
|
1624
|
+
QueryPlan {
|
|
1625
|
+
Fetch(service: "reviews", inclusionConditions: [{ include: "shouldInclude", skip: "shouldSkip" }]) {
|
|
1626
|
+
{
|
|
1627
|
+
topReviews @include(if: $shouldInclude) @skip(if: $shouldSkip) {
|
|
1628
|
+
body
|
|
1629
|
+
}
|
|
1630
|
+
}
|
|
1631
|
+
},
|
|
1632
|
+
}
|
|
1633
|
+
`);
|
|
1634
|
+
|
|
1635
|
+
expect({ queryPlan, variables }).not.toCallService('reviews');
|
|
1636
|
+
});
|
|
1637
|
+
});
|
|
1638
|
+
});
|
|
1382
1639
|
});
|
|
@@ -6,11 +6,10 @@ import {
|
|
|
6
6
|
import { GraphQLRequest, GraphQLExecutionResult, Logger } from 'apollo-server-types';
|
|
7
7
|
import {
|
|
8
8
|
composeAndValidate,
|
|
9
|
-
buildSubgraphSchema,
|
|
10
9
|
ServiceDefinition,
|
|
11
10
|
compositionHasErrors,
|
|
12
11
|
} from '@apollo/federation';
|
|
13
|
-
|
|
12
|
+
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
14
13
|
import {
|
|
15
14
|
executeQueryPlan,
|
|
16
15
|
buildOperationContext,
|
|
@@ -111,7 +110,11 @@ export function getTestingSupergraphSdl(services: typeof fixtures = fixtures) {
|
|
|
111
110
|
if (!compositionHasErrors(compositionResult)) {
|
|
112
111
|
return compositionResult.supergraphSdl;
|
|
113
112
|
}
|
|
114
|
-
throw new Error(
|
|
113
|
+
throw new Error(
|
|
114
|
+
`Testing fixtures don't compose properly!\n${compositionResult.errors.join(
|
|
115
|
+
'\t\n',
|
|
116
|
+
)}`,
|
|
117
|
+
);
|
|
115
118
|
}
|
|
116
119
|
|
|
117
120
|
export function wait(ms: number) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { GraphQLSchemaModule } from 'apollo-graphql';
|
|
2
|
-
import {
|
|
2
|
+
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
3
3
|
import { ApolloServer } from 'apollo-server';
|
|
4
4
|
import fetch from 'node-fetch';
|
|
5
5
|
import { ApolloGateway } from '../..';
|
|
@@ -7,7 +7,7 @@ import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
|
7
7
|
import { ApolloServerPluginInlineTrace } from 'apollo-server-core';
|
|
8
8
|
|
|
9
9
|
async function startFederatedServer(modules: GraphQLSchemaModule[]) {
|
|
10
|
-
const schema =
|
|
10
|
+
const schema = buildSubgraphSchema(modules);
|
|
11
11
|
const server = new ApolloServer({
|
|
12
12
|
schema,
|
|
13
13
|
// Manually installing the inline trace plugin means it doesn't log a message.
|
|
@@ -4,7 +4,7 @@ import {fixtures, spanSerializer} from 'apollo-federation-integration-testsuite'
|
|
|
4
4
|
import {fetch} from '../../__mocks__/apollo-server-env';
|
|
5
5
|
import {InMemorySpanExporter, SimpleSpanProcessor} from '@opentelemetry/tracing'
|
|
6
6
|
import {NodeTracerProvider} from '@opentelemetry/node';
|
|
7
|
-
import { buildSubgraphSchema } from '@apollo/
|
|
7
|
+
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
8
8
|
|
|
9
9
|
expect.addSnapshotSerializer(spanSerializer);
|
|
10
10
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import gql from 'graphql-tag';
|
|
2
2
|
import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
|
|
3
|
-
import { buildSubgraphSchema } from '@apollo/
|
|
3
|
+
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
4
4
|
|
|
5
5
|
import { LocalGraphQLDataSource } from '../../datasources/LocalGraphQLDataSource';
|
|
6
6
|
import { ApolloGateway } from '../../';
|
|
@@ -2,7 +2,7 @@ import { gunzipSync } from 'zlib';
|
|
|
2
2
|
import nock from 'nock';
|
|
3
3
|
import { GraphQLSchemaModule } from 'apollo-graphql';
|
|
4
4
|
import gql from 'graphql-tag';
|
|
5
|
-
import { buildSubgraphSchema } from '@apollo/
|
|
5
|
+
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
6
6
|
import { ApolloServer } from 'apollo-server';
|
|
7
7
|
import { ApolloServerPluginUsageReporting } from 'apollo-server-core';
|
|
8
8
|
import { execute, toPromise } from 'apollo-link';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { execute } from '../execution-utils';
|
|
2
2
|
import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
|
|
3
|
-
import { buildSubgraphSchema } from '@apollo/
|
|
3
|
+
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
4
4
|
import { LocalGraphQLDataSource } from '../../datasources/LocalGraphQLDataSource';
|
|
5
5
|
import { ApolloGateway } from '../../';
|
|
6
6
|
import { fixtures } from 'apollo-federation-integration-testsuite';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { LocalGraphQLDataSource } from '../LocalGraphQLDataSource';
|
|
2
|
-
import { buildSubgraphSchema } from '@apollo/
|
|
2
|
+
import { buildSubgraphSchema } from '@apollo/subgraph';
|
|
3
3
|
import gql from 'graphql-tag';
|
|
4
4
|
import { GraphQLResolverMap } from 'apollo-graphql';
|
|
5
5
|
import { GraphQLRequestContext } from 'apollo-server-types';
|
package/src/executeQueryPlan.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
GraphQLExecutionResult,
|
|
3
3
|
GraphQLRequestContext,
|
|
4
|
+
VariableValues,
|
|
4
5
|
} from 'apollo-server-types';
|
|
5
6
|
import { Headers } from 'apollo-server-env';
|
|
6
7
|
import {
|
|
@@ -232,6 +233,10 @@ async function executeNode<TContext>(
|
|
|
232
233
|
});
|
|
233
234
|
}
|
|
234
235
|
case 'Fetch': {
|
|
236
|
+
if (shouldSkipFetchNode(node, context.requestContext.request.variables)) {
|
|
237
|
+
return new Trace.QueryPlanNode();
|
|
238
|
+
}
|
|
239
|
+
|
|
235
240
|
const traceNode = new Trace.QueryPlanNode.FetchNode({
|
|
236
241
|
serviceName: node.serviceName,
|
|
237
242
|
// executeFetch will fill in the other fields if desired.
|
|
@@ -252,6 +257,31 @@ async function executeNode<TContext>(
|
|
|
252
257
|
}
|
|
253
258
|
}
|
|
254
259
|
|
|
260
|
+
export function shouldSkipFetchNode(
|
|
261
|
+
node: FetchNode,
|
|
262
|
+
variables: VariableValues = {},
|
|
263
|
+
) {
|
|
264
|
+
if (!node.inclusionConditions) return false;
|
|
265
|
+
|
|
266
|
+
return node.inclusionConditions.every((conditionals) => {
|
|
267
|
+
function resolveConditionalValue(conditional: 'skip' | 'include') {
|
|
268
|
+
const conditionalType = typeof conditionals[conditional];
|
|
269
|
+
if (conditionalType === 'boolean') {
|
|
270
|
+
return conditionals[conditional] as boolean;
|
|
271
|
+
} else if (conditionalType === 'string') {
|
|
272
|
+
return variables[conditionals[conditional] as string] as boolean;
|
|
273
|
+
} else {
|
|
274
|
+
return null;
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const includeValue = resolveConditionalValue('include');
|
|
279
|
+
const skipValue = resolveConditionalValue('skip');
|
|
280
|
+
|
|
281
|
+
return includeValue === false || skipValue === true;
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
|
|
255
285
|
async function executeFetch<TContext>(
|
|
256
286
|
context: ExecutionContext<TContext>,
|
|
257
287
|
fetch: FetchNode,
|
|
@@ -308,17 +338,17 @@ async function executeFetch<TContext>(
|
|
|
308
338
|
const representations: ResultMap[] = [];
|
|
309
339
|
const representationToEntity: number[] = [];
|
|
310
340
|
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
341
|
+
entities.forEach((entity, index) => {
|
|
342
|
+
const representation = executeSelectionSet(
|
|
343
|
+
context.operationContext,
|
|
344
|
+
entity,
|
|
345
|
+
requires,
|
|
346
|
+
);
|
|
347
|
+
if (representation && representation[TypeNameMetaFieldDef.name]) {
|
|
348
|
+
representations.push(representation);
|
|
349
|
+
representationToEntity.push(index);
|
|
350
|
+
}
|
|
351
|
+
});
|
|
322
352
|
|
|
323
353
|
// If there are no representations, that means the type conditions in
|
|
324
354
|
// the requires don't match any entities.
|
package/src/index.ts
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { deprecate } from 'util';
|
|
1
2
|
import { GraphQLService, Unsubscriber } from 'apollo-server-core';
|
|
2
3
|
import {
|
|
3
4
|
GraphQLExecutionResult,
|
|
@@ -117,7 +118,10 @@ export function getDefaultFetcher() {
|
|
|
117
118
|
* TODO(trevor:cloudconfig): Stop exporting this
|
|
118
119
|
* @deprecated This will be removed in a future version of @apollo/gateway
|
|
119
120
|
*/
|
|
120
|
-
export const getDefaultGcsFetcher =
|
|
121
|
+
export const getDefaultGcsFetcher = deprecate(
|
|
122
|
+
getDefaultFetcher,
|
|
123
|
+
`'getDefaultGcsFetcher' is deprecated. Use 'getDefaultFetcher' instead.`,
|
|
124
|
+
);
|
|
121
125
|
/**
|
|
122
126
|
* TODO(trevor:cloudconfig): Stop exporting this
|
|
123
127
|
* @deprecated This will be removed in a future version of @apollo/gateway
|
|
@@ -1270,6 +1274,11 @@ export class ApolloGateway implements GraphQLService {
|
|
|
1270
1274
|
}
|
|
1271
1275
|
}
|
|
1272
1276
|
|
|
1277
|
+
ApolloGateway.prototype.onSchemaChange = deprecate(
|
|
1278
|
+
ApolloGateway.prototype.onSchemaChange,
|
|
1279
|
+
`'ApolloGateway.prototype.onSchemaChange' is deprecated. Use 'ApolloGateway.prototype.onSchemaLoadOrUpdate' instead.`,
|
|
1280
|
+
);
|
|
1281
|
+
|
|
1273
1282
|
function approximateObjectSize<T>(obj: T): number {
|
|
1274
1283
|
return Buffer.byteLength(JSON.stringify(obj), 'utf8');
|
|
1275
1284
|
}
|
package/src/utilities/graphql.ts
CHANGED
|
@@ -8,12 +8,8 @@ import {
|
|
|
8
8
|
Kind,
|
|
9
9
|
ListTypeNode,
|
|
10
10
|
NamedTypeNode,
|
|
11
|
-
OperationDefinitionNode,
|
|
12
|
-
parse,
|
|
13
|
-
SelectionNode,
|
|
14
11
|
TypeNode,
|
|
15
12
|
} from 'graphql';
|
|
16
|
-
import { assert } from './assert';
|
|
17
13
|
|
|
18
14
|
export function getResponseName(node: FieldNode): string {
|
|
19
15
|
return node.alias ? node.alias.value : node.name.value;
|
|
@@ -41,21 +37,3 @@ export function astFromType(type: GraphQLType): TypeNode {
|
|
|
41
37
|
};
|
|
42
38
|
}
|
|
43
39
|
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* For lack of a "home of federation utilities", this function is copy/pasted
|
|
47
|
-
* verbatim across the federation, gateway, and query-planner packages. Any changes
|
|
48
|
-
* made here should be reflected in the other two locations as well.
|
|
49
|
-
*
|
|
50
|
-
* @param source A string representing a FieldSet
|
|
51
|
-
* @returns A parsed FieldSet
|
|
52
|
-
*/
|
|
53
|
-
export function parseSelections(source: string): ReadonlyArray<SelectionNode> {
|
|
54
|
-
const parsed = parse(`{${source}}`);
|
|
55
|
-
assert(
|
|
56
|
-
parsed.definitions.length === 1,
|
|
57
|
-
`Invalid FieldSet provided: '${source}'. FieldSets may not contain operations within them.`,
|
|
58
|
-
);
|
|
59
|
-
return (parsed.definitions[0] as OperationDefinitionNode).selectionSet
|
|
60
|
-
.selections;
|
|
61
|
-
}
|