@apollo/gateway 0.43.1 → 2.0.0-alpha.1
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 +2 -4
- package/LICENSE +95 -0
- package/dist/executeQueryPlan.d.ts +2 -3
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +2 -27
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +22 -41
- package/dist/index.js.map +1 -1
- package/package.json +7 -5
- package/src/__tests__/build-query-plan.feature +34 -24
- package/src/__tests__/buildQueryPlan.test.ts +76 -840
- package/src/__tests__/executeQueryPlan.test.ts +543 -537
- package/src/__tests__/execution-utils.ts +20 -26
- package/src/__tests__/gateway/lifecycle-hooks.test.ts +26 -4
- package/src/__tests__/gateway/reporting.test.ts +0 -14
- package/src/__tests__/integration/abstract-types.test.ts +40 -52
- package/src/__tests__/integration/boolean.test.ts +3 -1
- package/src/__tests__/integration/complex-key.test.ts +40 -55
- package/src/__tests__/integration/configuration.test.ts +5 -5
- package/src/__tests__/integration/custom-directives.test.ts +8 -2
- package/src/__tests__/integration/multiple-key.test.ts +10 -11
- package/src/__tests__/integration/requires.test.ts +2 -2
- package/src/__tests__/integration/scope.test.ts +19 -30
- package/src/__tests__/integration/value-types.test.ts +31 -31
- package/src/__tests__/loadSupergraphSdlFromStorage.test.ts +217 -142
- package/src/__tests__/queryPlanCucumber.test.ts +4 -18
- package/src/core/__tests__/core.test.ts +1 -0
- package/src/executeQueryPlan.ts +1 -32
- package/src/index.ts +39 -80
- package/src/utilities/__tests__/cleanErrorOfInaccessibleElements.test.ts +4 -4
- package/LICENSE.md +0 -21
- package/dist/core/index.d.ts +0 -13
- package/dist/core/index.d.ts.map +0 -1
- package/dist/core/index.js +0 -43
- package/dist/core/index.js.map +0 -1
- package/src/__tests__/build-query-plan-fragmentization.feature +0 -282
- package/src/core/index.ts +0 -51
|
@@ -2,12 +2,11 @@ import {
|
|
|
2
2
|
buildClientSchema,
|
|
3
3
|
getIntrospectionQuery,
|
|
4
4
|
GraphQLObjectType,
|
|
5
|
-
GraphQLSchema,
|
|
6
5
|
print,
|
|
7
6
|
} from 'graphql';
|
|
8
7
|
import { addResolversToSchema, GraphQLResolverMap } from 'apollo-graphql';
|
|
9
8
|
import gql from 'graphql-tag';
|
|
10
|
-
import {
|
|
9
|
+
import { GraphQLExecutionResult, GraphQLRequestContext } from 'apollo-server-types';
|
|
11
10
|
import { AuthenticationError } from 'apollo-server-core';
|
|
12
11
|
import { buildOperationContext } from '../operationContext';
|
|
13
12
|
import { executeQueryPlan } from '../executeQueryPlan';
|
|
@@ -17,15 +16,54 @@ import {
|
|
|
17
16
|
queryPlanSerializer,
|
|
18
17
|
superGraphWithInaccessible,
|
|
19
18
|
} from 'apollo-federation-integration-testsuite';
|
|
20
|
-
import {
|
|
19
|
+
import { QueryPlan, QueryPlanner } from '@apollo/query-planner';
|
|
21
20
|
import { ApolloGateway } from '..';
|
|
22
21
|
import { ApolloServerBase as ApolloServer } from 'apollo-server-core';
|
|
23
22
|
import { getFederatedTestingSchema } from './execution-utils';
|
|
23
|
+
import { Schema, Operation, parseOperation, buildSchemaFromAST } from '@apollo/federation-internals';
|
|
24
24
|
|
|
25
25
|
expect.addSnapshotSerializer(astSerializer);
|
|
26
26
|
expect.addSnapshotSerializer(queryPlanSerializer);
|
|
27
27
|
|
|
28
28
|
describe('executeQueryPlan', () => {
|
|
29
|
+
let serviceMap: {
|
|
30
|
+
[serviceName: string]: LocalGraphQLDataSource;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
let parseOp = (operation: string, operationSchema?: Schema): Operation => {
|
|
34
|
+
return parseOperation((operationSchema ?? schema), operation);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
let buildPlan = (operation: string | Operation, operationQueryPlanner?: QueryPlanner, operationSchema?: Schema): QueryPlan => {
|
|
38
|
+
const op = typeof operation === 'string' ? parseOp(operation, operationSchema): operation;
|
|
39
|
+
return (operationQueryPlanner ?? queryPlanner).buildQueryPlan(op);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async function executePlan(
|
|
43
|
+
queryPlan: QueryPlan,
|
|
44
|
+
operation: Operation,
|
|
45
|
+
executeRequestContext?: GraphQLRequestContext,
|
|
46
|
+
executeSchema?: Schema,
|
|
47
|
+
executeServiceMap?: { [serviceName: string]: LocalGraphQLDataSource }
|
|
48
|
+
): Promise<GraphQLExecutionResult> {
|
|
49
|
+
const operationContext = buildOperationContext({
|
|
50
|
+
schema: (executeSchema ?? schema).toAPISchema().toGraphQLJSSchema(),
|
|
51
|
+
operationDocument: gql`${operation.toString()}`,
|
|
52
|
+
});
|
|
53
|
+
return executeQueryPlan(
|
|
54
|
+
queryPlan,
|
|
55
|
+
executeServiceMap ?? serviceMap,
|
|
56
|
+
executeRequestContext ?? buildRequestContext(),
|
|
57
|
+
operationContext,
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async function executeOperation(operationString: string, requestContext?: GraphQLRequestContext): Promise<GraphQLExecutionResult> {
|
|
62
|
+
const operation = parseOp(operationString);
|
|
63
|
+
const queryPlan = buildPlan(operation);
|
|
64
|
+
return executePlan(queryPlan, operation, requestContext);
|
|
65
|
+
}
|
|
66
|
+
|
|
29
67
|
function overrideResolversInService(
|
|
30
68
|
serviceName: string,
|
|
31
69
|
resolvers: GraphQLResolverMap,
|
|
@@ -40,10 +78,7 @@ describe('executeQueryPlan', () => {
|
|
|
40
78
|
return jest.spyOn(entitiesField, 'resolve');
|
|
41
79
|
}
|
|
42
80
|
|
|
43
|
-
let
|
|
44
|
-
[serviceName: string]: LocalGraphQLDataSource;
|
|
45
|
-
};
|
|
46
|
-
let schema: GraphQLSchema;
|
|
81
|
+
let schema: Schema;
|
|
47
82
|
let queryPlanner: QueryPlanner;
|
|
48
83
|
beforeEach(() => {
|
|
49
84
|
expect(
|
|
@@ -52,15 +87,13 @@ describe('executeQueryPlan', () => {
|
|
|
52
87
|
).not.toThrow();
|
|
53
88
|
});
|
|
54
89
|
|
|
55
|
-
function buildRequestContext(
|
|
56
|
-
variables: VariableValues = {},
|
|
57
|
-
): GraphQLRequestContext {
|
|
90
|
+
function buildRequestContext(): GraphQLRequestContext {
|
|
58
91
|
// @ts-ignore
|
|
59
92
|
return {
|
|
60
93
|
cache: undefined as any,
|
|
61
94
|
context: {},
|
|
62
95
|
request: {
|
|
63
|
-
variables,
|
|
96
|
+
variables: {},
|
|
64
97
|
},
|
|
65
98
|
};
|
|
66
99
|
}
|
|
@@ -78,21 +111,7 @@ describe('executeQueryPlan', () => {
|
|
|
78
111
|
}
|
|
79
112
|
`;
|
|
80
113
|
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
const operationContext = buildOperationContext({
|
|
84
|
-
schema,
|
|
85
|
-
operationDocument,
|
|
86
|
-
});
|
|
87
|
-
|
|
88
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
89
|
-
|
|
90
|
-
const response = await executeQueryPlan(
|
|
91
|
-
queryPlan,
|
|
92
|
-
serviceMap,
|
|
93
|
-
buildRequestContext(),
|
|
94
|
-
operationContext,
|
|
95
|
-
);
|
|
114
|
+
const response = await executeOperation(operationString);
|
|
96
115
|
|
|
97
116
|
expect(response).not.toHaveProperty('errors');
|
|
98
117
|
});
|
|
@@ -117,21 +136,7 @@ describe('executeQueryPlan', () => {
|
|
|
117
136
|
}
|
|
118
137
|
`;
|
|
119
138
|
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
const operationContext = buildOperationContext({
|
|
123
|
-
schema,
|
|
124
|
-
operationDocument,
|
|
125
|
-
});
|
|
126
|
-
|
|
127
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
128
|
-
|
|
129
|
-
const response = await executeQueryPlan(
|
|
130
|
-
queryPlan,
|
|
131
|
-
serviceMap,
|
|
132
|
-
buildRequestContext(),
|
|
133
|
-
operationContext,
|
|
134
|
-
);
|
|
139
|
+
const response = await executeOperation(operationString);
|
|
135
140
|
|
|
136
141
|
expect(response).toHaveProperty('data.me', null);
|
|
137
142
|
expect(response).toHaveProperty(
|
|
@@ -176,21 +181,7 @@ describe('executeQueryPlan', () => {
|
|
|
176
181
|
}
|
|
177
182
|
`;
|
|
178
183
|
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
const operationContext = buildOperationContext({
|
|
182
|
-
schema,
|
|
183
|
-
operationDocument,
|
|
184
|
-
});
|
|
185
|
-
|
|
186
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
187
|
-
|
|
188
|
-
const response = await executeQueryPlan(
|
|
189
|
-
queryPlan,
|
|
190
|
-
serviceMap,
|
|
191
|
-
buildRequestContext(),
|
|
192
|
-
operationContext,
|
|
193
|
-
);
|
|
184
|
+
const response = await executeOperation(operationString);
|
|
194
185
|
|
|
195
186
|
expect(accountsEntitiesResolverSpy).not.toHaveBeenCalled();
|
|
196
187
|
|
|
@@ -253,21 +244,7 @@ describe('executeQueryPlan', () => {
|
|
|
253
244
|
}
|
|
254
245
|
`;
|
|
255
246
|
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
const operationContext = buildOperationContext({
|
|
259
|
-
schema,
|
|
260
|
-
operationDocument,
|
|
261
|
-
});
|
|
262
|
-
|
|
263
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
264
|
-
|
|
265
|
-
const response = await executeQueryPlan(
|
|
266
|
-
queryPlan,
|
|
267
|
-
serviceMap,
|
|
268
|
-
buildRequestContext(),
|
|
269
|
-
operationContext,
|
|
270
|
-
);
|
|
247
|
+
const response = await executeOperation(operationString);
|
|
271
248
|
|
|
272
249
|
expect(accountsEntitiesResolverSpy).toHaveBeenCalledTimes(1);
|
|
273
250
|
expect(accountsEntitiesResolverSpy.mock.calls[0][1]).toEqual({
|
|
@@ -354,21 +331,7 @@ describe('executeQueryPlan', () => {
|
|
|
354
331
|
}
|
|
355
332
|
`;
|
|
356
333
|
|
|
357
|
-
const
|
|
358
|
-
|
|
359
|
-
const operationContext = buildOperationContext({
|
|
360
|
-
schema,
|
|
361
|
-
operationDocument,
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
365
|
-
|
|
366
|
-
const response = await executeQueryPlan(
|
|
367
|
-
queryPlan,
|
|
368
|
-
serviceMap,
|
|
369
|
-
buildRequestContext(),
|
|
370
|
-
operationContext,
|
|
371
|
-
);
|
|
334
|
+
const response = await executeOperation(operationString);
|
|
372
335
|
|
|
373
336
|
expect(reviewsEntitiesResolverSpy).not.toHaveBeenCalled();
|
|
374
337
|
|
|
@@ -402,21 +365,7 @@ describe('executeQueryPlan', () => {
|
|
|
402
365
|
}
|
|
403
366
|
`;
|
|
404
367
|
|
|
405
|
-
const
|
|
406
|
-
|
|
407
|
-
const operationContext = buildOperationContext({
|
|
408
|
-
schema,
|
|
409
|
-
operationDocument,
|
|
410
|
-
});
|
|
411
|
-
|
|
412
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
413
|
-
|
|
414
|
-
const response = await executeQueryPlan(
|
|
415
|
-
queryPlan,
|
|
416
|
-
serviceMap,
|
|
417
|
-
buildRequestContext(),
|
|
418
|
-
operationContext,
|
|
419
|
-
);
|
|
368
|
+
const response = await executeOperation(operationString);
|
|
420
369
|
|
|
421
370
|
expect(reviewsEntitiesResolverSpy).toHaveBeenCalledTimes(1);
|
|
422
371
|
expect(reviewsEntitiesResolverSpy.mock.calls[0][1]).toEqual({
|
|
@@ -476,21 +425,7 @@ describe('executeQueryPlan', () => {
|
|
|
476
425
|
}
|
|
477
426
|
`;
|
|
478
427
|
|
|
479
|
-
const
|
|
480
|
-
|
|
481
|
-
const operationContext = buildOperationContext({
|
|
482
|
-
schema,
|
|
483
|
-
operationDocument,
|
|
484
|
-
});
|
|
485
|
-
|
|
486
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
487
|
-
|
|
488
|
-
const response = await executeQueryPlan(
|
|
489
|
-
queryPlan,
|
|
490
|
-
serviceMap,
|
|
491
|
-
buildRequestContext(),
|
|
492
|
-
operationContext,
|
|
493
|
-
);
|
|
428
|
+
const response = await executeOperation(operationString);
|
|
494
429
|
|
|
495
430
|
expect(response).toHaveProperty('data.me', null);
|
|
496
431
|
expect(response).toHaveProperty('data.topReviews', expect.any(Array));
|
|
@@ -513,21 +448,7 @@ describe('executeQueryPlan', () => {
|
|
|
513
448
|
}
|
|
514
449
|
`;
|
|
515
450
|
|
|
516
|
-
const
|
|
517
|
-
|
|
518
|
-
const operationContext = buildOperationContext({
|
|
519
|
-
schema,
|
|
520
|
-
operationDocument,
|
|
521
|
-
});
|
|
522
|
-
|
|
523
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
524
|
-
|
|
525
|
-
const response = await executeQueryPlan(
|
|
526
|
-
queryPlan,
|
|
527
|
-
serviceMap,
|
|
528
|
-
buildRequestContext(),
|
|
529
|
-
operationContext,
|
|
530
|
-
);
|
|
451
|
+
const response = await executeOperation(operationString);
|
|
531
452
|
|
|
532
453
|
expect(response).toHaveProperty('data.me', null);
|
|
533
454
|
expect(response).toHaveProperty('data.topReviews', expect.any(Array));
|
|
@@ -549,21 +470,7 @@ describe('executeQueryPlan', () => {
|
|
|
549
470
|
}
|
|
550
471
|
`;
|
|
551
472
|
|
|
552
|
-
const
|
|
553
|
-
|
|
554
|
-
const operationContext = buildOperationContext({
|
|
555
|
-
schema,
|
|
556
|
-
operationDocument,
|
|
557
|
-
});
|
|
558
|
-
|
|
559
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
560
|
-
|
|
561
|
-
const response = await executeQueryPlan(
|
|
562
|
-
queryPlan,
|
|
563
|
-
serviceMap,
|
|
564
|
-
buildRequestContext(),
|
|
565
|
-
operationContext,
|
|
566
|
-
);
|
|
473
|
+
const response = await executeOperation(operationString);
|
|
567
474
|
|
|
568
475
|
expect(response.data).toMatchInlineSnapshot(`
|
|
569
476
|
Object {
|
|
@@ -642,24 +549,9 @@ describe('executeQueryPlan', () => {
|
|
|
642
549
|
}
|
|
643
550
|
`;
|
|
644
551
|
|
|
645
|
-
const operationDocument = gql(operationString);
|
|
646
|
-
|
|
647
|
-
const operationContext = buildOperationContext({
|
|
648
|
-
schema,
|
|
649
|
-
operationDocument,
|
|
650
|
-
});
|
|
651
|
-
|
|
652
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
653
|
-
|
|
654
552
|
const requestContext = buildRequestContext();
|
|
655
553
|
requestContext.request.variables = { first: 3 };
|
|
656
|
-
|
|
657
|
-
const response = await executeQueryPlan(
|
|
658
|
-
queryPlan,
|
|
659
|
-
serviceMap,
|
|
660
|
-
requestContext,
|
|
661
|
-
operationContext,
|
|
662
|
-
);
|
|
554
|
+
const response = await executeOperation(operationString, requestContext);
|
|
663
555
|
|
|
664
556
|
expect(response.data).toMatchInlineSnapshot(`
|
|
665
557
|
Object {
|
|
@@ -741,24 +633,9 @@ describe('executeQueryPlan', () => {
|
|
|
741
633
|
}
|
|
742
634
|
`;
|
|
743
635
|
|
|
744
|
-
const operationDocument = gql(operationString);
|
|
745
|
-
|
|
746
|
-
const operationContext = buildOperationContext({
|
|
747
|
-
schema,
|
|
748
|
-
operationDocument,
|
|
749
|
-
});
|
|
750
|
-
|
|
751
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
752
|
-
|
|
753
636
|
const requestContext = buildRequestContext();
|
|
754
637
|
requestContext.request.variables = { locale: 'en-US' };
|
|
755
|
-
|
|
756
|
-
const response = await executeQueryPlan(
|
|
757
|
-
queryPlan,
|
|
758
|
-
serviceMap,
|
|
759
|
-
requestContext,
|
|
760
|
-
operationContext,
|
|
761
|
-
);
|
|
638
|
+
const response = await executeOperation(operationString, requestContext);
|
|
762
639
|
|
|
763
640
|
expect(response.data).toMatchInlineSnapshot(`
|
|
764
641
|
Object {
|
|
@@ -819,20 +696,7 @@ describe('executeQueryPlan', () => {
|
|
|
819
696
|
});
|
|
820
697
|
|
|
821
698
|
it('can execute an introspection query', async () => {
|
|
822
|
-
const
|
|
823
|
-
schema,
|
|
824
|
-
operationDocument: gql`
|
|
825
|
-
${getIntrospectionQuery()}
|
|
826
|
-
`,
|
|
827
|
-
});
|
|
828
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
829
|
-
|
|
830
|
-
const response = await executeQueryPlan(
|
|
831
|
-
queryPlan,
|
|
832
|
-
serviceMap,
|
|
833
|
-
buildRequestContext(),
|
|
834
|
-
operationContext,
|
|
835
|
-
);
|
|
699
|
+
const response = await executeOperation(`${getIntrospectionQuery()}`);
|
|
836
700
|
|
|
837
701
|
expect(response.data).toHaveProperty('__schema');
|
|
838
702
|
expect(response.errors).toBeUndefined();
|
|
@@ -849,21 +713,7 @@ describe('executeQueryPlan', () => {
|
|
|
849
713
|
}
|
|
850
714
|
`;
|
|
851
715
|
|
|
852
|
-
const
|
|
853
|
-
|
|
854
|
-
const operationContext = buildOperationContext({
|
|
855
|
-
schema,
|
|
856
|
-
operationDocument,
|
|
857
|
-
});
|
|
858
|
-
|
|
859
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
860
|
-
|
|
861
|
-
const response = await executeQueryPlan(
|
|
862
|
-
queryPlan,
|
|
863
|
-
serviceMap,
|
|
864
|
-
buildRequestContext(),
|
|
865
|
-
operationContext,
|
|
866
|
-
);
|
|
716
|
+
const response = await executeOperation(operationString);
|
|
867
717
|
|
|
868
718
|
expect(response.data).toMatchInlineSnapshot(`
|
|
869
719
|
Object {
|
|
@@ -893,21 +743,7 @@ describe('executeQueryPlan', () => {
|
|
|
893
743
|
}
|
|
894
744
|
`;
|
|
895
745
|
|
|
896
|
-
const
|
|
897
|
-
|
|
898
|
-
const operationContext = buildOperationContext({
|
|
899
|
-
schema,
|
|
900
|
-
operationDocument,
|
|
901
|
-
});
|
|
902
|
-
|
|
903
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
904
|
-
|
|
905
|
-
const response = await executeQueryPlan(
|
|
906
|
-
queryPlan,
|
|
907
|
-
serviceMap,
|
|
908
|
-
buildRequestContext(),
|
|
909
|
-
operationContext,
|
|
910
|
-
);
|
|
746
|
+
const response = await executeOperation(operationString);
|
|
911
747
|
|
|
912
748
|
expect(response.data).toMatchInlineSnapshot(`
|
|
913
749
|
Object {
|
|
@@ -948,21 +784,7 @@ describe('executeQueryPlan', () => {
|
|
|
948
784
|
}
|
|
949
785
|
`;
|
|
950
786
|
|
|
951
|
-
const
|
|
952
|
-
|
|
953
|
-
const operationContext = buildOperationContext({
|
|
954
|
-
schema,
|
|
955
|
-
operationDocument,
|
|
956
|
-
});
|
|
957
|
-
|
|
958
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
959
|
-
|
|
960
|
-
const response = await executeQueryPlan(
|
|
961
|
-
queryPlan,
|
|
962
|
-
serviceMap,
|
|
963
|
-
buildRequestContext(),
|
|
964
|
-
operationContext,
|
|
965
|
-
);
|
|
787
|
+
const response = await executeOperation(operationString);
|
|
966
788
|
|
|
967
789
|
expect(response.data).toMatchInlineSnapshot(`
|
|
968
790
|
Object {
|
|
@@ -990,21 +812,7 @@ describe('executeQueryPlan', () => {
|
|
|
990
812
|
}
|
|
991
813
|
`;
|
|
992
814
|
|
|
993
|
-
const
|
|
994
|
-
|
|
995
|
-
const operationContext = buildOperationContext({
|
|
996
|
-
schema,
|
|
997
|
-
operationDocument,
|
|
998
|
-
});
|
|
999
|
-
|
|
1000
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1001
|
-
|
|
1002
|
-
const response = await executeQueryPlan(
|
|
1003
|
-
queryPlan,
|
|
1004
|
-
serviceMap,
|
|
1005
|
-
buildRequestContext(),
|
|
1006
|
-
operationContext,
|
|
1007
|
-
);
|
|
815
|
+
const response = await executeOperation(operationString);
|
|
1008
816
|
|
|
1009
817
|
expect(response.data).toMatchInlineSnapshot(`
|
|
1010
818
|
Object {
|
|
@@ -1045,21 +853,7 @@ describe('executeQueryPlan', () => {
|
|
|
1045
853
|
}
|
|
1046
854
|
`;
|
|
1047
855
|
|
|
1048
|
-
const
|
|
1049
|
-
|
|
1050
|
-
const operationContext = buildOperationContext({
|
|
1051
|
-
schema,
|
|
1052
|
-
operationDocument,
|
|
1053
|
-
});
|
|
1054
|
-
|
|
1055
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1056
|
-
|
|
1057
|
-
const response = await executeQueryPlan(
|
|
1058
|
-
queryPlan,
|
|
1059
|
-
serviceMap,
|
|
1060
|
-
buildRequestContext(),
|
|
1061
|
-
operationContext,
|
|
1062
|
-
);
|
|
856
|
+
const response = await executeOperation(operationString);
|
|
1063
857
|
|
|
1064
858
|
expect(response.errors).toMatchInlineSnapshot(`undefined`);
|
|
1065
859
|
|
|
@@ -1094,21 +888,7 @@ describe('executeQueryPlan', () => {
|
|
|
1094
888
|
}
|
|
1095
889
|
`;
|
|
1096
890
|
|
|
1097
|
-
const
|
|
1098
|
-
|
|
1099
|
-
const operationContext = buildOperationContext({
|
|
1100
|
-
schema,
|
|
1101
|
-
operationDocument,
|
|
1102
|
-
});
|
|
1103
|
-
|
|
1104
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1105
|
-
|
|
1106
|
-
const response = await executeQueryPlan(
|
|
1107
|
-
queryPlan,
|
|
1108
|
-
serviceMap,
|
|
1109
|
-
buildRequestContext(),
|
|
1110
|
-
operationContext,
|
|
1111
|
-
);
|
|
891
|
+
const response = await executeOperation(operationString);
|
|
1112
892
|
|
|
1113
893
|
expect(response.errors).toBeUndefined();
|
|
1114
894
|
|
|
@@ -1137,21 +917,7 @@ describe('executeQueryPlan', () => {
|
|
|
1137
917
|
}
|
|
1138
918
|
`;
|
|
1139
919
|
|
|
1140
|
-
const
|
|
1141
|
-
|
|
1142
|
-
const operationContext = buildOperationContext({
|
|
1143
|
-
schema,
|
|
1144
|
-
operationDocument,
|
|
1145
|
-
});
|
|
1146
|
-
|
|
1147
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1148
|
-
|
|
1149
|
-
const response = await executeQueryPlan(
|
|
1150
|
-
queryPlan,
|
|
1151
|
-
serviceMap,
|
|
1152
|
-
buildRequestContext(),
|
|
1153
|
-
operationContext,
|
|
1154
|
-
);
|
|
920
|
+
const response = await executeOperation(operationString);
|
|
1155
921
|
|
|
1156
922
|
expect(response.data).toMatchInlineSnapshot(`
|
|
1157
923
|
Object {
|
|
@@ -1183,23 +949,12 @@ describe('executeQueryPlan', () => {
|
|
|
1183
949
|
|
|
1184
950
|
describe('@inaccessible', () => {
|
|
1185
951
|
it(`should not include @inaccessible fields in introspection`, async () => {
|
|
1186
|
-
schema =
|
|
1187
|
-
queryPlanner = new QueryPlanner(schema);
|
|
952
|
+
schema = buildSchemaFromAST(superGraphWithInaccessible);
|
|
1188
953
|
|
|
1189
|
-
const
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
`,
|
|
1194
|
-
});
|
|
1195
|
-
const queryPlan = queryPlanner.buildQueryPlan(operationContext);
|
|
1196
|
-
|
|
1197
|
-
const response = await executeQueryPlan(
|
|
1198
|
-
queryPlan,
|
|
1199
|
-
serviceMap,
|
|
1200
|
-
buildRequestContext(),
|
|
1201
|
-
operationContext,
|
|
1202
|
-
);
|
|
954
|
+
const operation = parseOp(`${getIntrospectionQuery()}`, schema);
|
|
955
|
+
queryPlanner = new QueryPlanner(schema);
|
|
956
|
+
const queryPlan = queryPlanner.buildQueryPlan(operation);
|
|
957
|
+
const response = await executePlan(queryPlan, operation, undefined, schema);
|
|
1203
958
|
|
|
1204
959
|
expect(response.data).toHaveProperty('__schema');
|
|
1205
960
|
expect(response.errors).toBeUndefined();
|
|
@@ -1225,24 +980,14 @@ describe('executeQueryPlan', () => {
|
|
|
1225
980
|
}
|
|
1226
981
|
`;
|
|
1227
982
|
|
|
1228
|
-
const
|
|
1229
|
-
|
|
1230
|
-
schema = buildComposedSchema(superGraphWithInaccessible);
|
|
983
|
+
const operation = parseOp(operationString);
|
|
1231
984
|
|
|
1232
|
-
|
|
1233
|
-
schema,
|
|
1234
|
-
operationDocument,
|
|
1235
|
-
});
|
|
985
|
+
schema = buildSchemaFromAST(superGraphWithInaccessible);
|
|
1236
986
|
|
|
1237
987
|
queryPlanner = new QueryPlanner(schema);
|
|
1238
|
-
const queryPlan = queryPlanner.buildQueryPlan(
|
|
988
|
+
const queryPlan = queryPlanner.buildQueryPlan(operation);
|
|
1239
989
|
|
|
1240
|
-
const response = await
|
|
1241
|
-
queryPlan,
|
|
1242
|
-
serviceMap,
|
|
1243
|
-
buildRequestContext(),
|
|
1244
|
-
operationContext,
|
|
1245
|
-
);
|
|
990
|
+
const response = await executePlan(queryPlan, operation, undefined, schema);
|
|
1246
991
|
|
|
1247
992
|
expect(response.data).toMatchInlineSnapshot(`
|
|
1248
993
|
Object {
|
|
@@ -1349,26 +1094,16 @@ describe('executeQueryPlan', () => {
|
|
|
1349
1094
|
}
|
|
1350
1095
|
`;
|
|
1351
1096
|
|
|
1352
|
-
const
|
|
1097
|
+
const operation = parseOp(operationString);
|
|
1353
1098
|
|
|
1354
1099
|
// Vehicle ID #1 is a "Car" type.
|
|
1355
1100
|
// This supergraph marks the "Car" type as inaccessible.
|
|
1356
|
-
schema =
|
|
1357
|
-
|
|
1358
|
-
const operationContext = buildOperationContext({
|
|
1359
|
-
schema,
|
|
1360
|
-
operationDocument,
|
|
1361
|
-
});
|
|
1101
|
+
schema = buildSchemaFromAST(superGraphWithInaccessible);
|
|
1362
1102
|
|
|
1363
1103
|
queryPlanner = new QueryPlanner(schema);
|
|
1364
|
-
const queryPlan = queryPlanner.buildQueryPlan(
|
|
1104
|
+
const queryPlan = queryPlanner.buildQueryPlan(operation);
|
|
1365
1105
|
|
|
1366
|
-
const response = await
|
|
1367
|
-
queryPlan,
|
|
1368
|
-
serviceMap,
|
|
1369
|
-
buildRequestContext(),
|
|
1370
|
-
operationContext,
|
|
1371
|
-
);
|
|
1106
|
+
const response = await executePlan(queryPlan, operation, undefined, schema);
|
|
1372
1107
|
|
|
1373
1108
|
expect(response.data?.vehicle).toEqual(null);
|
|
1374
1109
|
expect(response.errors).toBeUndefined();
|
|
@@ -1381,259 +1116,530 @@ describe('executeQueryPlan', () => {
|
|
|
1381
1116
|
});
|
|
1382
1117
|
});
|
|
1383
1118
|
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
body
|
|
1390
|
-
}
|
|
1391
|
-
}
|
|
1392
|
-
`;
|
|
1119
|
+
it('can query other subgraphs when the Query type is the type of a field', async () => {
|
|
1120
|
+
const s1 = gql`
|
|
1121
|
+
type Query {
|
|
1122
|
+
getA: A
|
|
1123
|
+
}
|
|
1393
1124
|
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1125
|
+
type A {
|
|
1126
|
+
q: Query
|
|
1127
|
+
}
|
|
1128
|
+
`;
|
|
1398
1129
|
|
|
1399
|
-
|
|
1130
|
+
const s2 = gql`
|
|
1131
|
+
type Query {
|
|
1132
|
+
one: Int
|
|
1133
|
+
}
|
|
1134
|
+
`;
|
|
1400
1135
|
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1136
|
+
const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([
|
|
1137
|
+
{ name: 'S1', typeDefs: s1 },
|
|
1138
|
+
{ name: 'S2', typeDefs: s2 }
|
|
1139
|
+
]);
|
|
1140
|
+
|
|
1141
|
+
addResolversToSchema(serviceMap['S1'].schema, {
|
|
1142
|
+
Query: {
|
|
1143
|
+
getA() {
|
|
1144
|
+
return {
|
|
1145
|
+
getA: {
|
|
1146
|
+
q: null
|
|
1147
|
+
}
|
|
1148
|
+
};
|
|
1149
|
+
},
|
|
1150
|
+
},
|
|
1151
|
+
A: {
|
|
1152
|
+
q() {
|
|
1153
|
+
return Object.create(null);
|
|
1154
|
+
}
|
|
1155
|
+
}
|
|
1156
|
+
});
|
|
1407
1157
|
|
|
1408
|
-
|
|
1409
|
-
|
|
1158
|
+
addResolversToSchema(serviceMap['S2'].schema, {
|
|
1159
|
+
Query: {
|
|
1160
|
+
one() {
|
|
1161
|
+
return 1;
|
|
1162
|
+
},
|
|
1163
|
+
},
|
|
1410
1164
|
});
|
|
1411
1165
|
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1166
|
+
const operation = parseOp(`
|
|
1167
|
+
query {
|
|
1168
|
+
getA {
|
|
1169
|
+
q {
|
|
1170
|
+
one
|
|
1417
1171
|
}
|
|
1418
1172
|
}
|
|
1419
|
-
|
|
1173
|
+
}
|
|
1174
|
+
`, schema);
|
|
1420
1175
|
|
|
1421
|
-
|
|
1422
|
-
schema,
|
|
1423
|
-
operationDocument,
|
|
1424
|
-
});
|
|
1176
|
+
const queryPlan = buildPlan(operation, queryPlanner);
|
|
1425
1177
|
|
|
1426
|
-
|
|
1178
|
+
expect(queryPlan).toMatchInlineSnapshot(`
|
|
1179
|
+
QueryPlan {
|
|
1180
|
+
Sequence {
|
|
1181
|
+
Fetch(service: "S1") {
|
|
1182
|
+
{
|
|
1183
|
+
getA {
|
|
1184
|
+
q {
|
|
1185
|
+
__typename
|
|
1186
|
+
}
|
|
1187
|
+
}
|
|
1188
|
+
}
|
|
1189
|
+
},
|
|
1190
|
+
Flatten(path: "getA.q") {
|
|
1191
|
+
Fetch(service: "S2") {
|
|
1192
|
+
{
|
|
1193
|
+
... on Query {
|
|
1194
|
+
one
|
|
1195
|
+
}
|
|
1196
|
+
}
|
|
1197
|
+
},
|
|
1198
|
+
},
|
|
1199
|
+
},
|
|
1200
|
+
}
|
|
1201
|
+
`);
|
|
1427
1202
|
|
|
1428
|
-
|
|
1429
|
-
const response = await executeQueryPlan(
|
|
1430
|
-
queryPlan,
|
|
1431
|
-
serviceMap,
|
|
1432
|
-
buildRequestContext(variables),
|
|
1433
|
-
operationContext,
|
|
1434
|
-
);
|
|
1203
|
+
const response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
|
|
1435
1204
|
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1205
|
+
expect(response.data).toMatchInlineSnapshot(`
|
|
1206
|
+
Object {
|
|
1207
|
+
"getA": Object {
|
|
1208
|
+
"q": Object {
|
|
1209
|
+
"one": 1,
|
|
1210
|
+
},
|
|
1211
|
+
},
|
|
1212
|
+
}
|
|
1213
|
+
`);
|
|
1214
|
+
})
|
|
1439
1215
|
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1216
|
+
describe('interfaces on interfaces', () => {
|
|
1217
|
+
it('can execute queries on an interface only implemented by other interfaces', async () => {
|
|
1218
|
+
const s1 = gql`
|
|
1219
|
+
type Query {
|
|
1220
|
+
allValues: [TopInterface!]!
|
|
1221
|
+
}
|
|
1222
|
+
|
|
1223
|
+
interface TopInterface {
|
|
1224
|
+
a: Int
|
|
1225
|
+
}
|
|
1226
|
+
|
|
1227
|
+
interface SubInterface1 implements TopInterface {
|
|
1228
|
+
a: Int
|
|
1229
|
+
b: String
|
|
1230
|
+
}
|
|
1231
|
+
|
|
1232
|
+
interface SubInterface2 implements TopInterface {
|
|
1233
|
+
a: Int
|
|
1234
|
+
c: String
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
type T1 implements SubInterface1 & TopInterface {
|
|
1238
|
+
a: Int
|
|
1239
|
+
b: String
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
type T2 implements SubInterface1 & TopInterface @key(fields: "b") {
|
|
1243
|
+
a: Int @external
|
|
1244
|
+
b: String
|
|
1245
|
+
}
|
|
1246
|
+
|
|
1247
|
+
type T3 implements SubInterface2 & TopInterface {
|
|
1248
|
+
a: Int
|
|
1249
|
+
c: String
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
type T4 implements SubInterface1 & SubInterface2 & TopInterface @key(fields: "a") {
|
|
1253
|
+
a: Int
|
|
1254
|
+
b: String @external
|
|
1255
|
+
c: String @external
|
|
1449
1256
|
}
|
|
1450
1257
|
`;
|
|
1451
1258
|
|
|
1452
|
-
const
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1259
|
+
const s2 = gql`
|
|
1260
|
+
type T2 @key(fields: "b") {
|
|
1261
|
+
a: Int
|
|
1262
|
+
b: String
|
|
1263
|
+
}
|
|
1456
1264
|
|
|
1457
|
-
|
|
1265
|
+
type T4 @key(fields: "a") {
|
|
1266
|
+
a: Int
|
|
1267
|
+
b: String
|
|
1268
|
+
c: String
|
|
1269
|
+
}
|
|
1270
|
+
`;
|
|
1458
1271
|
|
|
1459
|
-
const
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1272
|
+
const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([
|
|
1273
|
+
{ name: 'S1', typeDefs: s1 },
|
|
1274
|
+
{ name: 'S2', typeDefs: s2 }
|
|
1275
|
+
]);
|
|
1276
|
+
|
|
1277
|
+
const t1s_s1: any[] = [{ __typename: 'T1', a: 1, b: 'T1_v1'}, {__typename: 'T1', a: 2, b: 'T1_v2'}];
|
|
1278
|
+
const t2s_s1: any[] = [{__typename: 'T2', b: 'k1'}, {__typename: 'T2', b: 'k2'}];
|
|
1279
|
+
const t3s_s1: any[] = [{__typename: 'T3', a: 42, c: 'T3_v1'}];
|
|
1280
|
+
const t4s_s1: any[] = [{__typename: 'T4', a: 0}, {__typename: 'T4', a: 10}, {__typename: 'T4', a: 20}];
|
|
1281
|
+
|
|
1282
|
+
const t2s_s2 = new Map<string, {a: number, b: string}>();
|
|
1283
|
+
t2s_s2.set('k1', {a: 12 , b: 'k1'});
|
|
1284
|
+
t2s_s2.set('k2', {a: 24 , b: 'k2'});
|
|
1285
|
+
|
|
1286
|
+
const t4s_s2 = new Map<number, {a: number, b: string, c: string}>();
|
|
1287
|
+
t4s_s2.set(0, {a: 0, b: 'b_0', c: 'c_0'});
|
|
1288
|
+
t4s_s2.set(10, {a: 10, b: 'b_10', c: 'c_10'});
|
|
1289
|
+
t4s_s2.set(20, {a: 20, b: 'b_20', c: 'c_20'});
|
|
1290
|
+
|
|
1291
|
+
addResolversToSchema(serviceMap['S1'].schema, {
|
|
1292
|
+
Query: {
|
|
1293
|
+
allValues() {
|
|
1294
|
+
return t1s_s1.concat(t2s_s1).concat(t3s_s1).concat(t4s_s1);
|
|
1295
|
+
},
|
|
1296
|
+
},
|
|
1297
|
+
});
|
|
1465
1298
|
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1299
|
+
addResolversToSchema(serviceMap['S2'].schema, {
|
|
1300
|
+
T2: {
|
|
1301
|
+
__resolveReference(ref) {
|
|
1302
|
+
return t2s_s2.get(ref.b);
|
|
1303
|
+
}
|
|
1304
|
+
},
|
|
1305
|
+
T4: {
|
|
1306
|
+
__resolveReference(ref) {
|
|
1307
|
+
return t4s_s2.get(ref.a);
|
|
1308
|
+
}
|
|
1469
1309
|
},
|
|
1470
1310
|
});
|
|
1471
|
-
expect(queryPlan).toCallService('accounts');
|
|
1472
|
-
});
|
|
1473
1311
|
|
|
1474
|
-
|
|
1475
|
-
const operationDocument = gql`
|
|
1312
|
+
let operation = parseOp(`
|
|
1476
1313
|
query {
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1314
|
+
allValues {
|
|
1315
|
+
a
|
|
1316
|
+
... on SubInterface1 {
|
|
1317
|
+
b
|
|
1318
|
+
}
|
|
1319
|
+
... on SubInterface2 {
|
|
1320
|
+
c
|
|
1321
|
+
}
|
|
1482
1322
|
}
|
|
1483
1323
|
}
|
|
1484
|
-
|
|
1324
|
+
`, schema);
|
|
1485
1325
|
|
|
1486
|
-
|
|
1487
|
-
schema,
|
|
1488
|
-
operationDocument,
|
|
1489
|
-
});
|
|
1326
|
+
let queryPlan = buildPlan(operation, queryPlanner);
|
|
1490
1327
|
|
|
1491
|
-
|
|
1328
|
+
expect(queryPlan).toMatchInlineSnapshot(`
|
|
1329
|
+
QueryPlan {
|
|
1330
|
+
Sequence {
|
|
1331
|
+
Fetch(service: "S1") {
|
|
1332
|
+
{
|
|
1333
|
+
allValues {
|
|
1334
|
+
__typename
|
|
1335
|
+
... on T1 {
|
|
1336
|
+
a
|
|
1337
|
+
b
|
|
1338
|
+
}
|
|
1339
|
+
... on T2 {
|
|
1340
|
+
__typename
|
|
1341
|
+
b
|
|
1342
|
+
}
|
|
1343
|
+
... on T3 {
|
|
1344
|
+
a
|
|
1345
|
+
c
|
|
1346
|
+
}
|
|
1347
|
+
... on T4 {
|
|
1348
|
+
__typename
|
|
1349
|
+
a
|
|
1350
|
+
}
|
|
1351
|
+
}
|
|
1352
|
+
}
|
|
1353
|
+
},
|
|
1354
|
+
Flatten(path: "allValues.@") {
|
|
1355
|
+
Fetch(service: "S2") {
|
|
1356
|
+
{
|
|
1357
|
+
... on T2 {
|
|
1358
|
+
__typename
|
|
1359
|
+
b
|
|
1360
|
+
}
|
|
1361
|
+
... on T4 {
|
|
1362
|
+
__typename
|
|
1363
|
+
a
|
|
1364
|
+
}
|
|
1365
|
+
} =>
|
|
1366
|
+
{
|
|
1367
|
+
... on T2 {
|
|
1368
|
+
a
|
|
1369
|
+
}
|
|
1370
|
+
... on T4 {
|
|
1371
|
+
b
|
|
1372
|
+
c
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1375
|
+
},
|
|
1376
|
+
},
|
|
1377
|
+
},
|
|
1378
|
+
}
|
|
1379
|
+
`);
|
|
1492
1380
|
|
|
1493
|
-
|
|
1494
|
-
queryPlan,
|
|
1495
|
-
serviceMap,
|
|
1496
|
-
buildRequestContext(),
|
|
1497
|
-
operationContext,
|
|
1498
|
-
);
|
|
1381
|
+
let response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
|
|
1499
1382
|
|
|
1500
|
-
expect(response.data).
|
|
1501
|
-
|
|
1383
|
+
expect(response.data).toMatchInlineSnapshot(`
|
|
1384
|
+
Object {
|
|
1385
|
+
"allValues": Array [
|
|
1386
|
+
Object {
|
|
1387
|
+
"a": 1,
|
|
1388
|
+
"b": "T1_v1",
|
|
1389
|
+
},
|
|
1390
|
+
Object {
|
|
1391
|
+
"a": 2,
|
|
1392
|
+
"b": "T1_v2",
|
|
1393
|
+
},
|
|
1394
|
+
Object {
|
|
1395
|
+
"a": 12,
|
|
1396
|
+
"b": "k1",
|
|
1397
|
+
},
|
|
1398
|
+
Object {
|
|
1399
|
+
"a": 24,
|
|
1400
|
+
"b": "k2",
|
|
1401
|
+
},
|
|
1402
|
+
Object {
|
|
1403
|
+
"a": 42,
|
|
1404
|
+
"c": "T3_v1",
|
|
1405
|
+
},
|
|
1406
|
+
Object {
|
|
1407
|
+
"a": 0,
|
|
1408
|
+
"b": "b_0",
|
|
1409
|
+
"c": "c_0",
|
|
1410
|
+
},
|
|
1411
|
+
Object {
|
|
1412
|
+
"a": 10,
|
|
1413
|
+
"b": "b_10",
|
|
1414
|
+
"c": "c_10",
|
|
1415
|
+
},
|
|
1416
|
+
Object {
|
|
1417
|
+
"a": 20,
|
|
1418
|
+
"b": "b_20",
|
|
1419
|
+
"c": "c_20",
|
|
1420
|
+
},
|
|
1421
|
+
],
|
|
1422
|
+
}
|
|
1423
|
+
`);
|
|
1502
1424
|
});
|
|
1503
1425
|
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
}
|
|
1512
|
-
`;
|
|
1426
|
+
it('does not type explode when it does not need to', async () => {
|
|
1427
|
+
// Fairly similar example than the previous one, but ensure field `a` don't need
|
|
1428
|
+
// type explosion and unsure it isn't type-exploded.
|
|
1429
|
+
const s1 = gql`
|
|
1430
|
+
type Query {
|
|
1431
|
+
allValues: [TopInterface!]!
|
|
1432
|
+
}
|
|
1513
1433
|
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
});
|
|
1434
|
+
interface TopInterface {
|
|
1435
|
+
a: Int
|
|
1436
|
+
}
|
|
1518
1437
|
|
|
1519
|
-
|
|
1438
|
+
interface SubInterface1 implements TopInterface {
|
|
1439
|
+
a: Int
|
|
1440
|
+
b: String
|
|
1441
|
+
}
|
|
1520
1442
|
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
buildRequestContext(variables),
|
|
1526
|
-
operationContext,
|
|
1527
|
-
);
|
|
1443
|
+
interface SubInterface2 implements TopInterface {
|
|
1444
|
+
a: Int
|
|
1445
|
+
c: String
|
|
1446
|
+
}
|
|
1528
1447
|
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1448
|
+
type T1 implements SubInterface1 & TopInterface {
|
|
1449
|
+
a: Int
|
|
1450
|
+
b: String
|
|
1451
|
+
}
|
|
1532
1452
|
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
body
|
|
1538
|
-
}
|
|
1539
|
-
}
|
|
1540
|
-
`;
|
|
1453
|
+
type T2 implements SubInterface1 & TopInterface @key(fields: "a") {
|
|
1454
|
+
a: Int
|
|
1455
|
+
b: String @external
|
|
1456
|
+
}
|
|
1541
1457
|
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
}
|
|
1458
|
+
type T3 implements SubInterface2 & TopInterface {
|
|
1459
|
+
a: Int
|
|
1460
|
+
c: String
|
|
1461
|
+
}
|
|
1546
1462
|
|
|
1547
|
-
|
|
1463
|
+
type T4 implements SubInterface1 & SubInterface2 & TopInterface @key(fields: "a") {
|
|
1464
|
+
a: Int
|
|
1465
|
+
b: String @external
|
|
1466
|
+
c: String @external
|
|
1467
|
+
}
|
|
1468
|
+
`;
|
|
1548
1469
|
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
operationContext,
|
|
1555
|
-
);
|
|
1470
|
+
const s2 = gql`
|
|
1471
|
+
type T2 @key(fields: "a") {
|
|
1472
|
+
a: Int
|
|
1473
|
+
b: String
|
|
1474
|
+
}
|
|
1556
1475
|
|
|
1557
|
-
|
|
1558
|
-
|
|
1476
|
+
type T4 @key(fields: "a") {
|
|
1477
|
+
a: Int
|
|
1478
|
+
b: String
|
|
1479
|
+
c: String
|
|
1480
|
+
}
|
|
1481
|
+
`;
|
|
1482
|
+
|
|
1483
|
+
const { serviceMap, schema, queryPlanner} = getFederatedTestingSchema([
|
|
1484
|
+
{ name: 'S1', typeDefs: s1 },
|
|
1485
|
+
{ name: 'S2', typeDefs: s2 }
|
|
1486
|
+
]);
|
|
1487
|
+
|
|
1488
|
+
const t1s_s1: any[] = [{ __typename: 'T1', a: 1, b: 'T1_v1'}, {__typename: 'T1', a: 2, b: 'T1_v2'}];
|
|
1489
|
+
const t2s_s1: any[] = [{__typename: 'T2', a: 12}, {__typename: 'T2', a: 24}];
|
|
1490
|
+
const t3s_s1: any[] = [{__typename: 'T3', a: 42, c: 'T3_v1'}];
|
|
1491
|
+
const t4s_s1: any[] = [{__typename: 'T4', a: 0}, {__typename: 'T4', a: 10}, {__typename: 'T4', a: 20}];
|
|
1492
|
+
|
|
1493
|
+
const t2s_s2 = new Map<number, {a: number, b: string}>();
|
|
1494
|
+
t2s_s2.set(12, {a: 12 , b: 'k1'});
|
|
1495
|
+
t2s_s2.set(24, {a: 24 , b: 'k2'});
|
|
1496
|
+
|
|
1497
|
+
const t4s_s2 = new Map<number, {a: number, b: string, c: string}>();
|
|
1498
|
+
t4s_s2.set(0, {a: 0, b: 'b_0', c: 'c_0'});
|
|
1499
|
+
t4s_s2.set(10, {a: 10, b: 'b_10', c: 'c_10'});
|
|
1500
|
+
t4s_s2.set(20, {a: 20, b: 'b_20', c: 'c_20'});
|
|
1501
|
+
|
|
1502
|
+
addResolversToSchema(serviceMap['S1'].schema, {
|
|
1503
|
+
Query: {
|
|
1504
|
+
allValues() {
|
|
1505
|
+
return t1s_s1.concat(t2s_s1).concat(t3s_s1).concat(t4s_s1);
|
|
1506
|
+
},
|
|
1507
|
+
},
|
|
1559
1508
|
});
|
|
1560
1509
|
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
body
|
|
1566
|
-
}
|
|
1510
|
+
addResolversToSchema(serviceMap['S2'].schema, {
|
|
1511
|
+
T2: {
|
|
1512
|
+
__resolveReference(ref) {
|
|
1513
|
+
return t2s_s2.get(ref.b);
|
|
1567
1514
|
}
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
}
|
|
1515
|
+
},
|
|
1516
|
+
T4: {
|
|
1517
|
+
__resolveReference(ref) {
|
|
1518
|
+
return t4s_s2.get(ref.a);
|
|
1519
|
+
}
|
|
1520
|
+
},
|
|
1521
|
+
});
|
|
1574
1522
|
|
|
1575
|
-
|
|
1523
|
+
let operation = parseOp(`
|
|
1524
|
+
query {
|
|
1525
|
+
allValues {
|
|
1526
|
+
a
|
|
1527
|
+
}
|
|
1528
|
+
}
|
|
1529
|
+
`, schema);
|
|
1576
1530
|
|
|
1577
|
-
|
|
1578
|
-
const response = await executeQueryPlan(
|
|
1579
|
-
queryPlan,
|
|
1580
|
-
serviceMap,
|
|
1581
|
-
buildRequestContext(variables),
|
|
1582
|
-
operationContext,
|
|
1583
|
-
);
|
|
1531
|
+
let queryPlan = buildPlan(operation, queryPlanner);
|
|
1584
1532
|
|
|
1585
|
-
|
|
1586
|
-
|
|
1533
|
+
expect(queryPlan).toMatchInlineSnapshot(`
|
|
1534
|
+
QueryPlan {
|
|
1535
|
+
Fetch(service: "S1") {
|
|
1587
1536
|
{
|
|
1588
|
-
|
|
1537
|
+
allValues {
|
|
1538
|
+
__typename
|
|
1539
|
+
a
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
},
|
|
1543
|
+
}
|
|
1544
|
+
`);
|
|
1545
|
+
|
|
1546
|
+
let response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
|
|
1547
|
+
expect(response.data).toMatchInlineSnapshot(`
|
|
1548
|
+
Object {
|
|
1549
|
+
"allValues": Array [
|
|
1550
|
+
Object {
|
|
1551
|
+
"a": 1,
|
|
1589
1552
|
},
|
|
1590
|
-
{
|
|
1591
|
-
|
|
1553
|
+
Object {
|
|
1554
|
+
"a": 2,
|
|
1555
|
+
},
|
|
1556
|
+
Object {
|
|
1557
|
+
"a": 12,
|
|
1558
|
+
},
|
|
1559
|
+
Object {
|
|
1560
|
+
"a": 24,
|
|
1561
|
+
},
|
|
1562
|
+
Object {
|
|
1563
|
+
"a": 42,
|
|
1564
|
+
},
|
|
1565
|
+
Object {
|
|
1566
|
+
"a": 0,
|
|
1567
|
+
},
|
|
1568
|
+
Object {
|
|
1569
|
+
"a": 10,
|
|
1570
|
+
},
|
|
1571
|
+
Object {
|
|
1572
|
+
"a": 20,
|
|
1592
1573
|
},
|
|
1593
1574
|
],
|
|
1594
|
-
}
|
|
1595
|
-
|
|
1596
|
-
});
|
|
1575
|
+
}
|
|
1576
|
+
`);
|
|
1597
1577
|
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1578
|
+
operation = parseOp(`
|
|
1579
|
+
query {
|
|
1580
|
+
allValues {
|
|
1581
|
+
... on SubInterface1 {
|
|
1582
|
+
a
|
|
1603
1583
|
}
|
|
1604
1584
|
}
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
topReviews @include(if: $shouldInclude) @skip(if: $shouldSkip) {
|
|
1628
|
-
body
|
|
1585
|
+
}
|
|
1586
|
+
`, schema);
|
|
1587
|
+
|
|
1588
|
+
queryPlan = buildPlan(operation, queryPlanner);
|
|
1589
|
+
|
|
1590
|
+
// TODO: we're actually type-exploding in this case because currently, as soon as we need to type-explode, we do
|
|
1591
|
+
// so into all the runtime types, while here it could make sense to only type-explode into the direct sub-types=
|
|
1592
|
+
// (the sub-interfaces). We should fix this (but it's only sub-optimal, not incorrect).
|
|
1593
|
+
expect(queryPlan).toMatchInlineSnapshot(`
|
|
1594
|
+
QueryPlan {
|
|
1595
|
+
Fetch(service: "S1") {
|
|
1596
|
+
{
|
|
1597
|
+
allValues {
|
|
1598
|
+
__typename
|
|
1599
|
+
... on T1 {
|
|
1600
|
+
a
|
|
1601
|
+
}
|
|
1602
|
+
... on T2 {
|
|
1603
|
+
a
|
|
1604
|
+
}
|
|
1605
|
+
... on T4 {
|
|
1606
|
+
a
|
|
1629
1607
|
}
|
|
1630
1608
|
}
|
|
1609
|
+
}
|
|
1610
|
+
},
|
|
1611
|
+
}
|
|
1612
|
+
`);
|
|
1613
|
+
|
|
1614
|
+
response = await executePlan(queryPlan, operation, undefined, schema, serviceMap);
|
|
1615
|
+
expect(response.data).toMatchInlineSnapshot(`
|
|
1616
|
+
Object {
|
|
1617
|
+
"allValues": Array [
|
|
1618
|
+
Object {
|
|
1619
|
+
"a": 1,
|
|
1631
1620
|
},
|
|
1632
|
-
|
|
1621
|
+
Object {
|
|
1622
|
+
"a": 2,
|
|
1623
|
+
},
|
|
1624
|
+
Object {
|
|
1625
|
+
"a": 12,
|
|
1626
|
+
},
|
|
1627
|
+
Object {
|
|
1628
|
+
"a": 24,
|
|
1629
|
+
},
|
|
1630
|
+
Object {},
|
|
1631
|
+
Object {
|
|
1632
|
+
"a": 0,
|
|
1633
|
+
},
|
|
1634
|
+
Object {
|
|
1635
|
+
"a": 10,
|
|
1636
|
+
},
|
|
1637
|
+
Object {
|
|
1638
|
+
"a": 20,
|
|
1639
|
+
},
|
|
1640
|
+
],
|
|
1641
|
+
}
|
|
1633
1642
|
`);
|
|
1634
|
-
|
|
1635
|
-
expect({ queryPlan, variables }).not.toCallService('reviews');
|
|
1636
|
-
});
|
|
1637
1643
|
});
|
|
1638
1644
|
});
|
|
1639
1645
|
});
|