@apollo/gateway 2.4.0-alpha.1 → 2.4.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/dist/__generated__/graphqlTypes.d.ts +1 -0
- package/dist/__generated__/graphqlTypes.d.ts.map +1 -1
- package/dist/__generated__/graphqlTypes.js.map +1 -1
- package/dist/dataRewrites.d.ts +5 -0
- package/dist/dataRewrites.d.ts.map +1 -0
- package/dist/dataRewrites.js +103 -0
- package/dist/dataRewrites.js.map +1 -0
- package/dist/executeQueryPlan.d.ts.map +1 -1
- package/dist/executeQueryPlan.js +41 -110
- package/dist/executeQueryPlan.js.map +1 -1
- package/dist/resultShaping.js +3 -3
- package/dist/resultShaping.js.map +1 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/index.js +1 -1
- package/dist/supergraphManagers/UplinkSupergraphManager/index.js.map +1 -1
- package/package.json +4 -4
- package/src/__generated__/graphqlTypes.ts +4 -3
- package/src/__tests__/buildQueryPlan.test.ts +0 -1
- package/src/__tests__/executeQueryPlan.introspection.test.ts +140 -0
- package/src/__tests__/executeQueryPlan.test.ts +269 -5
- package/src/__tests__/gateway/buildService.test.ts +2 -2
- package/src/__tests__/gateway/supergraphSdl.test.ts +2 -2
- package/src/__tests__/integration/complex-key.test.ts +4 -4
- package/src/__tests__/integration/list-key.test.ts +2 -2
- package/src/__tests__/integration/multiple-key.test.ts +2 -2
- package/src/__tests__/integration/requires.test.ts +2 -2
- package/src/__tests__/integration/single-service.test.ts +1 -1
- package/src/__tests__/integration/value-types.test.ts +13 -16
- package/src/dataRewrites.ts +130 -0
- package/src/executeQueryPlan.ts +57 -135
- package/src/resultShaping.ts +3 -3
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
import { FetchDataRewrite } from "@apollo/query-planner";
|
|
2
|
+
import { assert } from "console";
|
|
3
|
+
import { GraphQLSchema, isAbstractType, isInterfaceType, isObjectType } from "graphql";
|
|
4
|
+
|
|
5
|
+
const FRAGMENT_PREFIX = '... on ';
|
|
6
|
+
|
|
7
|
+
export function applyRewrites(schema: GraphQLSchema, rewrites: FetchDataRewrite[] | undefined, value: Record<string, any>) {
|
|
8
|
+
if (!rewrites) {
|
|
9
|
+
return;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
for (const rewrite of rewrites) {
|
|
13
|
+
applyRewrite(schema, rewrite, value);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function applyRewrite(schema: GraphQLSchema, rewrite: FetchDataRewrite, value: Record<string, any>) {
|
|
18
|
+
const splitted = splitPathLastElement(rewrite.path);
|
|
19
|
+
if (!splitted) {
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const [parent, last] = splitted;
|
|
24
|
+
const { kind, value: fieldName } = parsePathElement(last);
|
|
25
|
+
// So far, all rewrites finish by a field name. If this ever changes, this assertion will catch it early and we can update.
|
|
26
|
+
assert(kind === 'fieldName', () => `Unexpected fragment as last element of ${rewrite.path}`);
|
|
27
|
+
applyAtPath(schema, parent, value, rewriteAtPathFunction(rewrite, fieldName));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function rewriteAtPathFunction(rewrite: FetchDataRewrite, fieldAtPath: string): (obj: Record<string, any>) => void {
|
|
31
|
+
switch (rewrite.kind) {
|
|
32
|
+
case 'ValueSetter':
|
|
33
|
+
return (obj) => {
|
|
34
|
+
obj[fieldAtPath] = rewrite.setValueTo;
|
|
35
|
+
};
|
|
36
|
+
case 'KeyRenamer':
|
|
37
|
+
return (obj) => {
|
|
38
|
+
obj[rewrite.renameKeyTo] = obj[fieldAtPath];
|
|
39
|
+
obj[fieldAtPath] = undefined;
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Given a path, separates the last element of path and the rest of it and return them as a pair.
|
|
47
|
+
* This will return `undefined` if the path is empty.
|
|
48
|
+
*/
|
|
49
|
+
function splitPathLastElement(path: string[]): [string[], string] | undefined {
|
|
50
|
+
if (path.length === 0) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const lastIdx = path.length - 1;
|
|
55
|
+
return [path.slice(0, lastIdx), path[lastIdx]];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function applyAtPath(schema: GraphQLSchema, path: string[], value: any, fct: (objAtPath: Record<string, any>) => void) {
|
|
59
|
+
if (Array.isArray(value)) {
|
|
60
|
+
for (const arrayValue of value) {
|
|
61
|
+
applyAtPath(schema, path, arrayValue, fct);
|
|
62
|
+
}
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if (typeof value !== 'object') {
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
if (path.length === 0) {
|
|
71
|
+
fct(value);
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
const [first, ...rest] = path;
|
|
76
|
+
const { kind, value: eltValue } = parsePathElement(first);
|
|
77
|
+
switch (kind) {
|
|
78
|
+
case 'fieldName':
|
|
79
|
+
applyAtPath(schema, rest, value[eltValue], fct);
|
|
80
|
+
break;
|
|
81
|
+
case 'typeName':
|
|
82
|
+
// When we apply rewrites, we don't always have the __typename of all object we would need to, but the code expects that
|
|
83
|
+
// this does not stop the rewrite to applying, hence the modified to `true` when the object typename is not found.
|
|
84
|
+
if (isObjectOfType(schema, value, eltValue, true)) {
|
|
85
|
+
applyAtPath(schema, rest, value, fct);
|
|
86
|
+
}
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function parsePathElement(elt: string): { kind: 'fieldName' | 'typeName', value: string } {
|
|
92
|
+
if (elt.startsWith(FRAGMENT_PREFIX)) {
|
|
93
|
+
return { kind: 'typeName', value: elt.slice(FRAGMENT_PREFIX.length) };
|
|
94
|
+
} else {
|
|
95
|
+
return { kind: 'fieldName', value: elt };
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
export function isObjectOfType(
|
|
101
|
+
schema: GraphQLSchema,
|
|
102
|
+
obj: Record<string, any>,
|
|
103
|
+
typeCondition: string,
|
|
104
|
+
defaultOnUnknownObjectType: boolean = false,
|
|
105
|
+
): boolean {
|
|
106
|
+
const objTypename = obj['__typename'];
|
|
107
|
+
if (!objTypename) {
|
|
108
|
+
return defaultOnUnknownObjectType;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (typeCondition === objTypename) {
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const type = schema.getType(objTypename);
|
|
116
|
+
if (!type) {
|
|
117
|
+
return false;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const conditionalType = schema.getType(typeCondition);
|
|
121
|
+
if (!conditionalType) {
|
|
122
|
+
return false;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
if (isAbstractType(conditionalType)) {
|
|
126
|
+
return (isObjectType(type) || isInterfaceType(type)) && schema.isSubType(conditionalType, type);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
return false;
|
|
130
|
+
}
|
package/src/executeQueryPlan.ts
CHANGED
|
@@ -5,15 +5,15 @@ import {
|
|
|
5
5
|
TypeNameMetaFieldDef,
|
|
6
6
|
GraphQLFieldResolver,
|
|
7
7
|
GraphQLFormattedError,
|
|
8
|
-
isAbstractType,
|
|
9
8
|
GraphQLSchema,
|
|
10
|
-
isObjectType,
|
|
11
|
-
isInterfaceType,
|
|
12
9
|
GraphQLErrorOptions,
|
|
13
10
|
DocumentNode,
|
|
14
11
|
executeSync,
|
|
15
12
|
OperationTypeNode,
|
|
16
13
|
FieldNode,
|
|
14
|
+
visit,
|
|
15
|
+
ASTNode,
|
|
16
|
+
VariableDefinitionNode,
|
|
17
17
|
} from 'graphql';
|
|
18
18
|
import { Trace, google } from '@apollo/usage-reporting-protobuf';
|
|
19
19
|
import { GraphQLDataSource, GraphQLDataSourceRequestKind } from './datasources/types';
|
|
@@ -26,17 +26,16 @@ import {
|
|
|
26
26
|
QueryPlanSelectionNode,
|
|
27
27
|
QueryPlanFieldNode,
|
|
28
28
|
getResponseName,
|
|
29
|
-
FetchDataInputRewrite,
|
|
30
|
-
FetchDataOutputRewrite,
|
|
31
29
|
evaluateCondition,
|
|
32
30
|
} from '@apollo/query-planner';
|
|
33
31
|
import { deepMerge } from './utilities/deepMerge';
|
|
34
32
|
import { isNotNullOrUndefined } from './utilities/array';
|
|
35
33
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
36
34
|
import { OpenTelemetrySpanNames, tracer } from "./utilities/opentelemetry";
|
|
37
|
-
import { assert, defaultRootName, errorCodeDef, ERRORS,
|
|
35
|
+
import { assert, defaultRootName, errorCodeDef, ERRORS, Operation, operationFromDocument, Schema } from '@apollo/federation-internals';
|
|
38
36
|
import { GatewayGraphQLRequestContext, GatewayExecutionResult } from '@apollo/server-gateway-interface';
|
|
39
37
|
import { computeResponse } from './resultShaping';
|
|
38
|
+
import { applyRewrites, isObjectOfType } from './dataRewrites';
|
|
40
39
|
|
|
41
40
|
export type ServiceMap = {
|
|
42
41
|
[serviceName: string]: GraphQLDataSource;
|
|
@@ -72,13 +71,33 @@ interface ExecutionContext {
|
|
|
72
71
|
errors: GraphQLError[];
|
|
73
72
|
}
|
|
74
73
|
|
|
75
|
-
function
|
|
74
|
+
function collectUsedVariables(node: ASTNode): Set<string> {
|
|
75
|
+
const usedVariables = new Set<string>();
|
|
76
|
+
visit(node, {
|
|
77
|
+
Variable: ({ name }) => {
|
|
78
|
+
usedVariables.add(name.value);
|
|
79
|
+
}
|
|
80
|
+
});
|
|
81
|
+
return usedVariables;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function makeIntrospectionQueryDocument(
|
|
85
|
+
introspectionSelection: FieldNode,
|
|
86
|
+
variableDefinitions?: readonly VariableDefinitionNode[],
|
|
87
|
+
): DocumentNode {
|
|
88
|
+
const usedVariables = collectUsedVariables(introspectionSelection);
|
|
89
|
+
const usedVariableDefinitions = variableDefinitions?.filter((def) => usedVariables.has(def.variable.name.value));
|
|
90
|
+
assert(
|
|
91
|
+
usedVariables.size === (usedVariableDefinitions?.length ?? 0),
|
|
92
|
+
() => `Should have found all used variables ${[...usedVariables]} in definitions ${JSON.stringify(variableDefinitions)}`,
|
|
93
|
+
);
|
|
76
94
|
return {
|
|
77
95
|
kind: Kind.DOCUMENT,
|
|
78
96
|
definitions: [
|
|
79
97
|
{
|
|
80
98
|
kind: Kind.OPERATION_DEFINITION,
|
|
81
99
|
operation: OperationTypeNode.QUERY,
|
|
100
|
+
variableDefinitions: usedVariableDefinitions,
|
|
82
101
|
selectionSet: {
|
|
83
102
|
kind: Kind.SELECTION_SET,
|
|
84
103
|
selections: [ introspectionSelection ],
|
|
@@ -91,14 +110,21 @@ function makeIntrospectionQueryDocument(introspectionSelection: FieldNode): Docu
|
|
|
91
110
|
function executeIntrospection(
|
|
92
111
|
schema: GraphQLSchema,
|
|
93
112
|
introspectionSelection: FieldNode,
|
|
113
|
+
variableDefinitions: ReadonlyArray<VariableDefinitionNode> | undefined,
|
|
114
|
+
variableValues: Record<string, any> | undefined,
|
|
94
115
|
): any {
|
|
95
|
-
const { data } = executeSync({
|
|
116
|
+
const { data, errors } = executeSync({
|
|
96
117
|
schema,
|
|
97
|
-
document: makeIntrospectionQueryDocument(introspectionSelection),
|
|
118
|
+
document: makeIntrospectionQueryDocument(introspectionSelection, variableDefinitions),
|
|
98
119
|
rootValue: {},
|
|
120
|
+
variableValues,
|
|
99
121
|
});
|
|
122
|
+
assert(
|
|
123
|
+
!errors || errors.length === 0,
|
|
124
|
+
() => `Introspection query for ${JSON.stringify(introspectionSelection)} should not have failed but got ${JSON.stringify(errors)}`
|
|
125
|
+
);
|
|
100
126
|
assert(data, () => `Introspection query for ${JSON.stringify(introspectionSelection)} should not have failed`);
|
|
101
|
-
return data[introspectionSelection.name.value];
|
|
127
|
+
return data[introspectionSelection.alias?.value ?? introspectionSelection.name.value];
|
|
102
128
|
}
|
|
103
129
|
|
|
104
130
|
export async function executeQueryPlan(
|
|
@@ -185,11 +211,17 @@ export async function executeQueryPlan(
|
|
|
185
211
|
let data;
|
|
186
212
|
try {
|
|
187
213
|
let postProcessingErrors: GraphQLError[];
|
|
214
|
+
const variables = requestContext.request.variables;
|
|
188
215
|
({ data, errors: postProcessingErrors } = computeResponse({
|
|
189
216
|
operation,
|
|
190
|
-
variables
|
|
217
|
+
variables,
|
|
191
218
|
input: unfilteredData,
|
|
192
|
-
introspectionHandling: (f) => executeIntrospection(
|
|
219
|
+
introspectionHandling: (f) => executeIntrospection(
|
|
220
|
+
operationContext.schema,
|
|
221
|
+
f.expandAllFragments().toSelectionNode(),
|
|
222
|
+
operationContext.operation.variableDefinitions,
|
|
223
|
+
variables,
|
|
224
|
+
),
|
|
193
225
|
}));
|
|
194
226
|
|
|
195
227
|
// If we have errors during the post-processing, we ignore them if any other errors have been thrown during
|
|
@@ -416,9 +448,12 @@ async function executeFetch(
|
|
|
416
448
|
|
|
417
449
|
if (!fetch.requires) {
|
|
418
450
|
const dataReceivedFromService = await sendOperation(variables);
|
|
451
|
+
if (dataReceivedFromService) {
|
|
452
|
+
applyRewrites(context.supergraphSchema, fetch.outputRewrites, dataReceivedFromService);
|
|
453
|
+
}
|
|
419
454
|
|
|
420
455
|
for (const entity of entities) {
|
|
421
|
-
deepMerge(entity,
|
|
456
|
+
deepMerge(entity, dataReceivedFromService);
|
|
422
457
|
}
|
|
423
458
|
} else {
|
|
424
459
|
const requires = fetch.requires;
|
|
@@ -434,9 +469,9 @@ async function executeFetch(
|
|
|
434
469
|
context.supergraphSchema,
|
|
435
470
|
entity,
|
|
436
471
|
requires,
|
|
437
|
-
fetch.inputRewrites,
|
|
438
472
|
);
|
|
439
473
|
if (representation && representation[TypeNameMetaFieldDef.name]) {
|
|
474
|
+
applyRewrites(context.supergraphSchema, fetch.inputRewrites, representation);
|
|
440
475
|
representations.push(representation);
|
|
441
476
|
representationToEntity.push(index);
|
|
442
477
|
}
|
|
@@ -473,8 +508,11 @@ async function executeFetch(
|
|
|
473
508
|
);
|
|
474
509
|
}
|
|
475
510
|
|
|
511
|
+
|
|
476
512
|
for (let i = 0; i < entities.length; i++) {
|
|
477
|
-
|
|
513
|
+
const receivedEntity = receivedEntities[i];
|
|
514
|
+
applyRewrites(context.supergraphSchema, fetch.outputRewrites, receivedEntity);
|
|
515
|
+
deepMerge(entities[representationToEntity[i]], receivedEntity);
|
|
478
516
|
}
|
|
479
517
|
}
|
|
480
518
|
}
|
|
@@ -707,84 +745,6 @@ export function generateHydratedPaths(
|
|
|
707
745
|
}
|
|
708
746
|
}
|
|
709
747
|
|
|
710
|
-
function applyOrMapRecursive(value: any | any[], fct: (v: any) => any | undefined): any | any[] | undefined {
|
|
711
|
-
if (Array.isArray(value)) {
|
|
712
|
-
const res = value.map((elt) => applyOrMapRecursive(elt, fct)).filter(isDefined);
|
|
713
|
-
return res.length === 0 ? undefined : res;
|
|
714
|
-
}
|
|
715
|
-
return fct(value);
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
function withFetchRewrites(fetchResult: ResultMap | null | void, rewrites: FetchDataOutputRewrite[] | undefined): ResultMap | null | void {
|
|
719
|
-
if (!rewrites || !fetchResult) {
|
|
720
|
-
return fetchResult;
|
|
721
|
-
}
|
|
722
|
-
|
|
723
|
-
for (const rewrite of rewrites) {
|
|
724
|
-
let obj: any = fetchResult;
|
|
725
|
-
let i = 0;
|
|
726
|
-
while (obj && i < rewrite.path.length - 1) {
|
|
727
|
-
const p = rewrite.path[i++];
|
|
728
|
-
if (p.startsWith('... on ')) {
|
|
729
|
-
const typename = p.slice('... on '.length);
|
|
730
|
-
// Filter only objects that match the condition.
|
|
731
|
-
obj = applyOrMapRecursive(obj, (elt) => elt[TypeNameMetaFieldDef.name] === typename ? elt : undefined);
|
|
732
|
-
} else {
|
|
733
|
-
obj = applyOrMapRecursive(obj, (elt) => elt[p]);
|
|
734
|
-
}
|
|
735
|
-
}
|
|
736
|
-
if (obj) {
|
|
737
|
-
applyOrMapRecursive(obj, (elt) => {
|
|
738
|
-
if (typeof elt === 'object') {
|
|
739
|
-
// We need to move the value at path[i] to `renameKeyTo`.
|
|
740
|
-
const removedKey = rewrite.path[i];
|
|
741
|
-
elt[rewrite.renameKeyTo] = elt[removedKey];
|
|
742
|
-
elt[removedKey] = undefined;
|
|
743
|
-
}
|
|
744
|
-
});
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
return fetchResult;
|
|
748
|
-
}
|
|
749
|
-
|
|
750
|
-
function filterEntityRewrites(entity: Record<string, any>, rewrites: FetchDataOutputRewrite[] | undefined): FetchDataOutputRewrite[] | undefined {
|
|
751
|
-
if (!rewrites) {
|
|
752
|
-
return undefined;
|
|
753
|
-
}
|
|
754
|
-
|
|
755
|
-
const typename = entity[TypeNameMetaFieldDef.name] as string;
|
|
756
|
-
const typenameAsFragment = `... on ${typename}`;
|
|
757
|
-
return rewrites.map((r) => r.path[0] === typenameAsFragment ? { ...r, path: r.path.slice(1) } : undefined).filter(isDefined)
|
|
758
|
-
}
|
|
759
|
-
|
|
760
|
-
function updateRewrites(rewrites: FetchDataInputRewrite[] | undefined, pathElement: string): {
|
|
761
|
-
updated: FetchDataInputRewrite[],
|
|
762
|
-
completeRewrite?: any,
|
|
763
|
-
} | undefined {
|
|
764
|
-
if (!rewrites) {
|
|
765
|
-
return undefined;
|
|
766
|
-
}
|
|
767
|
-
|
|
768
|
-
let completeRewrite: any = undefined;
|
|
769
|
-
const updated = rewrites
|
|
770
|
-
.map((r) => {
|
|
771
|
-
let u: FetchDataInputRewrite | undefined = undefined;
|
|
772
|
-
if (r.path[0] === pathElement) {
|
|
773
|
-
const updatedPath = r.path.slice(1);
|
|
774
|
-
if (updatedPath.length === 0) {
|
|
775
|
-
completeRewrite = r.setValueTo;
|
|
776
|
-
} else {
|
|
777
|
-
u = { ...r, path: updatedPath };
|
|
778
|
-
}
|
|
779
|
-
}
|
|
780
|
-
return u;
|
|
781
|
-
})
|
|
782
|
-
.filter(isDefined);
|
|
783
|
-
return updated.length === 0 && completeRewrite === undefined
|
|
784
|
-
? undefined
|
|
785
|
-
: { updated, completeRewrite };
|
|
786
|
-
}
|
|
787
|
-
|
|
788
748
|
/**
|
|
789
749
|
*
|
|
790
750
|
* @param source Result of GraphQL execution.
|
|
@@ -794,7 +754,6 @@ function executeSelectionSet(
|
|
|
794
754
|
schema: GraphQLSchema,
|
|
795
755
|
source: Record<string, any> | null,
|
|
796
756
|
selections: QueryPlanSelectionNode[],
|
|
797
|
-
activeRewrites?: FetchDataInputRewrite[],
|
|
798
757
|
): Record<string, any> | null {
|
|
799
758
|
|
|
800
759
|
// If the underlying service has returned null for the parent (source)
|
|
@@ -825,16 +784,10 @@ function executeSelectionSet(
|
|
|
825
784
|
return null;
|
|
826
785
|
}
|
|
827
786
|
|
|
828
|
-
const updatedRewrites = updateRewrites(activeRewrites, responseName);
|
|
829
|
-
if (updatedRewrites?.completeRewrite !== undefined) {
|
|
830
|
-
result[responseName] = updatedRewrites.completeRewrite;
|
|
831
|
-
continue;
|
|
832
|
-
}
|
|
833
|
-
|
|
834
787
|
if (Array.isArray(source[responseName])) {
|
|
835
788
|
result[responseName] = source[responseName].map((value: any) =>
|
|
836
789
|
selections
|
|
837
|
-
? executeSelectionSet(schema, value, selections
|
|
790
|
+
? executeSelectionSet(schema, value, selections)
|
|
838
791
|
: value,
|
|
839
792
|
);
|
|
840
793
|
} else if (selections) {
|
|
@@ -842,23 +795,18 @@ function executeSelectionSet(
|
|
|
842
795
|
schema,
|
|
843
796
|
source[responseName],
|
|
844
797
|
selections,
|
|
845
|
-
updatedRewrites?.updated,
|
|
846
798
|
);
|
|
847
799
|
} else {
|
|
848
800
|
result[responseName] = source[responseName];
|
|
849
801
|
}
|
|
850
802
|
break;
|
|
851
803
|
case Kind.INLINE_FRAGMENT:
|
|
852
|
-
if (!selection.typeCondition) continue;
|
|
804
|
+
if (!selection.typeCondition || !source) continue;
|
|
853
805
|
|
|
854
|
-
|
|
855
|
-
if (!typename) continue;
|
|
856
|
-
|
|
857
|
-
if (doesTypeConditionMatch(schema, selection.typeCondition, typename)) {
|
|
858
|
-
const updatedRewrites = activeRewrites ? updateRewrites(activeRewrites, `... on ${selection.typeCondition}`) : undefined;
|
|
806
|
+
if (isObjectOfType(schema, source, selection.typeCondition)) {
|
|
859
807
|
deepMerge(
|
|
860
808
|
result,
|
|
861
|
-
executeSelectionSet(schema, source, selection.selections
|
|
809
|
+
executeSelectionSet(schema, source, selection.selections),
|
|
862
810
|
);
|
|
863
811
|
}
|
|
864
812
|
break;
|
|
@@ -868,32 +816,6 @@ function executeSelectionSet(
|
|
|
868
816
|
return result;
|
|
869
817
|
}
|
|
870
818
|
|
|
871
|
-
function doesTypeConditionMatch(
|
|
872
|
-
schema: GraphQLSchema,
|
|
873
|
-
typeCondition: string,
|
|
874
|
-
typename: string,
|
|
875
|
-
): boolean {
|
|
876
|
-
if (typeCondition === typename) {
|
|
877
|
-
return true;
|
|
878
|
-
}
|
|
879
|
-
|
|
880
|
-
const type = schema.getType(typename);
|
|
881
|
-
if (!type) {
|
|
882
|
-
return false;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
const conditionalType = schema.getType(typeCondition);
|
|
886
|
-
if (!conditionalType) {
|
|
887
|
-
return false;
|
|
888
|
-
}
|
|
889
|
-
|
|
890
|
-
if (isAbstractType(conditionalType)) {
|
|
891
|
-
return (isObjectType(type) || isInterfaceType(type)) && schema.isSubType(conditionalType, type);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
|
-
return false;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
819
|
function moveIntoCursor(cursor: ResultCursor, pathInCursor: ResponsePath): ResultCursor | undefined {
|
|
898
820
|
const data = flattenResultsAtPath(cursor.data, pathInCursor);
|
|
899
821
|
return data ? {
|
package/src/resultShaping.ts
CHANGED
|
@@ -161,12 +161,12 @@ function applySelectionSet({
|
|
|
161
161
|
parentType: CompositeType,
|
|
162
162
|
}): ApplyResult {
|
|
163
163
|
for (const selection of selectionSet.selections()) {
|
|
164
|
-
if (shouldSkip(selection.element
|
|
164
|
+
if (shouldSkip(selection.element, parameters)) {
|
|
165
165
|
continue;
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
if (selection.kind === 'FieldSelection') {
|
|
169
|
-
const field = selection.element
|
|
169
|
+
const field = selection.element;
|
|
170
170
|
const fieldType = field.definition.type!;
|
|
171
171
|
const responseName = field.responseName();
|
|
172
172
|
const outputValue = output[responseName];
|
|
@@ -241,7 +241,7 @@ function applySelectionSet({
|
|
|
241
241
|
return ApplyResult.NULL_BUBBLE_UP;
|
|
242
242
|
}
|
|
243
243
|
} else {
|
|
244
|
-
const fragment = selection.element
|
|
244
|
+
const fragment = selection.element;
|
|
245
245
|
const typename = input[typenameFieldName];
|
|
246
246
|
assert(!typename || typeof typename === 'string', () => `Got unexpected value for __typename: ${typename}`);
|
|
247
247
|
if (typeConditionApplies(parameters.schema, fragment.typeCondition, typename, parentType)) {
|