@wundergraph/composition 0.37.0 → 0.37.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/README.md +41 -48
- package/dist/ast/utils.js +2 -2
- package/dist/ast/utils.js.map +1 -1
- package/dist/errors/errors.d.ts +9 -12
- package/dist/errors/errors.js +71 -68
- package/dist/errors/errors.js.map +1 -1
- package/dist/federation/types.d.ts +2 -2
- package/dist/index.d.ts +4 -2
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/normalization/normalization.d.ts +3 -1
- package/dist/normalization/normalization.js +8 -0
- package/dist/normalization/normalization.js.map +1 -1
- package/dist/normalization/types.d.ts +2 -2
- package/dist/resolvability-graph/graph-nodes.d.ts +1 -1
- package/dist/resolvability-graph/graph-nodes.js.map +1 -1
- package/dist/resolvability-graph/graph.d.ts +1 -2
- package/dist/resolvability-graph/graph.js.map +1 -1
- package/dist/resolvability-graph/utils.d.ts +1 -1
- package/dist/resolvability-graph/utils.js.map +1 -1
- package/dist/router-configuration/{router-configuration.d.ts → types.d.ts} +3 -4
- package/dist/router-configuration/types.js +3 -0
- package/dist/router-configuration/types.js.map +1 -0
- package/dist/router-configuration/utils.d.ts +3 -0
- package/dist/router-configuration/{router-configuration.js → utils.js} +9 -1
- package/dist/router-configuration/utils.js.map +1 -0
- package/dist/schema-building/types.d.ts +23 -7
- package/dist/schema-building/types.js.map +1 -1
- package/dist/schema-building/utils.d.ts +3 -6
- package/dist/schema-building/utils.js +11 -8
- package/dist/schema-building/utils.js.map +1 -1
- package/dist/subgraph/types.d.ts +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/utils/composition-version.js +1 -1
- package/dist/utils/string-constants.d.ts +3 -2
- package/dist/utils/string-constants.js +5 -4
- package/dist/utils/string-constants.js.map +1 -1
- package/dist/utils/types.d.ts +38 -0
- package/dist/utils/utils.d.ts +0 -48
- package/dist/utils/utils.js +0 -10
- package/dist/utils/utils.js.map +1 -1
- package/dist/v1/federation/federation-factory.d.ts +4 -4
- package/dist/v1/federation/federation-factory.js +46 -35
- package/dist/v1/federation/federation-factory.js.map +1 -1
- package/dist/v1/federation/utils.d.ts +7 -8
- package/dist/v1/federation/utils.js +22 -20
- package/dist/v1/federation/utils.js.map +1 -1
- package/dist/v1/normalization/normalization-factory.d.ts +19 -22
- package/dist/v1/normalization/normalization-factory.js +568 -70
- package/dist/v1/normalization/normalization-factory.js.map +1 -1
- package/dist/v1/normalization/types.d.ts +49 -0
- package/dist/v1/normalization/types.js +3 -0
- package/dist/v1/normalization/types.js.map +1 -0
- package/dist/v1/normalization/utils.d.ts +7 -42
- package/dist/v1/normalization/utils.js +59 -382
- package/dist/v1/normalization/utils.js.map +1 -1
- package/dist/v1/normalization/walkers.js +36 -23
- package/dist/v1/normalization/walkers.js.map +1 -1
- package/dist/v1/utils/utils.d.ts +15 -12
- package/dist/v1/utils/utils.js +53 -47
- package/dist/v1/utils/utils.js.map +1 -1
- package/dist/v1/warnings/warnings.d.ts +4 -3
- package/dist/v1/warnings/warnings.js +47 -17
- package/dist/v1/warnings/warnings.js.map +1 -1
- package/dist/warnings/{warnings.js → types.js} +1 -1
- package/dist/warnings/types.js.map +1 -0
- package/package.json +2 -2
- package/dist/router-configuration/router-configuration.js.map +0 -1
- package/dist/warnings/warnings.js.map +0 -1
- /package/dist/warnings/{warnings.d.ts → types.d.ts} +0 -0
|
@@ -24,6 +24,8 @@ const integer_constants_1 = require("../utils/integer-constants");
|
|
|
24
24
|
const string_constants_2 = require("../../utils/string-constants");
|
|
25
25
|
const integer_constants_2 = require("../../utils/integer-constants");
|
|
26
26
|
const utils_5 = require("../../utils/utils");
|
|
27
|
+
const index_1 = require("graphql/index");
|
|
28
|
+
const utils_6 = require("../../router-configuration/utils");
|
|
27
29
|
function normalizeSubgraphFromString(subgraphSDL, noLocation = true) {
|
|
28
30
|
const { error, documentNode } = (0, utils_1.safeParse)(subgraphSDL, noLocation);
|
|
29
31
|
if (error || !documentNode) {
|
|
@@ -41,8 +43,8 @@ class NormalizationFactory {
|
|
|
41
43
|
authorizationDataByParentTypeName = new Map();
|
|
42
44
|
childName = '';
|
|
43
45
|
concreteTypeNamesByAbstractTypeName = new Map();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
+
conditionalFieldDataByCoords = new Map();
|
|
47
|
+
configurationDataByTypeName = new Map();
|
|
46
48
|
customDirectiveDefinitions = new Map();
|
|
47
49
|
definedDirectiveNames = new Set();
|
|
48
50
|
directiveDefinitionByDirectiveName = new Map();
|
|
@@ -52,7 +54,8 @@ class NormalizationFactory {
|
|
|
52
54
|
entityDataByTypeName = new Map();
|
|
53
55
|
entityInterfaceDataByTypeName = new Map();
|
|
54
56
|
eventsConfigurations = new Map();
|
|
55
|
-
|
|
57
|
+
fieldSetDataByTypeName = new Map();
|
|
58
|
+
heirFieldAuthorizationDataByTypeName = new Map();
|
|
56
59
|
interfaceTypeNamesWithAuthorizationDirectives = new Set();
|
|
57
60
|
internalGraph;
|
|
58
61
|
invalidConfigureDescriptionNodeDatas = [];
|
|
@@ -62,8 +65,7 @@ class NormalizationFactory {
|
|
|
62
65
|
isParentObjectShareable = false;
|
|
63
66
|
isSubgraphEventDrivenGraph = false;
|
|
64
67
|
isSubgraphVersionTwo = false;
|
|
65
|
-
|
|
66
|
-
heirFieldAuthorizationDataByTypeName = new Map();
|
|
68
|
+
keyFieldSetDatasByTypeName = new Map();
|
|
67
69
|
lastParentNodeKind = graphql_1.Kind.NULL;
|
|
68
70
|
lastChildNodeKind = graphql_1.Kind.NULL;
|
|
69
71
|
leafTypeNamesWithAuthorizationDirectives = new Set();
|
|
@@ -81,6 +83,7 @@ class NormalizationFactory {
|
|
|
81
83
|
referencedTypeNames = new Set();
|
|
82
84
|
renamedParentTypeName = '';
|
|
83
85
|
subgraphName;
|
|
86
|
+
unvalidatedExternalFieldCoords = new Set();
|
|
84
87
|
usesEdfsNatsStreamConfiguration = false;
|
|
85
88
|
warnings = [];
|
|
86
89
|
constructor(internalGraph, subgraphName) {
|
|
@@ -488,13 +491,13 @@ class NormalizationFactory {
|
|
|
488
491
|
/*
|
|
489
492
|
* @extends is not interpreted as an extension under the following circumstances:
|
|
490
493
|
* 1. It's a root type
|
|
491
|
-
* 2. It's a V2 subgraph
|
|
494
|
+
* 2. It's a V2 subgraph (but extends is temporarily propagated to handle @external key fields)
|
|
492
495
|
* 3. And (of course) if @extends isn't defined at all
|
|
493
496
|
*/
|
|
494
|
-
if (isRootType ||
|
|
497
|
+
if (isRootType || !directivesByDirectiveName.has(string_constants_2.EXTENDS)) {
|
|
495
498
|
return types_1.ExtensionType.NONE;
|
|
496
499
|
}
|
|
497
|
-
// If
|
|
500
|
+
// If a V1 non-root Object defines @extends, it is considered an extension across subgraphs.
|
|
498
501
|
return types_1.ExtensionType.EXTENDS;
|
|
499
502
|
}
|
|
500
503
|
setParentDataExtensionType(parentData, incomingExtensionType) {
|
|
@@ -725,27 +728,25 @@ class NormalizationFactory {
|
|
|
725
728
|
}
|
|
726
729
|
addFieldDataByNode(fieldDataByFieldName, node, argumentDataByArgumentName, directivesByDirectiveName) {
|
|
727
730
|
const name = node.name.value;
|
|
728
|
-
const
|
|
729
|
-
const
|
|
731
|
+
const fieldCoords = `${this.originalParentTypeName}.${name}`;
|
|
732
|
+
const { isExternal, isShareable } = (0, utils_4.isNodeExternalOrShareable)(node, !this.isSubgraphVersionTwo, directivesByDirectiveName);
|
|
730
733
|
const fieldData = {
|
|
731
734
|
argumentDataByArgumentName: argumentDataByArgumentName,
|
|
732
735
|
configureDescriptionDataBySubgraphName: new Map(),
|
|
733
|
-
|
|
734
|
-
[this.subgraphName,
|
|
736
|
+
externalFieldDataBySubgraphName: new Map([
|
|
737
|
+
[this.subgraphName, (0, utils_4.newExternalFieldData)(isExternal)],
|
|
735
738
|
]),
|
|
736
739
|
isInaccessible: directivesByDirectiveName.has(string_constants_2.INACCESSIBLE),
|
|
737
|
-
isShareableBySubgraphName: new Map([
|
|
738
|
-
[this.subgraphName, isNodeExternalOrShareableResult.isShareable],
|
|
739
|
-
]),
|
|
740
|
+
isShareableBySubgraphName: new Map([[this.subgraphName, isShareable]]),
|
|
740
741
|
kind: graphql_1.Kind.FIELD_DEFINITION,
|
|
741
742
|
name,
|
|
742
743
|
namedTypeName: (0, ast_1.getTypeNodeNamedTypeName)(node.type),
|
|
743
|
-
node: (0, ast_1.getMutableFieldNode)(node,
|
|
744
|
+
node: (0, ast_1.getMutableFieldNode)(node, fieldCoords, this.errors),
|
|
744
745
|
originalParentTypeName: this.originalParentTypeName,
|
|
745
746
|
persistedDirectivesData: (0, utils_4.newPersistedDirectivesData)(),
|
|
746
747
|
renamedParentTypeName: this.renamedParentTypeName || this.originalParentTypeName,
|
|
747
748
|
subgraphNames: new Set([this.subgraphName]),
|
|
748
|
-
type: (0, ast_1.getMutableTypeNode)(node.type,
|
|
749
|
+
type: (0, ast_1.getMutableTypeNode)(node.type, fieldCoords, this.errors),
|
|
749
750
|
directivesByDirectiveName,
|
|
750
751
|
description: (0, utils_1.formatDescription)(node.description),
|
|
751
752
|
};
|
|
@@ -1031,23 +1032,24 @@ class NormalizationFactory {
|
|
|
1031
1032
|
this.extractConfigureDescriptionsData(newParentData);
|
|
1032
1033
|
this.parentDefinitionDataByTypeName.set(typeName, newParentData);
|
|
1033
1034
|
}
|
|
1034
|
-
extractKeyFieldSets(node,
|
|
1035
|
-
const isUnresolvableByRawKeyFieldSet = keyFieldSetData.isUnresolvableByKeyFieldSet;
|
|
1035
|
+
extractKeyFieldSets(node, keyFieldSetDataByFieldSet) {
|
|
1036
1036
|
const parentTypeName = node.name.value;
|
|
1037
1037
|
if (!node.directives?.length) {
|
|
1038
1038
|
// This should never happen
|
|
1039
1039
|
this.errors.push((0, errors_1.expectedEntityError)(parentTypeName));
|
|
1040
1040
|
return;
|
|
1041
1041
|
}
|
|
1042
|
-
// validation happens elsewhere
|
|
1042
|
+
// full validation happens elsewhere
|
|
1043
|
+
let keyNumber = 0;
|
|
1043
1044
|
for (const directive of node.directives) {
|
|
1044
1045
|
if (directive.name.value !== string_constants_2.KEY) {
|
|
1045
1046
|
continue;
|
|
1046
1047
|
}
|
|
1048
|
+
keyNumber += 1;
|
|
1047
1049
|
if (!directive.arguments || directive.arguments.length < 1) {
|
|
1048
1050
|
continue;
|
|
1049
1051
|
}
|
|
1050
|
-
let
|
|
1052
|
+
let rawFieldSet;
|
|
1051
1053
|
let isUnresolvable = false;
|
|
1052
1054
|
for (const arg of directive.arguments) {
|
|
1053
1055
|
if (arg.name.value === string_constants_2.RESOLVABLE) {
|
|
@@ -1057,18 +1059,339 @@ class NormalizationFactory {
|
|
|
1057
1059
|
continue;
|
|
1058
1060
|
}
|
|
1059
1061
|
if (arg.name.value !== string_constants_2.FIELDS) {
|
|
1060
|
-
|
|
1062
|
+
rawFieldSet = undefined;
|
|
1061
1063
|
break;
|
|
1062
1064
|
}
|
|
1063
1065
|
if (arg.value.kind !== graphql_1.Kind.STRING) {
|
|
1064
|
-
|
|
1066
|
+
rawFieldSet = undefined;
|
|
1065
1067
|
break;
|
|
1066
1068
|
}
|
|
1067
|
-
|
|
1069
|
+
rawFieldSet = arg.value.value;
|
|
1068
1070
|
}
|
|
1069
|
-
if (
|
|
1070
|
-
|
|
1071
|
+
if (rawFieldSet === undefined) {
|
|
1072
|
+
continue;
|
|
1071
1073
|
}
|
|
1074
|
+
const { error, documentNode } = (0, utils_1.safeParse)('{' + rawFieldSet + '}');
|
|
1075
|
+
if (error || !documentNode) {
|
|
1076
|
+
this.errors.push((0, errors_1.invalidDirectiveError)(string_constants_2.KEY, parentTypeName, (0, utils_5.numberToOrdinal)(keyNumber), [
|
|
1077
|
+
(0, errors_1.unparsableFieldSetErrorMessage)(rawFieldSet, error),
|
|
1078
|
+
]));
|
|
1079
|
+
continue;
|
|
1080
|
+
}
|
|
1081
|
+
const normalizedFieldSet = (0, utils_2.getNormalizedFieldSet)(documentNode);
|
|
1082
|
+
const keyFieldSetData = keyFieldSetDataByFieldSet.get(normalizedFieldSet);
|
|
1083
|
+
if (keyFieldSetData) {
|
|
1084
|
+
// Duplicate keys should potentially be a warning. For now, simply propagate if it's resolvable.
|
|
1085
|
+
keyFieldSetData.isUnresolvable ||= isUnresolvable;
|
|
1086
|
+
}
|
|
1087
|
+
else {
|
|
1088
|
+
keyFieldSetDataByFieldSet.set(normalizedFieldSet, {
|
|
1089
|
+
documentNode,
|
|
1090
|
+
isUnresolvable,
|
|
1091
|
+
normalizedFieldSet,
|
|
1092
|
+
rawFieldSet,
|
|
1093
|
+
});
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
getFieldSetParent(isProvides, parentData, fieldName, parentTypeName) {
|
|
1098
|
+
if (!isProvides) {
|
|
1099
|
+
return { fieldSetParentData: parentData };
|
|
1100
|
+
}
|
|
1101
|
+
const fieldData = (0, utils_5.getOrThrowError)(parentData.fieldDataByFieldName, fieldName, `${parentTypeName}.fieldDataByFieldName`);
|
|
1102
|
+
const fieldNamedTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
|
|
1103
|
+
const namedTypeData = this.parentDefinitionDataByTypeName.get(fieldNamedTypeName);
|
|
1104
|
+
// This error should never happen
|
|
1105
|
+
if (!namedTypeData) {
|
|
1106
|
+
return {
|
|
1107
|
+
errorString: (0, errors_1.unknownNamedTypeErrorMessage)(`${parentTypeName}.${fieldName}`, fieldNamedTypeName),
|
|
1108
|
+
};
|
|
1109
|
+
}
|
|
1110
|
+
if (namedTypeData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION && namedTypeData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
|
|
1111
|
+
return {
|
|
1112
|
+
errorString: (0, errors_1.incompatibleTypeWithProvidesErrorMessage)(`${parentTypeName}.${fieldName}`, fieldNamedTypeName),
|
|
1113
|
+
};
|
|
1114
|
+
}
|
|
1115
|
+
return { fieldSetParentData: namedTypeData };
|
|
1116
|
+
}
|
|
1117
|
+
validateConditionalFieldSet(selectionSetParentData, fieldSet, directiveFieldName, isProvides, directiveParentTypeName) {
|
|
1118
|
+
// Create a new selection set so that the value can be parsed as a new DocumentNode
|
|
1119
|
+
const { error, documentNode } = (0, utils_1.safeParse)('{' + fieldSet + '}');
|
|
1120
|
+
if (error || !documentNode) {
|
|
1121
|
+
return { errorMessages: [(0, errors_1.unparsableFieldSetErrorMessage)(fieldSet, error)] };
|
|
1122
|
+
}
|
|
1123
|
+
const nf = this;
|
|
1124
|
+
const parentDatas = [selectionSetParentData];
|
|
1125
|
+
const directiveName = (0, utils_2.getConditionalFieldSetDirectiveName)(isProvides);
|
|
1126
|
+
const definedFields = [];
|
|
1127
|
+
const directiveCoords = `${directiveParentTypeName}.${directiveFieldName}`;
|
|
1128
|
+
const fieldCoordsPath = (0, utils_2.getInitialFieldCoordsPath)(isProvides, directiveCoords);
|
|
1129
|
+
const fieldPath = [directiveFieldName];
|
|
1130
|
+
const externalAncestors = new Set();
|
|
1131
|
+
const errorMessages = [];
|
|
1132
|
+
let currentDepth = -1;
|
|
1133
|
+
let shouldDefineSelectionSet = true;
|
|
1134
|
+
let lastFieldName = directiveFieldName;
|
|
1135
|
+
let hasConditionalField = false;
|
|
1136
|
+
(0, index_1.visit)(documentNode, {
|
|
1137
|
+
Argument: {
|
|
1138
|
+
enter() {
|
|
1139
|
+
return false;
|
|
1140
|
+
},
|
|
1141
|
+
},
|
|
1142
|
+
Field: {
|
|
1143
|
+
enter(node) {
|
|
1144
|
+
const parentData = parentDatas[currentDepth];
|
|
1145
|
+
const parentTypeName = parentData.name;
|
|
1146
|
+
if (parentData.kind === graphql_1.Kind.UNION_TYPE_DEFINITION) {
|
|
1147
|
+
errorMessages.push((0, errors_1.invalidSelectionOnUnionErrorMessage)(fieldSet, fieldCoordsPath, parentTypeName));
|
|
1148
|
+
return index_1.BREAK;
|
|
1149
|
+
}
|
|
1150
|
+
const fieldName = node.name.value;
|
|
1151
|
+
const currentFieldCoords = `${parentTypeName}.${fieldName}`;
|
|
1152
|
+
nf.unvalidatedExternalFieldCoords.delete(currentFieldCoords);
|
|
1153
|
+
// If an object-like was just visited, a selection set should have been entered
|
|
1154
|
+
if (shouldDefineSelectionSet) {
|
|
1155
|
+
errorMessages.push((0, errors_1.invalidSelectionSetErrorMessage)(fieldSet, fieldCoordsPath, parentTypeName, (0, utils_5.kindToTypeString)(parentData.kind)));
|
|
1156
|
+
return index_1.BREAK;
|
|
1157
|
+
}
|
|
1158
|
+
fieldCoordsPath.push(currentFieldCoords);
|
|
1159
|
+
fieldPath.push(fieldName);
|
|
1160
|
+
lastFieldName = fieldName;
|
|
1161
|
+
const fieldData = parentData.fieldDataByFieldName.get(fieldName);
|
|
1162
|
+
// undefined if the field does not exist on the parent
|
|
1163
|
+
if (!fieldData) {
|
|
1164
|
+
errorMessages.push((0, errors_1.undefinedFieldInFieldSetErrorMessage)(fieldSet, parentTypeName, fieldName));
|
|
1165
|
+
return index_1.BREAK;
|
|
1166
|
+
}
|
|
1167
|
+
if (definedFields[currentDepth].has(fieldName)) {
|
|
1168
|
+
errorMessages.push((0, errors_1.duplicateFieldInFieldSetErrorMessage)(fieldSet, currentFieldCoords));
|
|
1169
|
+
return index_1.BREAK;
|
|
1170
|
+
}
|
|
1171
|
+
definedFields[currentDepth].add(fieldName);
|
|
1172
|
+
const { isDefinedExternal, isUnconditionallyProvided } = (0, utils_5.getOrThrowError)(fieldData.externalFieldDataBySubgraphName, nf.subgraphName, `${currentFieldCoords}.externalFieldDataBySubgraphName`);
|
|
1173
|
+
const isFieldConditional = isDefinedExternal && !isUnconditionallyProvided;
|
|
1174
|
+
if (!isUnconditionallyProvided) {
|
|
1175
|
+
hasConditionalField = true;
|
|
1176
|
+
}
|
|
1177
|
+
const namedTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
|
|
1178
|
+
// The child could itself be a parent
|
|
1179
|
+
const namedTypeData = nf.parentDefinitionDataByTypeName.get(namedTypeName);
|
|
1180
|
+
// The base scalars are not in the parents map
|
|
1181
|
+
if (constants_1.BASE_SCALARS.has(namedTypeName) ||
|
|
1182
|
+
namedTypeData?.kind === graphql_1.Kind.SCALAR_TYPE_DEFINITION ||
|
|
1183
|
+
namedTypeData?.kind === graphql_1.Kind.ENUM_TYPE_DEFINITION) {
|
|
1184
|
+
if (externalAncestors.size < 1 && !isDefinedExternal) {
|
|
1185
|
+
if (nf.isSubgraphVersionTwo) {
|
|
1186
|
+
nf.errors.push((0, errors_1.nonExternalConditionalFieldError)(directiveCoords, nf.subgraphName, currentFieldCoords, fieldSet, directiveName));
|
|
1187
|
+
return;
|
|
1188
|
+
}
|
|
1189
|
+
/* In V1, @requires and @provides do not need to declare any part of the field set @external.
|
|
1190
|
+
* It would appear that any such non-external fields are treated as if they are non-conditionally provided.
|
|
1191
|
+
* */
|
|
1192
|
+
nf.warnings.push((0, warnings_1.nonExternalConditionalFieldWarning)(directiveCoords, nf.subgraphName, currentFieldCoords, fieldSet, directiveName));
|
|
1193
|
+
return;
|
|
1194
|
+
}
|
|
1195
|
+
if (externalAncestors.size < 1 && isUnconditionallyProvided) {
|
|
1196
|
+
// V2 subgraphs return an error when an external key field on an entity extension is provided.
|
|
1197
|
+
if (nf.isSubgraphVersionTwo) {
|
|
1198
|
+
errorMessages.push((0, errors_1.fieldAlreadyProvidedErrorMessage)(currentFieldCoords, nf.subgraphName, directiveName));
|
|
1199
|
+
}
|
|
1200
|
+
else {
|
|
1201
|
+
nf.warnings.push((0, warnings_1.fieldAlreadyProvidedWarning)(currentFieldCoords, directiveName, directiveCoords, nf.subgraphName));
|
|
1202
|
+
}
|
|
1203
|
+
return;
|
|
1204
|
+
}
|
|
1205
|
+
// @TODO re-assess in v2 because this would be breaking for @provides in v1
|
|
1206
|
+
if (!isFieldConditional && !isProvides) {
|
|
1207
|
+
// Do not add unnecessary @requires configurations
|
|
1208
|
+
return;
|
|
1209
|
+
}
|
|
1210
|
+
const conditionalFieldData = (0, utils_5.getValueOrDefault)(nf.conditionalFieldDataByCoords, currentFieldCoords, utils_4.newConditionalFieldData);
|
|
1211
|
+
const fieldSetCondition = (0, utils_6.newFieldSetConditionData)({
|
|
1212
|
+
fieldCoordinatesPath: [...fieldCoordsPath],
|
|
1213
|
+
fieldPath: [...fieldPath],
|
|
1214
|
+
});
|
|
1215
|
+
isProvides
|
|
1216
|
+
? conditionalFieldData.providedBy.push(fieldSetCondition)
|
|
1217
|
+
: conditionalFieldData.requiredBy.push(fieldSetCondition);
|
|
1218
|
+
return;
|
|
1219
|
+
}
|
|
1220
|
+
if (!namedTypeData) {
|
|
1221
|
+
// Should not be possible to receive this error
|
|
1222
|
+
errorMessages.push((0, errors_1.unknownTypeInFieldSetErrorMessage)(fieldSet, currentFieldCoords, namedTypeName));
|
|
1223
|
+
return index_1.BREAK;
|
|
1224
|
+
}
|
|
1225
|
+
// TODO isFieldConditional
|
|
1226
|
+
if (isDefinedExternal) {
|
|
1227
|
+
if (isProvides) {
|
|
1228
|
+
(0, utils_5.getValueOrDefault)(nf.conditionalFieldDataByCoords, currentFieldCoords, utils_4.newConditionalFieldData).providedBy.push((0, utils_6.newFieldSetConditionData)({
|
|
1229
|
+
fieldCoordinatesPath: [...fieldCoordsPath],
|
|
1230
|
+
fieldPath: [...fieldPath],
|
|
1231
|
+
}));
|
|
1232
|
+
}
|
|
1233
|
+
externalAncestors.add(currentFieldCoords);
|
|
1234
|
+
}
|
|
1235
|
+
if (namedTypeData.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION ||
|
|
1236
|
+
namedTypeData.kind === graphql_1.Kind.INTERFACE_TYPE_DEFINITION ||
|
|
1237
|
+
namedTypeData.kind === graphql_1.Kind.UNION_TYPE_DEFINITION) {
|
|
1238
|
+
shouldDefineSelectionSet = true;
|
|
1239
|
+
parentDatas.push(namedTypeData);
|
|
1240
|
+
return;
|
|
1241
|
+
}
|
|
1242
|
+
},
|
|
1243
|
+
leave() {
|
|
1244
|
+
externalAncestors.delete(fieldCoordsPath.pop() || '');
|
|
1245
|
+
fieldPath.pop();
|
|
1246
|
+
},
|
|
1247
|
+
},
|
|
1248
|
+
InlineFragment: {
|
|
1249
|
+
enter(node) {
|
|
1250
|
+
const parentData = parentDatas[currentDepth];
|
|
1251
|
+
const parentTypeName = parentData.name;
|
|
1252
|
+
const fieldCoordinates = fieldCoordsPath.length < 1 ? selectionSetParentData.name : fieldCoordsPath[fieldCoordsPath.length - 1];
|
|
1253
|
+
if (!node.typeCondition) {
|
|
1254
|
+
errorMessages.push((0, errors_1.inlineFragmentWithoutTypeConditionErrorMessage)(fieldSet, fieldCoordinates));
|
|
1255
|
+
return index_1.BREAK;
|
|
1256
|
+
}
|
|
1257
|
+
const typeConditionName = node.typeCondition.name.value;
|
|
1258
|
+
// It's possible to infinitely define fragments
|
|
1259
|
+
if (typeConditionName === parentTypeName) {
|
|
1260
|
+
parentDatas.push(parentData);
|
|
1261
|
+
shouldDefineSelectionSet = true;
|
|
1262
|
+
return;
|
|
1263
|
+
}
|
|
1264
|
+
if (!(0, utils_1.isKindAbstract)(parentData.kind)) {
|
|
1265
|
+
errorMessages.push((0, errors_1.invalidInlineFragmentTypeErrorMessage)(fieldSet, fieldCoordsPath, typeConditionName, parentTypeName));
|
|
1266
|
+
return index_1.BREAK;
|
|
1267
|
+
}
|
|
1268
|
+
const fragmentNamedTypeData = nf.parentDefinitionDataByTypeName.get(typeConditionName);
|
|
1269
|
+
if (!fragmentNamedTypeData) {
|
|
1270
|
+
errorMessages.push((0, errors_1.unknownInlineFragmentTypeConditionErrorMessage)(fieldSet, fieldCoordsPath, parentTypeName, typeConditionName));
|
|
1271
|
+
return index_1.BREAK;
|
|
1272
|
+
}
|
|
1273
|
+
shouldDefineSelectionSet = true;
|
|
1274
|
+
switch (fragmentNamedTypeData.kind) {
|
|
1275
|
+
case graphql_1.Kind.INTERFACE_TYPE_DEFINITION: {
|
|
1276
|
+
if (!fragmentNamedTypeData.implementedInterfaceTypeNames.has(parentTypeName)) {
|
|
1277
|
+
break;
|
|
1278
|
+
}
|
|
1279
|
+
parentDatas.push(fragmentNamedTypeData);
|
|
1280
|
+
return;
|
|
1281
|
+
}
|
|
1282
|
+
case graphql_1.Kind.OBJECT_TYPE_DEFINITION: {
|
|
1283
|
+
const concreteTypeNames = nf.concreteTypeNamesByAbstractTypeName.get(parentTypeName);
|
|
1284
|
+
if (!concreteTypeNames || !concreteTypeNames.has(typeConditionName)) {
|
|
1285
|
+
break;
|
|
1286
|
+
}
|
|
1287
|
+
parentDatas.push(fragmentNamedTypeData);
|
|
1288
|
+
return;
|
|
1289
|
+
}
|
|
1290
|
+
case graphql_1.Kind.UNION_TYPE_DEFINITION: {
|
|
1291
|
+
parentDatas.push(fragmentNamedTypeData);
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
default: {
|
|
1295
|
+
errorMessages.push((0, errors_1.invalidInlineFragmentTypeConditionTypeErrorMessage)(fieldSet, fieldCoordsPath, parentTypeName, typeConditionName, (0, utils_5.kindToTypeString)(fragmentNamedTypeData.kind)));
|
|
1296
|
+
return index_1.BREAK;
|
|
1297
|
+
}
|
|
1298
|
+
}
|
|
1299
|
+
errorMessages.push((0, errors_1.invalidInlineFragmentTypeConditionErrorMessage)(fieldSet, fieldCoordsPath, typeConditionName, (0, utils_5.kindToTypeString)(parentData.kind), parentTypeName));
|
|
1300
|
+
return index_1.BREAK;
|
|
1301
|
+
},
|
|
1302
|
+
},
|
|
1303
|
+
SelectionSet: {
|
|
1304
|
+
enter() {
|
|
1305
|
+
if (!shouldDefineSelectionSet) {
|
|
1306
|
+
const parentData = parentDatas[currentDepth];
|
|
1307
|
+
if (parentData.kind === graphql_1.Kind.UNION_TYPE_DEFINITION) {
|
|
1308
|
+
// Should never happen
|
|
1309
|
+
errorMessages.push((0, errors_1.unparsableFieldSetSelectionErrorMessage)(fieldSet, lastFieldName));
|
|
1310
|
+
return index_1.BREAK;
|
|
1311
|
+
}
|
|
1312
|
+
const fieldData = parentData.fieldDataByFieldName.get(lastFieldName);
|
|
1313
|
+
if (!fieldData) {
|
|
1314
|
+
errorMessages.push((0, errors_1.undefinedFieldInFieldSetErrorMessage)(fieldSet, parentData.name, lastFieldName));
|
|
1315
|
+
return index_1.BREAK;
|
|
1316
|
+
}
|
|
1317
|
+
const fieldNamedTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
|
|
1318
|
+
// If the child is not found, it's a base scalar. Undefined types would have already been handled.
|
|
1319
|
+
const namedTypeData = nf.parentDefinitionDataByTypeName.get(fieldNamedTypeName);
|
|
1320
|
+
const childKind = namedTypeData ? namedTypeData.kind : graphql_1.Kind.SCALAR_TYPE_DEFINITION;
|
|
1321
|
+
errorMessages.push((0, errors_1.invalidSelectionSetDefinitionErrorMessage)(fieldSet, fieldCoordsPath, fieldNamedTypeName, (0, utils_5.kindToTypeString)(childKind)));
|
|
1322
|
+
return index_1.BREAK;
|
|
1323
|
+
}
|
|
1324
|
+
currentDepth += 1;
|
|
1325
|
+
shouldDefineSelectionSet = false;
|
|
1326
|
+
if (currentDepth < 0 || currentDepth >= parentDatas.length) {
|
|
1327
|
+
errorMessages.push((0, errors_1.unparsableFieldSetSelectionErrorMessage)(fieldSet, lastFieldName));
|
|
1328
|
+
return index_1.BREAK;
|
|
1329
|
+
}
|
|
1330
|
+
definedFields.push(new Set());
|
|
1331
|
+
},
|
|
1332
|
+
leave() {
|
|
1333
|
+
if (shouldDefineSelectionSet) {
|
|
1334
|
+
const parentData = parentDatas[currentDepth + 1];
|
|
1335
|
+
errorMessages.push((0, errors_1.invalidSelectionSetErrorMessage)(fieldSet, fieldCoordsPath, parentData.name, (0, utils_5.kindToTypeString)(parentData.kind)));
|
|
1336
|
+
shouldDefineSelectionSet = false;
|
|
1337
|
+
}
|
|
1338
|
+
// Empty selection sets would be a parse error, so it is unnecessary to handle them
|
|
1339
|
+
currentDepth -= 1;
|
|
1340
|
+
parentDatas.pop();
|
|
1341
|
+
definedFields.pop();
|
|
1342
|
+
},
|
|
1343
|
+
},
|
|
1344
|
+
});
|
|
1345
|
+
if (errorMessages.length > 0 || !hasConditionalField) {
|
|
1346
|
+
return { errorMessages };
|
|
1347
|
+
}
|
|
1348
|
+
return {
|
|
1349
|
+
configuration: { fieldName: directiveFieldName, selectionSet: (0, utils_2.getNormalizedFieldSet)(documentNode) },
|
|
1350
|
+
errorMessages,
|
|
1351
|
+
};
|
|
1352
|
+
}
|
|
1353
|
+
validateProvidesOrRequires(parentData, fieldSetByFieldName, isProvides) {
|
|
1354
|
+
const allErrorMessages = [];
|
|
1355
|
+
const configurations = [];
|
|
1356
|
+
const parentTypeName = (0, utils_4.getParentTypeName)(parentData);
|
|
1357
|
+
for (const [fieldName, fieldSet] of fieldSetByFieldName) {
|
|
1358
|
+
/* It is possible to encounter a field before encountering the type definition.
|
|
1359
|
+
Consequently, at that time, it is unknown whether the named type is an entity.
|
|
1360
|
+
If it isn't, the @provides directive does not make sense and can be ignored.
|
|
1361
|
+
*/
|
|
1362
|
+
const { fieldSetParentData, errorString } = this.getFieldSetParent(isProvides, parentData, fieldName, parentTypeName);
|
|
1363
|
+
const fieldCoords = `${parentTypeName}.${fieldName}`;
|
|
1364
|
+
if (errorString) {
|
|
1365
|
+
allErrorMessages.push(errorString);
|
|
1366
|
+
continue;
|
|
1367
|
+
}
|
|
1368
|
+
if (!fieldSetParentData) {
|
|
1369
|
+
continue;
|
|
1370
|
+
}
|
|
1371
|
+
const { errorMessages, configuration } = this.validateConditionalFieldSet(fieldSetParentData, fieldSet, fieldName, isProvides, parentTypeName);
|
|
1372
|
+
/*
|
|
1373
|
+
* It is possible to return no error messages nor configuration if the @provides or @requires directive is
|
|
1374
|
+
* considered completely redundant, i.e.,:
|
|
1375
|
+
* 1. All fields to which the directive refers are declared @external but are also key fields on an entity extension.
|
|
1376
|
+
* 2. The subgraph is V1 and all fields to which the directive refers are not declared @external.
|
|
1377
|
+
* In these cases, the fields are considered unconditionally provided.
|
|
1378
|
+
* If all the fields to which the directive refers are unconditionally provided, the directive is redundant.
|
|
1379
|
+
* For V2 subgraphs, this will propagate as an error; for V1 subgraphs, this will propagate as a warning.
|
|
1380
|
+
* */
|
|
1381
|
+
if (errorMessages.length > 0) {
|
|
1382
|
+
allErrorMessages.push(` On field "${fieldCoords}":\n -` + errorMessages.join(string_constants_2.HYPHEN_JOIN));
|
|
1383
|
+
continue;
|
|
1384
|
+
}
|
|
1385
|
+
if (configuration) {
|
|
1386
|
+
configurations.push(configuration);
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
if (allErrorMessages.length > 0) {
|
|
1390
|
+
this.errors.push((0, errors_1.invalidProvidesOrRequiresDirectivesError)((0, utils_2.getConditionalFieldSetDirectiveName)(isProvides), allErrorMessages));
|
|
1391
|
+
return;
|
|
1392
|
+
}
|
|
1393
|
+
if (configurations.length > 0) {
|
|
1394
|
+
return configurations;
|
|
1072
1395
|
}
|
|
1073
1396
|
}
|
|
1074
1397
|
validateInterfaceImplementations(data) {
|
|
@@ -1575,11 +1898,11 @@ class NormalizationFactory {
|
|
|
1575
1898
|
}
|
|
1576
1899
|
}
|
|
1577
1900
|
validateEventDrivenKeyDefinition(typeName, invalidKeyFieldSetsByEntityTypeName) {
|
|
1578
|
-
const
|
|
1579
|
-
if (!
|
|
1901
|
+
const keyFieldSetDataByFieldSet = this.keyFieldSetDatasByTypeName.get(typeName);
|
|
1902
|
+
if (!keyFieldSetDataByFieldSet) {
|
|
1580
1903
|
return;
|
|
1581
1904
|
}
|
|
1582
|
-
for (const [keyFieldSet, isUnresolvable] of
|
|
1905
|
+
for (const [keyFieldSet, { isUnresolvable }] of keyFieldSetDataByFieldSet) {
|
|
1583
1906
|
if (isUnresolvable) {
|
|
1584
1907
|
continue;
|
|
1585
1908
|
}
|
|
@@ -1590,7 +1913,7 @@ class NormalizationFactory {
|
|
|
1590
1913
|
for (const [fieldName, fieldData] of fieldDataByFieldName) {
|
|
1591
1914
|
const fieldPath = `${fieldData.originalParentTypeName}.${fieldName}`;
|
|
1592
1915
|
if (keyFieldNames.has(fieldName)) {
|
|
1593
|
-
if (!fieldData.
|
|
1916
|
+
if (!fieldData.externalFieldDataBySubgraphName.get(this.subgraphName)?.isDefinedExternal) {
|
|
1594
1917
|
nonExternalKeyFieldNameByFieldPath.set(fieldPath, fieldName);
|
|
1595
1918
|
}
|
|
1596
1919
|
continue;
|
|
@@ -1765,22 +2088,20 @@ class NormalizationFactory {
|
|
|
1765
2088
|
this.internalGraph.addEdge(this.internalGraph.addOrUpdateNode(unionTypeName, { isAbstract: true }), this.internalGraph.addOrUpdateNode(memberTypeName), memberTypeName, true);
|
|
1766
2089
|
}
|
|
1767
2090
|
}
|
|
1768
|
-
|
|
1769
|
-
const
|
|
1770
|
-
|
|
1771
|
-
if (keys) {
|
|
1772
|
-
configurationData.keys = keys;
|
|
1773
|
-
}
|
|
1774
|
-
}
|
|
1775
|
-
validateAndAddKeysToConfiguration() {
|
|
1776
|
-
for (const [parentTypeName, keyFieldSetData] of this.keyFieldSetDataByTypeName) {
|
|
1777
|
-
const parentData = this.parentDefinitionDataByTypeName.get(parentTypeName);
|
|
2091
|
+
addValidKeyFieldSetConfigurations() {
|
|
2092
|
+
for (const [entityTypeName, keyFieldSetDataByFieldSet] of this.keyFieldSetDatasByTypeName) {
|
|
2093
|
+
const parentData = this.parentDefinitionDataByTypeName.get(entityTypeName);
|
|
1778
2094
|
if (!parentData ||
|
|
1779
2095
|
(parentData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION && parentData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION)) {
|
|
1780
|
-
this.errors.push((0, errors_1.
|
|
2096
|
+
this.errors.push((0, errors_1.undefinedCompositeOutputTypeError)(entityTypeName));
|
|
1781
2097
|
continue;
|
|
1782
2098
|
}
|
|
1783
|
-
|
|
2099
|
+
const typeName = (0, utils_4.getParentTypeName)(parentData);
|
|
2100
|
+
const configurationData = (0, utils_5.getValueOrDefault)(this.configurationDataByTypeName, typeName, () => (0, utils_6.newConfigurationData)(true, typeName));
|
|
2101
|
+
const keys = (0, utils_2.validateKeyFieldSets)(this, parentData, keyFieldSetDataByFieldSet, configurationData.fieldNames);
|
|
2102
|
+
if (keys) {
|
|
2103
|
+
configurationData.keys = keys;
|
|
2104
|
+
}
|
|
1784
2105
|
}
|
|
1785
2106
|
}
|
|
1786
2107
|
getValidFlattenedDirectiveArray(directivesByDirectiveName, directiveCoords) {
|
|
@@ -1862,6 +2183,179 @@ class NormalizationFactory {
|
|
|
1862
2183
|
unionDefinitionData.node.types = (0, utils_3.mapToArrayOfValues)(unionDefinitionData.memberByMemberTypeName);
|
|
1863
2184
|
return unionDefinitionData.node;
|
|
1864
2185
|
}
|
|
2186
|
+
evaluateExternalKeyFields() {
|
|
2187
|
+
const invalidTypeNames = [];
|
|
2188
|
+
for (const [entityTypeName, keyFieldSetDataByFieldSet] of this.keyFieldSetDatasByTypeName) {
|
|
2189
|
+
const entityParentData = this.parentDefinitionDataByTypeName.get(entityTypeName);
|
|
2190
|
+
// The parent data should always exist.
|
|
2191
|
+
if (!entityParentData ||
|
|
2192
|
+
(entityParentData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION &&
|
|
2193
|
+
entityParentData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION)) {
|
|
2194
|
+
// If somehow the parent data does not exist, prevent the same error occurring by removing that type from the map.
|
|
2195
|
+
invalidTypeNames.push(entityTypeName);
|
|
2196
|
+
this.errors.push((0, errors_1.undefinedCompositeOutputTypeError)(entityTypeName));
|
|
2197
|
+
continue;
|
|
2198
|
+
}
|
|
2199
|
+
const nf = this;
|
|
2200
|
+
for (const keyFieldSetData of keyFieldSetDataByFieldSet.values()) {
|
|
2201
|
+
const parentDatas = [entityParentData];
|
|
2202
|
+
// Entity extension fields are effectively never @external, so propagate a warning.
|
|
2203
|
+
const externalExtensionFieldCoordsByRawFieldSet = new Map();
|
|
2204
|
+
let currentDepth = -1;
|
|
2205
|
+
let shouldDefineSelectionSet = true;
|
|
2206
|
+
(0, index_1.visit)(keyFieldSetData.documentNode, {
|
|
2207
|
+
Argument: {
|
|
2208
|
+
enter() {
|
|
2209
|
+
return index_1.BREAK;
|
|
2210
|
+
},
|
|
2211
|
+
},
|
|
2212
|
+
Field: {
|
|
2213
|
+
enter(node) {
|
|
2214
|
+
const parentData = parentDatas[currentDepth];
|
|
2215
|
+
const parentTypeName = parentData.name;
|
|
2216
|
+
// If a composite type was just visited, a selection set should have been entered
|
|
2217
|
+
if (shouldDefineSelectionSet) {
|
|
2218
|
+
return index_1.BREAK;
|
|
2219
|
+
}
|
|
2220
|
+
const fieldName = node.name.value;
|
|
2221
|
+
const fieldCoords = `${parentTypeName}.${fieldName}`;
|
|
2222
|
+
// If a field declared @external is a key field, it is valid use of @external.
|
|
2223
|
+
nf.unvalidatedExternalFieldCoords.delete(fieldCoords);
|
|
2224
|
+
const fieldData = parentData.fieldDataByFieldName.get(fieldName);
|
|
2225
|
+
// undefined if the field does not exist on the parent
|
|
2226
|
+
if (!fieldData || fieldData.argumentDataByArgumentName.size) {
|
|
2227
|
+
return index_1.BREAK;
|
|
2228
|
+
}
|
|
2229
|
+
// Fields that form part of an entity key are intrinsically shareable
|
|
2230
|
+
fieldData.isShareableBySubgraphName.set(nf.subgraphName, true);
|
|
2231
|
+
/* !!! IMPORTANT NOTE REGARDING INCONSISTENT APOLLO BEHAVIOUR !!!
|
|
2232
|
+
* V1 entities with "@extends" may define unique nested key fields as @external without restriction.
|
|
2233
|
+
* However, V1 entity extensions (with the "extend" keyword) cannot do this.
|
|
2234
|
+
* Instead, an error is returned stating that there must be a non-external definition of the field.
|
|
2235
|
+
* This inconsistency in behaviour appears to be a bug.
|
|
2236
|
+
* It doesn't make much sense to enforce "origin fields" only sometimes (or ever, honestly).
|
|
2237
|
+
* Consequently, a decision was made not ever to enforce meaningless origin fields for extensions.
|
|
2238
|
+
*
|
|
2239
|
+
* In the event the nested key field is not unique, the error may propagate as a field resolvability
|
|
2240
|
+
* error, e.g., unable to use the nested @external key field to satisfy a field set in another subgraph.
|
|
2241
|
+
*
|
|
2242
|
+
* In addition, the nested key field of a V2 entity extension (either "@extends" or "extend" keyword)
|
|
2243
|
+
* are considered unconditionally provided regardless of the presence of "@external".
|
|
2244
|
+
*
|
|
2245
|
+
* However, if the subgraph is an EDG, the @external state should be kept regardless of extension.
|
|
2246
|
+
* */
|
|
2247
|
+
const externalFieldData = fieldData.externalFieldDataBySubgraphName.get(nf.subgraphName);
|
|
2248
|
+
if (nf.edfsDirectiveReferences.size < 1 &&
|
|
2249
|
+
externalFieldData &&
|
|
2250
|
+
externalFieldData.isDefinedExternal &&
|
|
2251
|
+
!externalFieldData.isUnconditionallyProvided) {
|
|
2252
|
+
/*
|
|
2253
|
+
* The key field is unconditionally provided if all the following are true:
|
|
2254
|
+
* 1. The root entity is an extension type.
|
|
2255
|
+
* 2. The field is also a key field for the parent entity.
|
|
2256
|
+
*/
|
|
2257
|
+
if (entityParentData.extensionType !== types_1.ExtensionType.NONE) {
|
|
2258
|
+
externalFieldData.isUnconditionallyProvided = true;
|
|
2259
|
+
(0, utils_5.getValueOrDefault)(externalExtensionFieldCoordsByRawFieldSet, keyFieldSetData.rawFieldSet, () => new Set()).add(fieldCoords);
|
|
2260
|
+
}
|
|
2261
|
+
}
|
|
2262
|
+
(0, utils_5.getValueOrDefault)(nf.keyFieldNamesByParentTypeName, parentTypeName, () => new Set()).add(fieldName);
|
|
2263
|
+
const namedTypeName = (0, ast_1.getTypeNodeNamedTypeName)(fieldData.node.type);
|
|
2264
|
+
// The base scalars are not in the parents map
|
|
2265
|
+
if (constants_1.BASE_SCALARS.has(namedTypeName)) {
|
|
2266
|
+
return;
|
|
2267
|
+
}
|
|
2268
|
+
// The child could itself be a parent
|
|
2269
|
+
const namedTypeData = nf.parentDefinitionDataByTypeName.get(namedTypeName);
|
|
2270
|
+
if (!namedTypeData) {
|
|
2271
|
+
return index_1.BREAK;
|
|
2272
|
+
}
|
|
2273
|
+
if (namedTypeData.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION) {
|
|
2274
|
+
shouldDefineSelectionSet = true;
|
|
2275
|
+
parentDatas.push(namedTypeData);
|
|
2276
|
+
return;
|
|
2277
|
+
}
|
|
2278
|
+
// interfaces and unions are invalid in a key directive
|
|
2279
|
+
if ((0, utils_1.isKindAbstract)(namedTypeData.kind)) {
|
|
2280
|
+
return index_1.BREAK;
|
|
2281
|
+
}
|
|
2282
|
+
},
|
|
2283
|
+
},
|
|
2284
|
+
InlineFragment: {
|
|
2285
|
+
enter() {
|
|
2286
|
+
return index_1.BREAK;
|
|
2287
|
+
},
|
|
2288
|
+
},
|
|
2289
|
+
SelectionSet: {
|
|
2290
|
+
enter() {
|
|
2291
|
+
if (!shouldDefineSelectionSet) {
|
|
2292
|
+
return index_1.BREAK;
|
|
2293
|
+
}
|
|
2294
|
+
currentDepth += 1;
|
|
2295
|
+
shouldDefineSelectionSet = false;
|
|
2296
|
+
if (currentDepth < 0 || currentDepth >= parentDatas.length) {
|
|
2297
|
+
return index_1.BREAK;
|
|
2298
|
+
}
|
|
2299
|
+
},
|
|
2300
|
+
leave() {
|
|
2301
|
+
if (shouldDefineSelectionSet) {
|
|
2302
|
+
shouldDefineSelectionSet = false;
|
|
2303
|
+
}
|
|
2304
|
+
// Empty selection sets would be a parse error, so it is unnecessary to handle them
|
|
2305
|
+
currentDepth -= 1;
|
|
2306
|
+
parentDatas.pop();
|
|
2307
|
+
},
|
|
2308
|
+
},
|
|
2309
|
+
});
|
|
2310
|
+
if (externalExtensionFieldCoordsByRawFieldSet.size < 1) {
|
|
2311
|
+
continue;
|
|
2312
|
+
}
|
|
2313
|
+
for (const [rawFieldSet, fieldCoords] of externalExtensionFieldCoordsByRawFieldSet) {
|
|
2314
|
+
this.warnings.push((0, warnings_1.externalEntityExtensionKeyFieldWarning)(entityParentData.name, rawFieldSet, [...fieldCoords], this.subgraphName));
|
|
2315
|
+
}
|
|
2316
|
+
}
|
|
2317
|
+
}
|
|
2318
|
+
for (const invalidTypeName of invalidTypeNames) {
|
|
2319
|
+
this.keyFieldSetDatasByTypeName.delete(invalidTypeName);
|
|
2320
|
+
}
|
|
2321
|
+
}
|
|
2322
|
+
addValidConditionalFieldSetConfigurations() {
|
|
2323
|
+
for (const [typeName, fieldSetData] of this.fieldSetDataByTypeName) {
|
|
2324
|
+
const parentData = this.parentDefinitionDataByTypeName.get(typeName);
|
|
2325
|
+
if (!parentData ||
|
|
2326
|
+
(parentData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION && parentData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION)) {
|
|
2327
|
+
this.errors.push((0, errors_1.undefinedCompositeOutputTypeError)(typeName));
|
|
2328
|
+
continue;
|
|
2329
|
+
}
|
|
2330
|
+
const parentTypeName = (0, utils_4.getParentTypeName)(parentData);
|
|
2331
|
+
const configurationData = (0, utils_5.getValueOrDefault)(this.configurationDataByTypeName, parentTypeName, () => (0, utils_6.newConfigurationData)(false, parentTypeName));
|
|
2332
|
+
const provides = this.validateProvidesOrRequires(parentData, fieldSetData.provides, true);
|
|
2333
|
+
if (provides) {
|
|
2334
|
+
configurationData.provides = provides;
|
|
2335
|
+
}
|
|
2336
|
+
const requires = this.validateProvidesOrRequires(parentData, fieldSetData.requires, false);
|
|
2337
|
+
if (requires) {
|
|
2338
|
+
configurationData.requires = requires;
|
|
2339
|
+
}
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
addFieldNamesToConfigurationData(fieldDataByFieldName, configurationData) {
|
|
2343
|
+
const externalFieldNames = new Set();
|
|
2344
|
+
for (const [fieldName, fieldData] of fieldDataByFieldName) {
|
|
2345
|
+
const externalFieldData = fieldData.externalFieldDataBySubgraphName.get(this.subgraphName);
|
|
2346
|
+
if (!externalFieldData || externalFieldData.isUnconditionallyProvided) {
|
|
2347
|
+
configurationData.fieldNames.add(fieldName);
|
|
2348
|
+
continue;
|
|
2349
|
+
}
|
|
2350
|
+
externalFieldNames.add(fieldName);
|
|
2351
|
+
if (this.edfsDirectiveReferences.size > 0) {
|
|
2352
|
+
configurationData.fieldNames.add(fieldName);
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
if (externalFieldNames.size > 0) {
|
|
2356
|
+
configurationData.externalFieldNames = externalFieldNames;
|
|
2357
|
+
}
|
|
2358
|
+
}
|
|
1865
2359
|
normalize(document) {
|
|
1866
2360
|
/* factory.allDirectiveDefinitions is initialized with v1 directive definitions, and v2 definitions are only added
|
|
1867
2361
|
after the visitor has visited the entire schema and the subgraph is known to be a V2 graph. Consequently,
|
|
@@ -1953,6 +2447,8 @@ class NormalizationFactory {
|
|
|
1953
2447
|
this.errors.push((0, errors_1.configureDescriptionNoDescriptionError)((0, utils_5.kindToTypeString)(data.kind), data.name));
|
|
1954
2448
|
}
|
|
1955
2449
|
}
|
|
2450
|
+
// Check all key field sets for @external fields to assess whether they are conditional
|
|
2451
|
+
this.evaluateExternalKeyFields();
|
|
1956
2452
|
for (const [parentTypeName, parentDefinitionData] of this.parentDefinitionDataByTypeName) {
|
|
1957
2453
|
switch (parentDefinitionData.kind) {
|
|
1958
2454
|
case graphql_1.Kind.ENUM_TYPE_DEFINITION:
|
|
@@ -1976,6 +2472,10 @@ class NormalizationFactory {
|
|
|
1976
2472
|
const isEntity = this.entityDataByTypeName.has(parentTypeName);
|
|
1977
2473
|
const operationTypeNode = this.operationTypeNodeByTypeName.get(parentTypeName);
|
|
1978
2474
|
const isObject = parentDefinitionData.kind === graphql_1.Kind.OBJECT_TYPE_DEFINITION;
|
|
2475
|
+
if (this.isSubgraphVersionTwo && parentDefinitionData.extensionType === types_1.ExtensionType.EXTENDS) {
|
|
2476
|
+
// @extends is essentially ignored in V2. It was only propagated to handle @external key fields.
|
|
2477
|
+
parentDefinitionData.extensionType = types_1.ExtensionType.NONE;
|
|
2478
|
+
}
|
|
1979
2479
|
if (operationTypeNode) {
|
|
1980
2480
|
parentDefinitionData.fieldDataByFieldName.delete(string_constants_2.SERVICE_FIELD);
|
|
1981
2481
|
parentDefinitionData.fieldDataByFieldName.delete(string_constants_2.ENTITIES_FIELD);
|
|
@@ -1985,7 +2485,7 @@ class NormalizationFactory {
|
|
|
1985
2485
|
if (this.parentsWithChildArguments.has(parentTypeName) || !isObject) {
|
|
1986
2486
|
const externalInterfaceFieldNames = [];
|
|
1987
2487
|
for (const [fieldName, fieldData] of parentDefinitionData.fieldDataByFieldName) {
|
|
1988
|
-
if (!isObject && fieldData.
|
|
2488
|
+
if (!isObject && fieldData.externalFieldDataBySubgraphName.get(this.subgraphName)?.isDefinedExternal) {
|
|
1989
2489
|
externalInterfaceFieldNames.push(fieldName);
|
|
1990
2490
|
}
|
|
1991
2491
|
// Arguments can only be fully validated once all parents types are known
|
|
@@ -1998,18 +2498,15 @@ class NormalizationFactory {
|
|
|
1998
2498
|
: this.warnings.push((0, warnings_1.externalInterfaceFieldsWarning)(this.subgraphName, parentTypeName, externalInterfaceFieldNames));
|
|
1999
2499
|
}
|
|
2000
2500
|
}
|
|
2001
|
-
const newParentTypeName =
|
|
2002
|
-
|
|
2003
|
-
: parentTypeName;
|
|
2004
|
-
const configurationData = {
|
|
2005
|
-
fieldNames: new Set(),
|
|
2006
|
-
isRootNode: isEntity,
|
|
2007
|
-
typeName: newParentTypeName,
|
|
2008
|
-
};
|
|
2501
|
+
const newParentTypeName = (0, utils_4.getParentTypeName)(parentDefinitionData);
|
|
2502
|
+
const configurationData = (0, utils_5.getValueOrDefault)(this.configurationDataByTypeName, newParentTypeName, () => (0, utils_6.newConfigurationData)(isEntity, parentTypeName));
|
|
2009
2503
|
const entityInterfaceData = this.entityInterfaceDataByTypeName.get(parentTypeName);
|
|
2010
2504
|
if (entityInterfaceData) {
|
|
2011
2505
|
entityInterfaceData.fieldDatas = (0, utils_3.fieldDatasToSimpleFieldDatas)(parentDefinitionData.fieldDataByFieldName.values());
|
|
2012
|
-
|
|
2506
|
+
const concreteTypeNames = this.concreteTypeNamesByAbstractTypeName.get(parentTypeName);
|
|
2507
|
+
if (concreteTypeNames) {
|
|
2508
|
+
(0, utils_5.addIterableValuesToSet)(concreteTypeNames, entityInterfaceData.concreteTypeNames);
|
|
2509
|
+
}
|
|
2013
2510
|
configurationData.isInterfaceObject = entityInterfaceData.isInterfaceObject;
|
|
2014
2511
|
configurationData.entityInterfaceConcreteTypeNames = entityInterfaceData.concreteTypeNames;
|
|
2015
2512
|
}
|
|
@@ -2017,8 +2514,7 @@ class NormalizationFactory {
|
|
|
2017
2514
|
if (events) {
|
|
2018
2515
|
configurationData.events = events;
|
|
2019
2516
|
}
|
|
2020
|
-
this.
|
|
2021
|
-
(0, utils_2.addFieldNamesToConfigurationData)(parentDefinitionData.fieldDataByFieldName, configurationData);
|
|
2517
|
+
this.addFieldNamesToConfigurationData(parentDefinitionData.fieldDataByFieldName, configurationData);
|
|
2022
2518
|
this.validateInterfaceImplementations(parentDefinitionData);
|
|
2023
2519
|
definitions.push(this.getCompositeOutputNodeByData(parentDefinitionData));
|
|
2024
2520
|
// interfaces and objects must define at least one field
|
|
@@ -2042,6 +2538,10 @@ class NormalizationFactory {
|
|
|
2042
2538
|
throw (0, errors_1.unexpectedKindFatalError)(parentTypeName);
|
|
2043
2539
|
}
|
|
2044
2540
|
}
|
|
2541
|
+
// this is where @provides and @requires configurations are added to the ConfigurationData
|
|
2542
|
+
this.addValidConditionalFieldSetConfigurations();
|
|
2543
|
+
// this is where @key configurations are added to the ConfigurationData
|
|
2544
|
+
this.addValidKeyFieldSetConfigurations();
|
|
2045
2545
|
// Check that explicitly defined operations types are valid objects and that their fields are also valid
|
|
2046
2546
|
for (const operationType of Object.values(graphql_1.OperationTypeNode)) {
|
|
2047
2547
|
const operationTypeNode = this.schemaData.operationTypes.get(operationType);
|
|
@@ -2067,7 +2567,7 @@ class NormalizationFactory {
|
|
|
2067
2567
|
if (!objectData) {
|
|
2068
2568
|
continue;
|
|
2069
2569
|
}
|
|
2070
|
-
const rootNode = this.
|
|
2570
|
+
const rootNode = this.configurationDataByTypeName.get(defaultTypeName);
|
|
2071
2571
|
if (rootNode) {
|
|
2072
2572
|
rootNode.isRootNode = true;
|
|
2073
2573
|
rootNode.typeName = defaultTypeName;
|
|
@@ -2101,17 +2601,6 @@ class NormalizationFactory {
|
|
|
2101
2601
|
this.errors.push((0, errors_1.undefinedTypeError)(referencedTypeName));
|
|
2102
2602
|
}
|
|
2103
2603
|
}
|
|
2104
|
-
this.validateAndAddKeysToConfiguration();
|
|
2105
|
-
for (const [parentTypeName, fieldSetData] of this.fieldSetDataByTypeName) {
|
|
2106
|
-
const parentData = this.parentDefinitionDataByTypeName.get(parentTypeName);
|
|
2107
|
-
if (!parentData ||
|
|
2108
|
-
(parentData.kind !== graphql_1.Kind.OBJECT_TYPE_DEFINITION && parentData.kind !== graphql_1.Kind.INTERFACE_TYPE_DEFINITION)) {
|
|
2109
|
-
this.errors.push((0, errors_1.undefinedObjectLikeParentError)(parentTypeName));
|
|
2110
|
-
continue;
|
|
2111
|
-
}
|
|
2112
|
-
// this is where keys, provides, and requires are added to the ConfigurationData
|
|
2113
|
-
(0, utils_2.validateAndAddConditionalFieldSetsToConfiguration)(this, parentData, fieldSetData);
|
|
2114
|
-
}
|
|
2115
2604
|
const persistedDirectiveDefinitionDataByDirectiveName = new Map();
|
|
2116
2605
|
for (const directiveDefinitionNode of this.directiveDefinitionByDirectiveName.values()) {
|
|
2117
2606
|
// TODO @composeDirective directives would also be handled here
|
|
@@ -2145,8 +2634,8 @@ class NormalizationFactory {
|
|
|
2145
2634
|
// configurationDataMap is map of ConfigurationData per type name.
|
|
2146
2635
|
// It is an Intermediate configuration object that will be converted to an engine configuration in the router
|
|
2147
2636
|
concreteTypeNamesByAbstractTypeName: this.concreteTypeNamesByAbstractTypeName,
|
|
2148
|
-
conditionalFieldDataByCoordinates: this.
|
|
2149
|
-
configurationDataByTypeName: this.
|
|
2637
|
+
conditionalFieldDataByCoordinates: this.conditionalFieldDataByCoords,
|
|
2638
|
+
configurationDataByTypeName: this.configurationDataByTypeName,
|
|
2150
2639
|
entityDataByTypeName: this.entityDataByTypeName,
|
|
2151
2640
|
entityInterfaces: this.entityInterfaceDataByTypeName,
|
|
2152
2641
|
isEventDrivenGraph: this.isSubgraphEventDrivenGraph,
|
|
@@ -2218,8 +2707,17 @@ function batchNormalize(subgraphs) {
|
|
|
2218
2707
|
}
|
|
2219
2708
|
(0, utils_5.addIterableValuesToSet)(incomingConcreteTypeNames, existingConcreteTypeNames);
|
|
2220
2709
|
}
|
|
2221
|
-
for (const entityData of normalizationResult.entityDataByTypeName
|
|
2222
|
-
|
|
2710
|
+
for (const [typeName, entityData] of normalizationResult.entityDataByTypeName) {
|
|
2711
|
+
const keyFieldSetDataByFieldSet = entityData.keyFieldSetDatasBySubgraphName.get(subgraphName);
|
|
2712
|
+
if (!keyFieldSetDataByFieldSet) {
|
|
2713
|
+
continue;
|
|
2714
|
+
}
|
|
2715
|
+
(0, utils_3.upsertEntityData)({
|
|
2716
|
+
entityDataByTypeName,
|
|
2717
|
+
keyFieldSetDataByFieldSet,
|
|
2718
|
+
typeName,
|
|
2719
|
+
subgraphName,
|
|
2720
|
+
});
|
|
2223
2721
|
}
|
|
2224
2722
|
if (subgraph.name) {
|
|
2225
2723
|
internalSubgraphBySubgraphName.set(subgraphName, {
|