@apollo/gateway 2.3.5 → 2.3.6
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/package.json +4 -4
- package/src/__generated__/graphqlTypes.ts +4 -3
- package/src/__tests__/executeQueryPlan.introspection.test.ts +140 -0
- package/src/__tests__/executeQueryPlan.test.ts +508 -0
- package/src/dataRewrites.ts +130 -0
- package/src/executeQueryPlan.ts +57 -135
|
@@ -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,16 +26,15 @@ import {
|
|
|
26
26
|
QueryPlanSelectionNode,
|
|
27
27
|
QueryPlanFieldNode,
|
|
28
28
|
getResponseName,
|
|
29
|
-
FetchDataInputRewrite,
|
|
30
|
-
FetchDataOutputRewrite,
|
|
31
29
|
} from '@apollo/query-planner';
|
|
32
30
|
import { deepMerge } from './utilities/deepMerge';
|
|
33
31
|
import { isNotNullOrUndefined } from './utilities/array';
|
|
34
32
|
import { SpanStatusCode } from "@opentelemetry/api";
|
|
35
33
|
import { OpenTelemetrySpanNames, tracer } from "./utilities/opentelemetry";
|
|
36
|
-
import { assert, defaultRootName, errorCodeDef, ERRORS,
|
|
34
|
+
import { assert, defaultRootName, errorCodeDef, ERRORS, operationFromDocument, Schema } from '@apollo/federation-internals';
|
|
37
35
|
import { GatewayGraphQLRequestContext, GatewayExecutionResult } from '@apollo/server-gateway-interface';
|
|
38
36
|
import { computeResponse } from './resultShaping';
|
|
37
|
+
import { applyRewrites, isObjectOfType } from './dataRewrites';
|
|
39
38
|
|
|
40
39
|
export type ServiceMap = {
|
|
41
40
|
[serviceName: string]: GraphQLDataSource;
|
|
@@ -70,13 +69,33 @@ interface ExecutionContext {
|
|
|
70
69
|
errors: GraphQLError[];
|
|
71
70
|
}
|
|
72
71
|
|
|
73
|
-
function
|
|
72
|
+
function collectUsedVariables(node: ASTNode): Set<string> {
|
|
73
|
+
const usedVariables = new Set<string>();
|
|
74
|
+
visit(node, {
|
|
75
|
+
Variable: ({ name }) => {
|
|
76
|
+
usedVariables.add(name.value);
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
return usedVariables;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function makeIntrospectionQueryDocument(
|
|
83
|
+
introspectionSelection: FieldNode,
|
|
84
|
+
variableDefinitions?: readonly VariableDefinitionNode[],
|
|
85
|
+
): DocumentNode {
|
|
86
|
+
const usedVariables = collectUsedVariables(introspectionSelection);
|
|
87
|
+
const usedVariableDefinitions = variableDefinitions?.filter((def) => usedVariables.has(def.variable.name.value));
|
|
88
|
+
assert(
|
|
89
|
+
usedVariables.size === (usedVariableDefinitions?.length ?? 0),
|
|
90
|
+
() => `Should have found all used variables ${[...usedVariables]} in definitions ${JSON.stringify(variableDefinitions)}`,
|
|
91
|
+
);
|
|
74
92
|
return {
|
|
75
93
|
kind: Kind.DOCUMENT,
|
|
76
94
|
definitions: [
|
|
77
95
|
{
|
|
78
96
|
kind: Kind.OPERATION_DEFINITION,
|
|
79
97
|
operation: OperationTypeNode.QUERY,
|
|
98
|
+
variableDefinitions: usedVariableDefinitions,
|
|
80
99
|
selectionSet: {
|
|
81
100
|
kind: Kind.SELECTION_SET,
|
|
82
101
|
selections: [ introspectionSelection ],
|
|
@@ -89,14 +108,21 @@ function makeIntrospectionQueryDocument(introspectionSelection: FieldNode): Docu
|
|
|
89
108
|
function executeIntrospection(
|
|
90
109
|
schema: GraphQLSchema,
|
|
91
110
|
introspectionSelection: FieldNode,
|
|
111
|
+
variableDefinitions: ReadonlyArray<VariableDefinitionNode> | undefined,
|
|
112
|
+
variableValues: Record<string, any> | undefined,
|
|
92
113
|
): any {
|
|
93
|
-
const { data } = executeSync({
|
|
114
|
+
const { data, errors } = executeSync({
|
|
94
115
|
schema,
|
|
95
|
-
document: makeIntrospectionQueryDocument(introspectionSelection),
|
|
116
|
+
document: makeIntrospectionQueryDocument(introspectionSelection, variableDefinitions),
|
|
96
117
|
rootValue: {},
|
|
118
|
+
variableValues,
|
|
97
119
|
});
|
|
120
|
+
assert(
|
|
121
|
+
!errors || errors.length === 0,
|
|
122
|
+
() => `Introspection query for ${JSON.stringify(introspectionSelection)} should not have failed but got ${JSON.stringify(errors)}`
|
|
123
|
+
);
|
|
98
124
|
assert(data, () => `Introspection query for ${JSON.stringify(introspectionSelection)} should not have failed`);
|
|
99
|
-
return data[introspectionSelection.name.value];
|
|
125
|
+
return data[introspectionSelection.alias?.value ?? introspectionSelection.name.value];
|
|
100
126
|
}
|
|
101
127
|
|
|
102
128
|
export async function executeQueryPlan(
|
|
@@ -163,11 +189,17 @@ export async function executeQueryPlan(
|
|
|
163
189
|
);
|
|
164
190
|
|
|
165
191
|
let postProcessingErrors: GraphQLError[];
|
|
192
|
+
const variables = requestContext.request.variables;
|
|
166
193
|
({ data, errors: postProcessingErrors } = computeResponse({
|
|
167
194
|
operation,
|
|
168
|
-
variables
|
|
195
|
+
variables,
|
|
169
196
|
input: unfilteredData,
|
|
170
|
-
introspectionHandling: (f) => executeIntrospection(
|
|
197
|
+
introspectionHandling: (f) => executeIntrospection(
|
|
198
|
+
operationContext.schema,
|
|
199
|
+
f.expandFragments().toSelectionNode(),
|
|
200
|
+
operationContext.operation.variableDefinitions,
|
|
201
|
+
variables,
|
|
202
|
+
),
|
|
171
203
|
}));
|
|
172
204
|
|
|
173
205
|
// If we have errors during the post-processing, we ignore them if any other errors have been thrown during
|
|
@@ -376,9 +408,12 @@ async function executeFetch(
|
|
|
376
408
|
|
|
377
409
|
if (!fetch.requires) {
|
|
378
410
|
const dataReceivedFromService = await sendOperation(variables);
|
|
411
|
+
if (dataReceivedFromService) {
|
|
412
|
+
applyRewrites(context.supergraphSchema, fetch.outputRewrites, dataReceivedFromService);
|
|
413
|
+
}
|
|
379
414
|
|
|
380
415
|
for (const entity of entities) {
|
|
381
|
-
deepMerge(entity,
|
|
416
|
+
deepMerge(entity, dataReceivedFromService);
|
|
382
417
|
}
|
|
383
418
|
} else {
|
|
384
419
|
const requires = fetch.requires;
|
|
@@ -394,9 +429,9 @@ async function executeFetch(
|
|
|
394
429
|
context.supergraphSchema,
|
|
395
430
|
entity,
|
|
396
431
|
requires,
|
|
397
|
-
fetch.inputRewrites,
|
|
398
432
|
);
|
|
399
433
|
if (representation && representation[TypeNameMetaFieldDef.name]) {
|
|
434
|
+
applyRewrites(context.supergraphSchema, fetch.inputRewrites, representation);
|
|
400
435
|
representations.push(representation);
|
|
401
436
|
representationToEntity.push(index);
|
|
402
437
|
}
|
|
@@ -433,8 +468,11 @@ async function executeFetch(
|
|
|
433
468
|
);
|
|
434
469
|
}
|
|
435
470
|
|
|
471
|
+
|
|
436
472
|
for (let i = 0; i < entities.length; i++) {
|
|
437
|
-
|
|
473
|
+
const receivedEntity = receivedEntities[i];
|
|
474
|
+
applyRewrites(context.supergraphSchema, fetch.outputRewrites, receivedEntity);
|
|
475
|
+
deepMerge(entities[representationToEntity[i]], receivedEntity);
|
|
438
476
|
}
|
|
439
477
|
}
|
|
440
478
|
}
|
|
@@ -667,84 +705,6 @@ export function generateHydratedPaths(
|
|
|
667
705
|
}
|
|
668
706
|
}
|
|
669
707
|
|
|
670
|
-
function applyOrMapRecursive(value: any | any[], fct: (v: any) => any | undefined): any | any[] | undefined {
|
|
671
|
-
if (Array.isArray(value)) {
|
|
672
|
-
const res = value.map((elt) => applyOrMapRecursive(elt, fct)).filter(isDefined);
|
|
673
|
-
return res.length === 0 ? undefined : res;
|
|
674
|
-
}
|
|
675
|
-
return fct(value);
|
|
676
|
-
}
|
|
677
|
-
|
|
678
|
-
function withFetchRewrites(fetchResult: ResultMap | null | void, rewrites: FetchDataOutputRewrite[] | undefined): ResultMap | null | void {
|
|
679
|
-
if (!rewrites || !fetchResult) {
|
|
680
|
-
return fetchResult;
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
for (const rewrite of rewrites) {
|
|
684
|
-
let obj: any = fetchResult;
|
|
685
|
-
let i = 0;
|
|
686
|
-
while (obj && i < rewrite.path.length - 1) {
|
|
687
|
-
const p = rewrite.path[i++];
|
|
688
|
-
if (p.startsWith('... on ')) {
|
|
689
|
-
const typename = p.slice('... on '.length);
|
|
690
|
-
// Filter only objects that match the condition.
|
|
691
|
-
obj = applyOrMapRecursive(obj, (elt) => elt[TypeNameMetaFieldDef.name] === typename ? elt : undefined);
|
|
692
|
-
} else {
|
|
693
|
-
obj = applyOrMapRecursive(obj, (elt) => elt[p]);
|
|
694
|
-
}
|
|
695
|
-
}
|
|
696
|
-
if (obj) {
|
|
697
|
-
applyOrMapRecursive(obj, (elt) => {
|
|
698
|
-
if (typeof elt === 'object') {
|
|
699
|
-
// We need to move the value at path[i] to `renameKeyTo`.
|
|
700
|
-
const removedKey = rewrite.path[i];
|
|
701
|
-
elt[rewrite.renameKeyTo] = elt[removedKey];
|
|
702
|
-
elt[removedKey] = undefined;
|
|
703
|
-
}
|
|
704
|
-
});
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
return fetchResult;
|
|
708
|
-
}
|
|
709
|
-
|
|
710
|
-
function filterEntityRewrites(entity: Record<string, any>, rewrites: FetchDataOutputRewrite[] | undefined): FetchDataOutputRewrite[] | undefined {
|
|
711
|
-
if (!rewrites) {
|
|
712
|
-
return undefined;
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
const typename = entity[TypeNameMetaFieldDef.name] as string;
|
|
716
|
-
const typenameAsFragment = `... on ${typename}`;
|
|
717
|
-
return rewrites.map((r) => r.path[0] === typenameAsFragment ? { ...r, path: r.path.slice(1) } : undefined).filter(isDefined)
|
|
718
|
-
}
|
|
719
|
-
|
|
720
|
-
function updateRewrites(rewrites: FetchDataInputRewrite[] | undefined, pathElement: string): {
|
|
721
|
-
updated: FetchDataInputRewrite[],
|
|
722
|
-
completeRewrite?: any,
|
|
723
|
-
} | undefined {
|
|
724
|
-
if (!rewrites) {
|
|
725
|
-
return undefined;
|
|
726
|
-
}
|
|
727
|
-
|
|
728
|
-
let completeRewrite: any = undefined;
|
|
729
|
-
const updated = rewrites
|
|
730
|
-
.map((r) => {
|
|
731
|
-
let u: FetchDataInputRewrite | undefined = undefined;
|
|
732
|
-
if (r.path[0] === pathElement) {
|
|
733
|
-
const updatedPath = r.path.slice(1);
|
|
734
|
-
if (updatedPath.length === 0) {
|
|
735
|
-
completeRewrite = r.setValueTo;
|
|
736
|
-
} else {
|
|
737
|
-
u = { ...r, path: updatedPath };
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
return u;
|
|
741
|
-
})
|
|
742
|
-
.filter(isDefined);
|
|
743
|
-
return updated.length === 0 && completeRewrite === undefined
|
|
744
|
-
? undefined
|
|
745
|
-
: { updated, completeRewrite };
|
|
746
|
-
}
|
|
747
|
-
|
|
748
708
|
/**
|
|
749
709
|
*
|
|
750
710
|
* @param source Result of GraphQL execution.
|
|
@@ -754,7 +714,6 @@ function executeSelectionSet(
|
|
|
754
714
|
schema: GraphQLSchema,
|
|
755
715
|
source: Record<string, any> | null,
|
|
756
716
|
selections: QueryPlanSelectionNode[],
|
|
757
|
-
activeRewrites?: FetchDataInputRewrite[],
|
|
758
717
|
): Record<string, any> | null {
|
|
759
718
|
|
|
760
719
|
// If the underlying service has returned null for the parent (source)
|
|
@@ -785,16 +744,10 @@ function executeSelectionSet(
|
|
|
785
744
|
return null;
|
|
786
745
|
}
|
|
787
746
|
|
|
788
|
-
const updatedRewrites = updateRewrites(activeRewrites, responseName);
|
|
789
|
-
if (updatedRewrites?.completeRewrite !== undefined) {
|
|
790
|
-
result[responseName] = updatedRewrites.completeRewrite;
|
|
791
|
-
continue;
|
|
792
|
-
}
|
|
793
|
-
|
|
794
747
|
if (Array.isArray(source[responseName])) {
|
|
795
748
|
result[responseName] = source[responseName].map((value: any) =>
|
|
796
749
|
selections
|
|
797
|
-
? executeSelectionSet(schema, value, selections
|
|
750
|
+
? executeSelectionSet(schema, value, selections)
|
|
798
751
|
: value,
|
|
799
752
|
);
|
|
800
753
|
} else if (selections) {
|
|
@@ -802,23 +755,18 @@ function executeSelectionSet(
|
|
|
802
755
|
schema,
|
|
803
756
|
source[responseName],
|
|
804
757
|
selections,
|
|
805
|
-
updatedRewrites?.updated,
|
|
806
758
|
);
|
|
807
759
|
} else {
|
|
808
760
|
result[responseName] = source[responseName];
|
|
809
761
|
}
|
|
810
762
|
break;
|
|
811
763
|
case Kind.INLINE_FRAGMENT:
|
|
812
|
-
if (!selection.typeCondition) continue;
|
|
764
|
+
if (!selection.typeCondition || !source) continue;
|
|
813
765
|
|
|
814
|
-
|
|
815
|
-
if (!typename) continue;
|
|
816
|
-
|
|
817
|
-
if (doesTypeConditionMatch(schema, selection.typeCondition, typename)) {
|
|
818
|
-
const updatedRewrites = activeRewrites ? updateRewrites(activeRewrites, `... on ${selection.typeCondition}`) : undefined;
|
|
766
|
+
if (isObjectOfType(schema, source, selection.typeCondition)) {
|
|
819
767
|
deepMerge(
|
|
820
768
|
result,
|
|
821
|
-
executeSelectionSet(schema, source, selection.selections
|
|
769
|
+
executeSelectionSet(schema, source, selection.selections),
|
|
822
770
|
);
|
|
823
771
|
}
|
|
824
772
|
break;
|
|
@@ -828,32 +776,6 @@ function executeSelectionSet(
|
|
|
828
776
|
return result;
|
|
829
777
|
}
|
|
830
778
|
|
|
831
|
-
function doesTypeConditionMatch(
|
|
832
|
-
schema: GraphQLSchema,
|
|
833
|
-
typeCondition: string,
|
|
834
|
-
typename: string,
|
|
835
|
-
): boolean {
|
|
836
|
-
if (typeCondition === typename) {
|
|
837
|
-
return true;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
|
-
const type = schema.getType(typename);
|
|
841
|
-
if (!type) {
|
|
842
|
-
return false;
|
|
843
|
-
}
|
|
844
|
-
|
|
845
|
-
const conditionalType = schema.getType(typeCondition);
|
|
846
|
-
if (!conditionalType) {
|
|
847
|
-
return false;
|
|
848
|
-
}
|
|
849
|
-
|
|
850
|
-
if (isAbstractType(conditionalType)) {
|
|
851
|
-
return (isObjectType(type) || isInterfaceType(type)) && schema.isSubType(conditionalType, type);
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
return false;
|
|
855
|
-
}
|
|
856
|
-
|
|
857
779
|
function moveIntoCursor(cursor: ResultCursor, pathInCursor: ResponsePath): ResultCursor | undefined {
|
|
858
780
|
const data = flattenResultsAtPath(cursor.data, pathInCursor);
|
|
859
781
|
return data ? {
|