/
getFieldsNotInSubschema.ts
86 lines (73 loc) · 3.11 KB
/
getFieldsNotInSubschema.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
import { GraphQLSchema, FieldNode, GraphQLObjectType, GraphQLResolveInfo } from 'graphql';
import { collectFields, GraphQLExecutionContext, Maybe } from '@graphql-tools/utils';
import { isSubschemaConfig } from './subschemaConfig';
import { MergedTypeInfo, SubschemaConfig, StitchingInfo } from './types';
import { memoizeInfoAnd2Objects } from './memoize';
function collectSubFields(info: GraphQLResolveInfo, typeName: string): Record<string, Array<FieldNode>> {
let subFieldNodes: Record<string, Array<FieldNode>> = Object.create(null);
const visitedFragmentNames = Object.create(null);
const type = info.schema.getType(typeName) as GraphQLObjectType;
const partialExecutionContext = {
schema: info.schema,
variableValues: info.variableValues,
fragments: info.fragments,
} as unknown as GraphQLExecutionContext;
for (const fieldNode of info.fieldNodes) {
if (fieldNode.selectionSet) {
subFieldNodes = collectFields(
partialExecutionContext,
type,
fieldNode.selectionSet,
subFieldNodes,
visitedFragmentNames
);
}
}
// TODO: Verify whether it is safe that extensions always exists.
const stitchingInfo: Maybe<StitchingInfo> = info.schema.extensions?.['stitchingInfo'];
const fieldNodesByField = stitchingInfo?.fieldNodesByField;
const subFieldNodesByFieldName = Object.create(null);
for (const responseKey in subFieldNodes) {
const fieldName = subFieldNodes[responseKey][0].name.value;
const additionalFieldNodes = fieldNodesByField?.[typeName]?.[fieldName];
if (additionalFieldNodes) {
for (const additionalFieldNode of additionalFieldNodes) {
const additionalFieldName = additionalFieldNode.name.value;
if (subFieldNodesByFieldName[additionalFieldName] == null) {
subFieldNodesByFieldName[additionalFieldName] = [additionalFieldNode];
} else {
subFieldNodesByFieldName[additionalFieldName].push(additionalFieldNode);
}
}
}
}
for (const responseKey in subFieldNodes) {
const fieldName = subFieldNodes[responseKey][0].name.value;
if (subFieldNodesByFieldName[fieldName] == null) {
subFieldNodesByFieldName[fieldName] = subFieldNodes[responseKey];
} else {
subFieldNodesByFieldName[fieldName].concat(subFieldNodes[responseKey]);
}
}
return subFieldNodesByFieldName;
}
export const getFieldsNotInSubschema = memoizeInfoAnd2Objects(function (
info: GraphQLResolveInfo,
subschema: GraphQLSchema | SubschemaConfig<any, any, any, any>,
mergedTypeInfo: MergedTypeInfo
): Array<FieldNode> {
const typeMap = isSubschemaConfig(subschema) ? mergedTypeInfo.typeMaps.get(subschema) : subschema.getTypeMap();
if (!typeMap) {
return [];
}
const typeName = mergedTypeInfo.typeName;
const fields = (typeMap[typeName] as GraphQLObjectType).getFields();
const subFieldNodes = collectSubFields(info, typeName);
let fieldsNotInSchema: Array<FieldNode> = [];
for (const fieldName in subFieldNodes) {
if (!(fieldName in fields)) {
fieldsNotInSchema = fieldsNotInSchema.concat(subFieldNodes[fieldName]);
}
}
return fieldsNotInSchema;
});