@apollo/federation-internals 2.0.0-alpha.4 → 2.0.0-preview.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +11 -1
- package/dist/buildSchema.d.ts +7 -3
- package/dist/buildSchema.d.ts.map +1 -1
- package/dist/buildSchema.js +41 -22
- package/dist/buildSchema.js.map +1 -1
- package/dist/coreSpec.d.ts +26 -4
- package/dist/coreSpec.d.ts.map +1 -1
- package/dist/coreSpec.js +86 -25
- package/dist/coreSpec.js.map +1 -1
- package/dist/definitions.d.ts +50 -43
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +201 -217
- package/dist/definitions.js.map +1 -1
- package/dist/directiveAndTypeSpecification.d.ts +38 -0
- package/dist/directiveAndTypeSpecification.d.ts.map +1 -0
- package/dist/directiveAndTypeSpecification.js +196 -0
- package/dist/directiveAndTypeSpecification.js.map +1 -0
- package/dist/error.d.ts +10 -1
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +22 -2
- package/dist/error.js.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.d.ts.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.js +29 -94
- package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
- package/dist/federation.d.ts +88 -46
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +745 -233
- package/dist/federation.js.map +1 -1
- package/dist/federationSpec.d.ts +19 -0
- package/dist/federationSpec.d.ts.map +1 -0
- package/dist/federationSpec.js +91 -0
- package/dist/federationSpec.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -1
- package/dist/index.js.map +1 -1
- package/dist/joinSpec.d.ts +1 -0
- package/dist/joinSpec.d.ts.map +1 -1
- package/dist/joinSpec.js +1 -0
- package/dist/joinSpec.js.map +1 -1
- package/dist/operations.d.ts +8 -1
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +11 -4
- package/dist/operations.js.map +1 -1
- package/dist/print.d.ts +11 -9
- package/dist/print.d.ts.map +1 -1
- package/dist/print.js +21 -11
- package/dist/print.js.map +1 -1
- package/dist/schemaUpgrader.d.ts +108 -0
- package/dist/schemaUpgrader.d.ts.map +1 -0
- package/dist/schemaUpgrader.js +498 -0
- package/dist/schemaUpgrader.js.map +1 -0
- package/dist/sharing.d.ts +3 -0
- package/dist/sharing.d.ts.map +1 -0
- package/dist/sharing.js +51 -0
- package/dist/sharing.js.map +1 -0
- package/dist/supergraphs.d.ts.map +1 -1
- package/dist/supergraphs.js +2 -3
- package/dist/supergraphs.js.map +1 -1
- package/dist/tagSpec.d.ts.map +1 -1
- package/dist/tagSpec.js +1 -3
- package/dist/tagSpec.js.map +1 -1
- package/dist/utils.d.ts +8 -0
- package/dist/utils.d.ts.map +1 -1
- package/dist/utils.js +49 -1
- package/dist/utils.js.map +1 -1
- package/dist/validate.d.ts.map +1 -1
- package/dist/validate.js +9 -4
- package/dist/validate.js.map +1 -1
- package/dist/validation/KnownTypeNamesInFederationRule.d.ts.map +1 -1
- package/dist/validation/KnownTypeNamesInFederationRule.js +1 -2
- package/dist/validation/KnownTypeNamesInFederationRule.js.map +1 -1
- package/dist/values.d.ts +1 -0
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +3 -2
- package/dist/values.js.map +1 -1
- package/jest.config.js +5 -1
- package/package.json +4 -7
- package/src/__tests__/definitions.test.ts +19 -17
- package/src/__tests__/extractSubgraphsFromSupergraph.test.ts +103 -0
- package/src/__tests__/federation.test.ts +31 -0
- package/src/__tests__/operations.test.ts +2 -3
- package/src/__tests__/schemaUpgrader.test.ts +168 -0
- package/src/__tests__/subgraphValidation.test.ts +33 -19
- package/src/__tests__/values.test.ts +2 -4
- package/src/buildSchema.ts +55 -36
- package/src/coreSpec.ts +112 -31
- package/src/definitions.ts +247 -260
- package/src/directiveAndTypeSpecification.ts +276 -0
- package/src/error.ts +61 -5
- package/src/extractSubgraphsFromSupergraph.ts +35 -119
- package/src/federation.ts +960 -293
- package/src/federationSpec.ts +113 -0
- package/src/index.ts +2 -0
- package/src/joinSpec.ts +2 -1
- package/src/operations.ts +22 -7
- package/src/print.ts +51 -38
- package/src/schemaUpgrader.ts +657 -0
- package/src/sharing.ts +68 -0
- package/src/supergraphs.ts +3 -3
- package/src/tagSpec.ts +1 -3
- package/src/utils.ts +85 -0
- package/src/validate.ts +13 -7
- package/src/validation/KnownTypeNamesInFederationRule.ts +1 -7
- package/src/values.ts +7 -3
- package/tsconfig.test.tsbuildinfo +1 -1
- package/tsconfig.tsbuildinfo +1 -1
package/dist/federation.js
CHANGED
|
@@ -1,49 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.removeInactiveProvidesAndRequires = exports.addSubgraphToError = exports.addSubgraphToASTNode = exports.Subgraph = exports.FEDERATION_OPERATION_FIELDS = exports.entitiesFieldName = exports.serviceFieldName = exports.FEDERATION_OPERATION_TYPES = exports.entityTypeSpec = exports.serviceTypeSpec = exports.anyTypeSpec = exports.Subgraphs = exports.subgraphsFromServiceList = exports.collectTargetFields = exports.parseFieldSetArgument = exports.newEmptyFederation2Schema = exports.buildSubgraph = exports.isEntityType = exports.isFederationField = exports.isFederationSubgraphSchema = exports.federationMetadata = exports.printSubgraphNames = exports.asFed2SubgraphDocument = exports.setSchemaAsFed2Subgraph = exports.FederationBlueprint = exports.FederationMetadata = exports.collectUsedExternalFieldsCoordinates = exports.FEDERATION_RESERVED_SUBGRAPH_NAME = void 0;
|
|
4
4
|
const definitions_1 = require("./definitions");
|
|
5
5
|
const utils_1 = require("./utils");
|
|
6
6
|
const specifiedRules_1 = require("graphql/validation/specifiedRules");
|
|
7
7
|
const graphql_1 = require("graphql");
|
|
8
|
-
const print_1 = require("./print");
|
|
9
8
|
const KnownTypeNamesInFederationRule_1 = require("./validation/KnownTypeNamesInFederationRule");
|
|
10
9
|
const buildSchema_1 = require("./buildSchema");
|
|
11
10
|
const operations_1 = require("./operations");
|
|
12
11
|
const tagSpec_1 = require("./tagSpec");
|
|
13
12
|
const error_1 = require("./error");
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
exports.extendsDirectiveName = 'extends';
|
|
21
|
-
exports.externalDirectiveName = 'external';
|
|
22
|
-
exports.requiresDirectiveName = 'requires';
|
|
23
|
-
exports.providesDirectiveName = 'provides';
|
|
24
|
-
exports.tagDirectiveName = 'tag';
|
|
25
|
-
exports.serviceFieldName = '_service';
|
|
26
|
-
exports.entitiesFieldName = '_entities';
|
|
13
|
+
const sharing_1 = require("./sharing");
|
|
14
|
+
const coreSpec_1 = require("./coreSpec");
|
|
15
|
+
const federationSpec_1 = require("./federationSpec");
|
|
16
|
+
const print_1 = require("./print");
|
|
17
|
+
const directiveAndTypeSpecification_1 = require("./directiveAndTypeSpecification");
|
|
18
|
+
const linkSpec = coreSpec_1.LINK_VERSIONS.latest();
|
|
27
19
|
const tagSpec = tagSpec_1.TAG_VERSIONS.latest();
|
|
20
|
+
const federationSpec = federationSpec_1.FEDERATION_VERSIONS.latest();
|
|
28
21
|
exports.FEDERATION_RESERVED_SUBGRAPH_NAME = '_';
|
|
29
|
-
const FEDERATION_TYPES = [
|
|
30
|
-
exports.entityTypeName,
|
|
31
|
-
exports.serviceTypeName,
|
|
32
|
-
exports.anyTypeName,
|
|
33
|
-
exports.fieldSetTypeName
|
|
34
|
-
];
|
|
35
|
-
const FEDERATION_DIRECTIVES = [
|
|
36
|
-
exports.keyDirectiveName,
|
|
37
|
-
exports.extendsDirectiveName,
|
|
38
|
-
exports.externalDirectiveName,
|
|
39
|
-
exports.requiresDirectiveName,
|
|
40
|
-
exports.providesDirectiveName,
|
|
41
|
-
exports.tagDirectiveName
|
|
42
|
-
];
|
|
43
|
-
const FEDERATION_ROOT_FIELDS = [
|
|
44
|
-
exports.serviceFieldName,
|
|
45
|
-
exports.entitiesFieldName
|
|
46
|
-
];
|
|
47
22
|
const FEDERATION_OMITTED_VALIDATION_RULES = [
|
|
48
23
|
graphql_1.PossibleTypeExtensionsRule,
|
|
49
24
|
graphql_1.KnownTypeNamesRule
|
|
@@ -52,33 +27,30 @@ const FEDERATION_SPECIFIC_VALIDATION_RULES = [
|
|
|
52
27
|
KnownTypeNamesInFederationRule_1.KnownTypeNamesInFederationRule
|
|
53
28
|
];
|
|
54
29
|
const FEDERATION_VALIDATION_RULES = specifiedRules_1.specifiedSDLRules.filter(rule => !FEDERATION_OMITTED_VALIDATION_RULES.includes(rule)).concat(FEDERATION_SPECIFIC_VALIDATION_RULES);
|
|
55
|
-
function validateFieldSetSelections(directiveName, selectionSet, hasExternalInParents,
|
|
30
|
+
function validateFieldSetSelections(directiveName, selectionSet, hasExternalInParents, federationMetadata, allowOnNonExternalLeafFields) {
|
|
56
31
|
for (const selection of selectionSet.selections()) {
|
|
57
32
|
if (selection.kind === 'FieldSelection') {
|
|
58
33
|
const field = selection.element().definition;
|
|
59
|
-
const isExternal =
|
|
60
|
-
if (isExternal) {
|
|
61
|
-
externalFieldCoordinatesCollector.push(field.coordinate);
|
|
62
|
-
}
|
|
34
|
+
const isExternal = federationMetadata.isFieldExternal(field);
|
|
63
35
|
if (field.hasArguments()) {
|
|
64
|
-
throw
|
|
36
|
+
throw error_1.ERROR_CATEGORIES.FIELDS_HAS_ARGS.get(directiveName).err({
|
|
65
37
|
message: `field ${field.coordinate} cannot be included because it has arguments (fields with argument are not allowed in @${directiveName})`,
|
|
66
38
|
nodes: field.sourceAST
|
|
67
39
|
});
|
|
68
40
|
}
|
|
69
41
|
const mustBeExternal = !selection.selectionSet && !allowOnNonExternalLeafFields && !hasExternalInParents;
|
|
70
42
|
if (!isExternal && mustBeExternal) {
|
|
71
|
-
const errorCode =
|
|
72
|
-
if (
|
|
43
|
+
const errorCode = error_1.ERROR_CATEGORIES.DIRECTIVE_FIELDS_MISSING_EXTERNAL.get(directiveName);
|
|
44
|
+
if (federationMetadata.isFieldFakeExternal(field)) {
|
|
73
45
|
throw errorCode.err({
|
|
74
46
|
message: `field "${field.coordinate}" should not be part of a @${directiveName} since it is already "effectively" provided by this subgraph `
|
|
75
|
-
+ `(while it is marked @${
|
|
47
|
+
+ `(while it is marked @${federationSpec_1.externalDirectiveSpec.name}, it is a @${federationSpec_1.keyDirectiveSpec.name} field of an extension type, which are not internally considered external for historical/backward compatibility reasons)`,
|
|
76
48
|
nodes: field.sourceAST
|
|
77
49
|
});
|
|
78
50
|
}
|
|
79
51
|
else {
|
|
80
52
|
throw errorCode.err({
|
|
81
|
-
message: `field "${field.coordinate}" should not be part of a @${directiveName} since it is already provided by this subgraph (it is not marked @${
|
|
53
|
+
message: `field "${field.coordinate}" should not be part of a @${directiveName} since it is already provided by this subgraph (it is not marked @${federationSpec_1.externalDirectiveSpec.name})`,
|
|
82
54
|
nodes: field.sourceAST
|
|
83
55
|
});
|
|
84
56
|
}
|
|
@@ -89,24 +61,24 @@ function validateFieldSetSelections(directiveName, selectionSet, hasExternalInPa
|
|
|
89
61
|
if (!newHasExternalInParents && (0, definitions_1.isInterfaceType)(parentType)) {
|
|
90
62
|
for (const implem of parentType.possibleRuntimeTypes()) {
|
|
91
63
|
const fieldInImplem = implem.field(field.name);
|
|
92
|
-
if (fieldInImplem &&
|
|
64
|
+
if (fieldInImplem && federationMetadata.isFieldExternal(fieldInImplem)) {
|
|
93
65
|
newHasExternalInParents = true;
|
|
94
66
|
break;
|
|
95
67
|
}
|
|
96
68
|
}
|
|
97
69
|
}
|
|
98
|
-
validateFieldSetSelections(directiveName, selection.selectionSet, newHasExternalInParents,
|
|
70
|
+
validateFieldSetSelections(directiveName, selection.selectionSet, newHasExternalInParents, federationMetadata, allowOnNonExternalLeafFields);
|
|
99
71
|
}
|
|
100
72
|
}
|
|
101
73
|
else {
|
|
102
|
-
validateFieldSetSelections(directiveName, selection.selectionSet, hasExternalInParents,
|
|
74
|
+
validateFieldSetSelections(directiveName, selection.selectionSet, hasExternalInParents, federationMetadata, allowOnNonExternalLeafFields);
|
|
103
75
|
}
|
|
104
76
|
}
|
|
105
77
|
}
|
|
106
|
-
function validateFieldSet(type, directive,
|
|
78
|
+
function validateFieldSet(type, directive, federationMetadata, allowOnNonExternalLeafFields, onFields) {
|
|
107
79
|
var _a;
|
|
108
80
|
try {
|
|
109
|
-
const
|
|
81
|
+
const fieldAccessor = onFields
|
|
110
82
|
? (type, fieldName) => {
|
|
111
83
|
const field = type.field(fieldName);
|
|
112
84
|
if (field) {
|
|
@@ -115,9 +87,9 @@ function validateFieldSet(type, directive, externalTester, externalFieldCoordina
|
|
|
115
87
|
return field;
|
|
116
88
|
}
|
|
117
89
|
: undefined;
|
|
118
|
-
const selectionSet = parseFieldSetArgument(type, directive,
|
|
90
|
+
const selectionSet = parseFieldSetArgument({ parentType: type, directive, fieldAccessor });
|
|
119
91
|
try {
|
|
120
|
-
validateFieldSetSelections(directive.name, selectionSet, false,
|
|
92
|
+
validateFieldSetSelections(directive.name, selectionSet, false, federationMetadata, allowOnNonExternalLeafFields);
|
|
121
93
|
return undefined;
|
|
122
94
|
}
|
|
123
95
|
catch (e) {
|
|
@@ -128,7 +100,7 @@ function validateFieldSet(type, directive, externalTester, externalFieldCoordina
|
|
|
128
100
|
if (e.nodes) {
|
|
129
101
|
nodes.push(...e.nodes);
|
|
130
102
|
}
|
|
131
|
-
const codeDef = (_a = (0, error_1.errorCodeDef)(e)) !== null && _a !== void 0 ? _a :
|
|
103
|
+
const codeDef = (_a = (0, error_1.errorCodeDef)(e)) !== null && _a !== void 0 ? _a : error_1.ERROR_CATEGORIES.DIRECTIVE_INVALID_FIELDS.get(directive.name);
|
|
132
104
|
return codeDef.err({
|
|
133
105
|
message: `${fieldSetErrorDescriptor(directive)}: ${e.message.trim()}`,
|
|
134
106
|
nodes,
|
|
@@ -156,13 +128,13 @@ function fieldSetTargetDescription(directive) {
|
|
|
156
128
|
const targetKind = directive.parent instanceof definitions_1.FieldDefinition ? "field" : "type";
|
|
157
129
|
return `${targetKind} "${(_a = directive.parent) === null || _a === void 0 ? void 0 : _a.coordinate}"`;
|
|
158
130
|
}
|
|
159
|
-
function validateAllFieldSet(definition, targetTypeExtractor, errorCollector,
|
|
131
|
+
function validateAllFieldSet(definition, targetTypeExtractor, errorCollector, federationMetadata, isOnParentType, allowOnNonExternalLeafFields, onFields) {
|
|
160
132
|
for (const application of definition.applications()) {
|
|
161
133
|
const elt = application.parent;
|
|
162
134
|
const type = targetTypeExtractor(elt);
|
|
163
135
|
const parentType = isOnParentType ? type : elt.parent;
|
|
164
136
|
if ((0, definitions_1.isInterfaceType)(parentType)) {
|
|
165
|
-
const code =
|
|
137
|
+
const code = error_1.ERROR_CATEGORIES.DIRECTIVE_UNSUPPORTED_ON_INTERFACE.get(definition.name);
|
|
166
138
|
errorCollector.push(code.err({
|
|
167
139
|
message: isOnParentType
|
|
168
140
|
? `Cannot use ${definition.coordinate} on interface "${parentType.coordinate}": ${definition.coordinate} is not yet supported on interfaces`
|
|
@@ -170,19 +142,57 @@ function validateAllFieldSet(definition, targetTypeExtractor, errorCollector, ex
|
|
|
170
142
|
nodes: (0, definitions_1.sourceASTs)(application).concat(isOnParentType ? [] : (0, definitions_1.sourceASTs)(type)),
|
|
171
143
|
}));
|
|
172
144
|
}
|
|
173
|
-
const error = validateFieldSet(type, application,
|
|
145
|
+
const error = validateFieldSet(type, application, federationMetadata, allowOnNonExternalLeafFields, onFields);
|
|
174
146
|
if (error) {
|
|
175
147
|
errorCollector.push(error);
|
|
176
148
|
}
|
|
177
149
|
}
|
|
178
150
|
}
|
|
179
|
-
function
|
|
180
|
-
|
|
151
|
+
function collectUsedExternalFieldsCoordinates(metadata) {
|
|
152
|
+
const usedExternalCoordinates = new Set();
|
|
153
|
+
collectUsedExternaFieldsForDirective(metadata, metadata.keyDirective(), type => type, usedExternalCoordinates);
|
|
154
|
+
collectUsedExternaFieldsForDirective(metadata, metadata.requiresDirective(), field => field.parent, usedExternalCoordinates);
|
|
155
|
+
collectUsedExternaFieldsForDirective(metadata, metadata.providesDirective(), field => {
|
|
156
|
+
const type = (0, definitions_1.baseType)(field.type);
|
|
157
|
+
return (0, definitions_1.isCompositeType)(type) ? type : undefined;
|
|
158
|
+
}, usedExternalCoordinates);
|
|
159
|
+
for (const itfType of metadata.schema.types('InterfaceType')) {
|
|
160
|
+
const runtimeTypes = itfType.possibleRuntimeTypes();
|
|
161
|
+
for (const field of itfType.fields()) {
|
|
162
|
+
for (const runtimeType of runtimeTypes) {
|
|
163
|
+
const implemField = runtimeType.field(field.name);
|
|
164
|
+
if (implemField && metadata.isFieldExternal(implemField)) {
|
|
165
|
+
usedExternalCoordinates.add(implemField.coordinate);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
return usedExternalCoordinates;
|
|
171
|
+
}
|
|
172
|
+
exports.collectUsedExternalFieldsCoordinates = collectUsedExternalFieldsCoordinates;
|
|
173
|
+
function collectUsedExternaFieldsForDirective(metadata, definition, targetTypeExtractor, usedExternalCoordinates) {
|
|
174
|
+
for (const application of definition.applications()) {
|
|
175
|
+
const type = targetTypeExtractor(application.parent);
|
|
176
|
+
if (!type) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
collectTargetFields({
|
|
180
|
+
parentType: type,
|
|
181
|
+
directive: application,
|
|
182
|
+
includeInterfaceFieldsImplementations: true,
|
|
183
|
+
validate: false,
|
|
184
|
+
}).filter((field) => metadata.isFieldExternal(field))
|
|
185
|
+
.forEach((field) => usedExternalCoordinates.add(field.coordinate));
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
function validateAllExternalFieldsUsed(metadata, errorCollector) {
|
|
189
|
+
const allUsedExternals = collectUsedExternalFieldsCoordinates(metadata);
|
|
190
|
+
for (const type of metadata.schema.types()) {
|
|
181
191
|
if (!(0, definitions_1.isObjectType)(type) && !(0, definitions_1.isInterfaceType)(type)) {
|
|
182
192
|
continue;
|
|
183
193
|
}
|
|
184
194
|
for (const field of type.fields()) {
|
|
185
|
-
if (!
|
|
195
|
+
if (!metadata.isFieldExternal(field) || allUsedExternals.has(field.coordinate)) {
|
|
186
196
|
continue;
|
|
187
197
|
}
|
|
188
198
|
if (!isFieldSatisfyingInterface(field)) {
|
|
@@ -195,87 +205,250 @@ function validateAllExternalFieldsUsed(schema, externalTester, allExternalFields
|
|
|
195
205
|
}
|
|
196
206
|
}
|
|
197
207
|
}
|
|
208
|
+
function validateNoExternalOnInterfaceFields(metadata, errorCollector) {
|
|
209
|
+
for (const itf of metadata.schema.types('InterfaceType')) {
|
|
210
|
+
for (const field of itf.fields()) {
|
|
211
|
+
if (metadata.isFieldExternal(field)) {
|
|
212
|
+
errorCollector.push(error_1.ERRORS.EXTERNAL_ON_INTERFACE.err({
|
|
213
|
+
message: `Interface type field "${field.coordinate}" is marked @external but @external is not allowed on interface fields (it is nonsensical).`,
|
|
214
|
+
nodes: field.sourceAST,
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
198
220
|
function isFieldSatisfyingInterface(field) {
|
|
199
221
|
return field.parent.interfaces().some(itf => itf.field(field.name));
|
|
200
222
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
this.addBuiltInDirective(schema, exports.extendsDirectiveName)
|
|
217
|
-
.addLocations(graphql_1.DirectiveLocation.OBJECT, graphql_1.DirectiveLocation.INTERFACE);
|
|
218
|
-
this.addBuiltInDirective(schema, exports.externalDirectiveName)
|
|
219
|
-
.addLocations(graphql_1.DirectiveLocation.OBJECT, graphql_1.DirectiveLocation.FIELD_DEFINITION);
|
|
220
|
-
for (const name of [exports.requiresDirectiveName, exports.providesDirectiveName]) {
|
|
221
|
-
this.addBuiltInDirective(schema, name)
|
|
222
|
-
.addLocations(graphql_1.DirectiveLocation.FIELD_DEFINITION)
|
|
223
|
-
.addArgument('fields', fieldSetType);
|
|
224
|
-
}
|
|
225
|
-
const directive = this.addBuiltInDirective(schema, 'tag').addLocations(...tagSpec_1.tagLocations);
|
|
226
|
-
directive.addArgument("name", new definitions_1.NonNullType(schema.stringType()));
|
|
227
|
-
}
|
|
228
|
-
prepareValidation(schema) {
|
|
229
|
-
super.prepareValidation(schema);
|
|
230
|
-
let entityType = schema.type(exports.entityTypeName);
|
|
231
|
-
if (!entityType.isBuiltIn) {
|
|
232
|
-
if (entityType.membersCount() === 0) {
|
|
233
|
-
entityType.remove();
|
|
234
|
-
}
|
|
235
|
-
entityType = schema.builtInTypes('UnionType', true).find(u => u.name === exports.entityTypeName);
|
|
236
|
-
}
|
|
237
|
-
entityType.clearTypes();
|
|
238
|
-
for (const objectType of schema.types("ObjectType")) {
|
|
239
|
-
if (isEntityType(objectType)) {
|
|
240
|
-
entityType.addType(objectType);
|
|
241
|
-
}
|
|
242
|
-
}
|
|
243
|
-
const hasEntities = entityType.membersCount() > 0;
|
|
244
|
-
if (!hasEntities) {
|
|
245
|
-
entityType.remove();
|
|
246
|
-
}
|
|
247
|
-
const queryRoot = schema.schemaDefinition.root("query");
|
|
248
|
-
const queryType = queryRoot ? queryRoot.type : schema.addType(new definitions_1.ObjectType("Query"));
|
|
249
|
-
const entityField = queryType.field(exports.entitiesFieldName);
|
|
250
|
-
if (hasEntities) {
|
|
251
|
-
const anyType = schema.type(exports.anyTypeName);
|
|
252
|
-
(0, utils_1.assert)(anyType, `The schema should have the _Any type`);
|
|
253
|
-
const entityFieldType = new definitions_1.NonNullType(new definitions_1.ListType(entityType));
|
|
254
|
-
if (!entityField) {
|
|
255
|
-
this.addBuiltInField(queryType, exports.entitiesFieldName, entityFieldType)
|
|
256
|
-
.addArgument('representations', new definitions_1.NonNullType(new definitions_1.ListType(new definitions_1.NonNullType(anyType))));
|
|
223
|
+
function validateInterfaceRuntimeImplementationFieldsTypes(itf, metadata, errorCollector) {
|
|
224
|
+
var _a;
|
|
225
|
+
const requiresDirective = (_a = federationMetadata(itf.schema())) === null || _a === void 0 ? void 0 : _a.requiresDirective();
|
|
226
|
+
(0, utils_1.assert)(requiresDirective, 'Schema should be a federation subgraph, but @requires directive not found');
|
|
227
|
+
const runtimeTypes = itf.possibleRuntimeTypes();
|
|
228
|
+
for (const field of itf.fields()) {
|
|
229
|
+
const withExternalOrRequires = [];
|
|
230
|
+
const typeToImplems = new utils_1.MultiMap();
|
|
231
|
+
const nodes = [];
|
|
232
|
+
for (const type of runtimeTypes) {
|
|
233
|
+
const implemField = type.field(field.name);
|
|
234
|
+
if (!implemField)
|
|
235
|
+
continue;
|
|
236
|
+
if (implemField.sourceAST) {
|
|
237
|
+
nodes.push(implemField.sourceAST);
|
|
257
238
|
}
|
|
258
|
-
|
|
259
|
-
|
|
239
|
+
if (metadata.isFieldExternal(implemField) || implemField.hasAppliedDirective(requiresDirective)) {
|
|
240
|
+
withExternalOrRequires.push(implemField);
|
|
260
241
|
}
|
|
242
|
+
const returnType = implemField.type;
|
|
243
|
+
typeToImplems.add(returnType.toString(), implemField);
|
|
261
244
|
}
|
|
262
|
-
|
|
263
|
-
|
|
245
|
+
if (withExternalOrRequires.length > 0 && typeToImplems.size > 1) {
|
|
246
|
+
const typeToImplemsArray = [...typeToImplems.entries()];
|
|
247
|
+
errorCollector.push(error_1.ERRORS.INTERFACE_FIELD_IMPLEM_TYPE_MISMATCH.err({
|
|
248
|
+
message: `Some of the runtime implementations of interface field "${field.coordinate}" are marked @external or have a @require (${withExternalOrRequires.map(printFieldCoordinate)}) so all the implementations should use the same type (a current limitation of federation; see https://github.com/apollographql/federation/issues/1257), but ${formatFieldsToReturnType(typeToImplemsArray[0])} while ${(0, utils_1.joinStrings)(typeToImplemsArray.slice(1).map(formatFieldsToReturnType), ' and ')}.`,
|
|
249
|
+
nodes
|
|
250
|
+
}));
|
|
264
251
|
}
|
|
265
|
-
|
|
266
|
-
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
const printFieldCoordinate = (f) => `"${f.coordinate}"`;
|
|
255
|
+
function formatFieldsToReturnType([type, implems]) {
|
|
256
|
+
return `${(0, utils_1.joinStrings)(implems.map(printFieldCoordinate))} ${implems.length == 1 ? 'has' : 'have'} type "${type}"`;
|
|
257
|
+
}
|
|
258
|
+
function checkIfFed2Schema(schema) {
|
|
259
|
+
const core = schema.coreFeatures;
|
|
260
|
+
if (!core) {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
const federationFeature = core.getByIdentity(federationSpec.identity);
|
|
264
|
+
return !!federationFeature && federationFeature.url.version.satisfies(new coreSpec_1.FeatureVersion(2, 0));
|
|
265
|
+
}
|
|
266
|
+
class FederationMetadata {
|
|
267
|
+
constructor(schema) {
|
|
268
|
+
this.schema = schema;
|
|
269
|
+
}
|
|
270
|
+
onInvalidate() {
|
|
271
|
+
this._externalTester = undefined;
|
|
272
|
+
this._sharingPredicate = undefined;
|
|
273
|
+
this._isFed2Schema = undefined;
|
|
274
|
+
}
|
|
275
|
+
isFed2Schema() {
|
|
276
|
+
if (!this._isFed2Schema) {
|
|
277
|
+
this._isFed2Schema = checkIfFed2Schema(this.schema);
|
|
278
|
+
}
|
|
279
|
+
return this._isFed2Schema;
|
|
280
|
+
}
|
|
281
|
+
externalTester() {
|
|
282
|
+
if (!this._externalTester) {
|
|
283
|
+
this._externalTester = new ExternalTester(this.schema);
|
|
267
284
|
}
|
|
285
|
+
return this._externalTester;
|
|
286
|
+
}
|
|
287
|
+
sharingPredicate() {
|
|
288
|
+
if (!this._sharingPredicate) {
|
|
289
|
+
this._sharingPredicate = (0, sharing_1.computeShareables)(this.schema);
|
|
290
|
+
}
|
|
291
|
+
return this._sharingPredicate;
|
|
292
|
+
}
|
|
293
|
+
isFieldExternal(field) {
|
|
294
|
+
return this.externalTester().isExternal(field);
|
|
295
|
+
}
|
|
296
|
+
isFieldPartiallyExternal(field) {
|
|
297
|
+
return this.externalTester().isPartiallyExternal(field);
|
|
298
|
+
}
|
|
299
|
+
isFieldFullyExternal(field) {
|
|
300
|
+
return this.externalTester().isFullyExternal(field);
|
|
301
|
+
}
|
|
302
|
+
isFieldFakeExternal(field) {
|
|
303
|
+
return this.externalTester().isFakeExternal(field);
|
|
304
|
+
}
|
|
305
|
+
selectionSelectsAnyExternalField(selectionSet) {
|
|
306
|
+
return this.externalTester().selectsAnyExternalField(selectionSet);
|
|
307
|
+
}
|
|
308
|
+
isFieldShareable(field) {
|
|
309
|
+
return this.sharingPredicate()(field);
|
|
310
|
+
}
|
|
311
|
+
federationDirectiveNameInSchema(name) {
|
|
312
|
+
if (this.isFed2Schema()) {
|
|
313
|
+
const coreFeatures = this.schema.coreFeatures;
|
|
314
|
+
(0, utils_1.assert)(coreFeatures, 'Schema should be a core schema');
|
|
315
|
+
const federationFeature = coreFeatures.getByIdentity(federationSpec.identity);
|
|
316
|
+
(0, utils_1.assert)(federationFeature, 'Schema should have the federation feature');
|
|
317
|
+
return federationFeature.directiveNameInSchema(name);
|
|
318
|
+
}
|
|
319
|
+
else {
|
|
320
|
+
return name;
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
federationTypeNameInSchema(name) {
|
|
324
|
+
if (name.charAt(0) === '_') {
|
|
325
|
+
return name;
|
|
326
|
+
}
|
|
327
|
+
if (this.isFed2Schema()) {
|
|
328
|
+
const coreFeatures = this.schema.coreFeatures;
|
|
329
|
+
(0, utils_1.assert)(coreFeatures, 'Schema should be a core schema');
|
|
330
|
+
const federationFeature = coreFeatures.getByIdentity(federationSpec.identity);
|
|
331
|
+
(0, utils_1.assert)(federationFeature, 'Schema should have the federation feature');
|
|
332
|
+
return federationFeature.typeNameInSchema(name);
|
|
333
|
+
}
|
|
334
|
+
else {
|
|
335
|
+
return '_' + name;
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
getFederationDirective(name) {
|
|
339
|
+
const directive = this.schema.directive(this.federationDirectiveNameInSchema(name));
|
|
340
|
+
(0, utils_1.assert)(directive, `The provided schema does not have federation directive @${name}`);
|
|
341
|
+
return directive;
|
|
342
|
+
}
|
|
343
|
+
keyDirective() {
|
|
344
|
+
return this.getFederationDirective(federationSpec_1.keyDirectiveSpec.name);
|
|
345
|
+
}
|
|
346
|
+
extendsDirective() {
|
|
347
|
+
return this.getFederationDirective(federationSpec_1.extendsDirectiveSpec.name);
|
|
348
|
+
}
|
|
349
|
+
externalDirective() {
|
|
350
|
+
return this.getFederationDirective(federationSpec_1.externalDirectiveSpec.name);
|
|
351
|
+
}
|
|
352
|
+
requiresDirective() {
|
|
353
|
+
return this.getFederationDirective(federationSpec_1.requiresDirectiveSpec.name);
|
|
354
|
+
}
|
|
355
|
+
providesDirective() {
|
|
356
|
+
return this.getFederationDirective(federationSpec_1.providesDirectiveSpec.name);
|
|
357
|
+
}
|
|
358
|
+
shareableDirective() {
|
|
359
|
+
return this.getFederationDirective(federationSpec_1.shareableDirectiveSpec.name);
|
|
360
|
+
}
|
|
361
|
+
tagDirective() {
|
|
362
|
+
return this.getFederationDirective(federationSpec_1.tagDirectiveSpec.name);
|
|
363
|
+
}
|
|
364
|
+
allFederationDirectives() {
|
|
365
|
+
const baseDirectives = [
|
|
366
|
+
this.keyDirective(),
|
|
367
|
+
this.externalDirective(),
|
|
368
|
+
this.requiresDirective(),
|
|
369
|
+
this.providesDirective(),
|
|
370
|
+
this.tagDirective(),
|
|
371
|
+
this.extendsDirective(),
|
|
372
|
+
];
|
|
373
|
+
return this.isFed2Schema()
|
|
374
|
+
? baseDirectives.concat(this.shareableDirective())
|
|
375
|
+
: baseDirectives;
|
|
376
|
+
}
|
|
377
|
+
entityType() {
|
|
378
|
+
return this.schema.type(this.federationTypeNameInSchema(exports.entityTypeSpec.name));
|
|
379
|
+
}
|
|
380
|
+
anyType() {
|
|
381
|
+
return this.schema.type(this.federationTypeNameInSchema(exports.anyTypeSpec.name));
|
|
382
|
+
}
|
|
383
|
+
serviceType() {
|
|
384
|
+
return this.schema.type(this.federationTypeNameInSchema(exports.serviceTypeSpec.name));
|
|
385
|
+
}
|
|
386
|
+
fieldSetType() {
|
|
387
|
+
return this.schema.type(this.federationTypeNameInSchema(federationSpec_1.fieldSetTypeSpec.name));
|
|
388
|
+
}
|
|
389
|
+
allFederationTypes() {
|
|
390
|
+
const baseTypes = [
|
|
391
|
+
this.anyType(),
|
|
392
|
+
this.serviceType(),
|
|
393
|
+
this.fieldSetType(),
|
|
394
|
+
];
|
|
395
|
+
const entityType = this.entityType();
|
|
396
|
+
if (entityType) {
|
|
397
|
+
baseTypes.push(entityType);
|
|
398
|
+
}
|
|
399
|
+
return baseTypes;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
exports.FederationMetadata = FederationMetadata;
|
|
403
|
+
class FederationBlueprint extends definitions_1.SchemaBlueprint {
|
|
404
|
+
onAddedCoreFeature(schema, feature) {
|
|
405
|
+
super.onAddedCoreFeature(schema, feature);
|
|
406
|
+
if (feature.url.identity === federationSpec_1.federationIdentity) {
|
|
407
|
+
const spec = federationSpec_1.FEDERATION_VERSIONS.find(feature.url.version);
|
|
408
|
+
if (spec) {
|
|
409
|
+
spec.addElementsToSchema(schema);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
onMissingDirectiveDefinition(schema, name) {
|
|
414
|
+
if (name === coreSpec_1.linkDirectiveDefaultName) {
|
|
415
|
+
linkSpec.addToSchema(schema);
|
|
416
|
+
return schema.directive(name);
|
|
417
|
+
}
|
|
418
|
+
return super.onMissingDirectiveDefinition(schema, name);
|
|
419
|
+
}
|
|
420
|
+
ignoreParsedField(type, fieldName) {
|
|
421
|
+
if (!exports.FEDERATION_OPERATION_FIELDS.includes(fieldName)) {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
const metadata = federationMetadata(type.schema());
|
|
425
|
+
return !!metadata && !metadata.isFed2Schema();
|
|
426
|
+
}
|
|
427
|
+
onConstructed(schema) {
|
|
428
|
+
const existing = federationMetadata(schema);
|
|
429
|
+
if (!existing) {
|
|
430
|
+
schema['_federationMetadata'] = new FederationMetadata(schema);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
onDirectiveDefinitionAndSchemaParsed(schema) {
|
|
434
|
+
completeSubgraphSchema(schema);
|
|
435
|
+
}
|
|
436
|
+
onInvalidation(schema) {
|
|
437
|
+
super.onInvalidation(schema);
|
|
438
|
+
const metadata = federationMetadata(schema);
|
|
439
|
+
(0, utils_1.assert)(metadata, 'Federation schema should have had its metadata set on construction');
|
|
440
|
+
FederationMetadata.prototype['onInvalidate'].call(metadata);
|
|
268
441
|
}
|
|
269
442
|
onValidation(schema) {
|
|
270
443
|
var _a;
|
|
271
|
-
const errors = super.onValidation(schema
|
|
444
|
+
const errors = super.onValidation(schema);
|
|
272
445
|
for (const k of definitions_1.allSchemaRootKinds) {
|
|
273
446
|
const type = (_a = schema.schemaDefinition.root(k)) === null || _a === void 0 ? void 0 : _a.type;
|
|
274
447
|
const defaultName = (0, definitions_1.defaultRootName)(k);
|
|
275
448
|
if (type && type.name !== defaultName) {
|
|
276
449
|
const existing = schema.type(defaultName);
|
|
277
450
|
if (existing) {
|
|
278
|
-
errors.push(
|
|
451
|
+
errors.push(error_1.ERROR_CATEGORIES.ROOT_TYPE_USED.get(k).err({
|
|
279
452
|
message: `The schema has a type named "${defaultName}" but it is not set as the ${k} root type ("${type.name}" is instead): `
|
|
280
453
|
+ 'this is not supported by federation. '
|
|
281
454
|
+ 'If a root type does not use its default name, there should be no other type with that default name.',
|
|
@@ -285,21 +458,25 @@ class FederationBuiltIns extends definitions_1.BuiltIns {
|
|
|
285
458
|
type.rename(defaultName);
|
|
286
459
|
}
|
|
287
460
|
}
|
|
288
|
-
const
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
461
|
+
const metadata = federationMetadata(schema);
|
|
462
|
+
(0, utils_1.assert)(metadata, 'Federation schema should have had its metadata set on construction');
|
|
463
|
+
if (!metadata.isFed2Schema()) {
|
|
464
|
+
return errors;
|
|
465
|
+
}
|
|
466
|
+
const keyDirective = metadata.keyDirective();
|
|
467
|
+
validateAllFieldSet(keyDirective, type => type, errors, metadata, true, true, field => {
|
|
468
|
+
const type = (0, definitions_1.baseType)(field.type);
|
|
469
|
+
if ((0, definitions_1.isUnionType)(type) || (0, definitions_1.isInterfaceType)(type)) {
|
|
470
|
+
let kind = type.kind;
|
|
294
471
|
kind = kind.slice(0, kind.length - 'Type'.length);
|
|
295
472
|
throw error_1.ERRORS.KEY_FIELDS_SELECT_INVALID_TYPE.err({
|
|
296
473
|
message: `field "${field.coordinate}" is a ${kind} type which is not allowed in @key`
|
|
297
474
|
});
|
|
298
475
|
}
|
|
299
476
|
});
|
|
300
|
-
validateAllFieldSet(
|
|
301
|
-
validateAllFieldSet(
|
|
302
|
-
if (
|
|
477
|
+
validateAllFieldSet(metadata.requiresDirective(), field => field.parent, errors, metadata, false, false);
|
|
478
|
+
validateAllFieldSet(metadata.providesDirective(), field => {
|
|
479
|
+
if (metadata.isFieldExternal(field)) {
|
|
303
480
|
throw new graphql_1.GraphQLError(`Cannot have both @provides and @external on field "${field.coordinate}"`, field.sourceAST);
|
|
304
481
|
}
|
|
305
482
|
const type = (0, definitions_1.baseType)(field.type);
|
|
@@ -310,90 +487,129 @@ class FederationBuiltIns extends definitions_1.BuiltIns {
|
|
|
310
487
|
});
|
|
311
488
|
}
|
|
312
489
|
return type;
|
|
313
|
-
}, errors,
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
490
|
+
}, errors, metadata, false, false);
|
|
491
|
+
validateNoExternalOnInterfaceFields(metadata, errors);
|
|
492
|
+
validateAllExternalFieldsUsed(metadata, errors);
|
|
493
|
+
const tagDirective = metadata.tagDirective();
|
|
494
|
+
if (tagDirective) {
|
|
317
495
|
const error = tagSpec.checkCompatibleDirective(tagDirective);
|
|
318
496
|
if (error) {
|
|
319
497
|
errors.push(error);
|
|
320
498
|
}
|
|
321
499
|
}
|
|
500
|
+
for (const itf of schema.types('InterfaceType')) {
|
|
501
|
+
validateInterfaceRuntimeImplementationFieldsTypes(itf, metadata, errors);
|
|
502
|
+
}
|
|
322
503
|
return errors;
|
|
323
504
|
}
|
|
324
505
|
validationRules() {
|
|
325
506
|
return FEDERATION_VALIDATION_RULES;
|
|
326
507
|
}
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
externalDirective(schema) {
|
|
334
|
-
return this.getTypedDirective(schema, exports.externalDirectiveName);
|
|
335
|
-
}
|
|
336
|
-
requiresDirective(schema) {
|
|
337
|
-
return this.getTypedDirective(schema, exports.requiresDirectiveName);
|
|
508
|
+
}
|
|
509
|
+
exports.FederationBlueprint = FederationBlueprint;
|
|
510
|
+
const federationBlueprint = new FederationBlueprint();
|
|
511
|
+
function findUnusedNamedForLinkDirective(schema) {
|
|
512
|
+
if (!schema.directive(linkSpec.url.name)) {
|
|
513
|
+
return undefined;
|
|
338
514
|
}
|
|
339
|
-
|
|
340
|
-
|
|
515
|
+
const baseName = linkSpec.url.name;
|
|
516
|
+
let n = 1;
|
|
517
|
+
for (;;) {
|
|
518
|
+
const candidate = baseName + n;
|
|
519
|
+
if (!schema.directive(candidate)) {
|
|
520
|
+
return candidate;
|
|
521
|
+
}
|
|
341
522
|
}
|
|
342
|
-
|
|
343
|
-
|
|
523
|
+
}
|
|
524
|
+
function setSchemaAsFed2Subgraph(schema) {
|
|
525
|
+
let core = schema.coreFeatures;
|
|
526
|
+
let spec;
|
|
527
|
+
if (core) {
|
|
528
|
+
spec = core.coreDefinition;
|
|
529
|
+
(0, utils_1.assert)(spec.url.version.satisfies(linkSpec.version), `Fed2 schema must use @link with version >= 1.0, but schema uses ${spec.url}`);
|
|
344
530
|
}
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
if (directive.isBuiltIn) {
|
|
352
|
-
definitions.push((0, graphql_1.parse)((0, print_1.printDirectiveDefinition)(directive, print_1.defaultPrintOptions)).definitions[0]);
|
|
353
|
-
}
|
|
354
|
-
}
|
|
355
|
-
return {
|
|
356
|
-
kind: graphql_1.Kind.DOCUMENT,
|
|
357
|
-
loc: document.loc,
|
|
358
|
-
definitions
|
|
359
|
-
};
|
|
531
|
+
else {
|
|
532
|
+
const alias = findUnusedNamedForLinkDirective(schema);
|
|
533
|
+
linkSpec.addToSchema(schema, alias);
|
|
534
|
+
spec = linkSpec;
|
|
535
|
+
core = schema.coreFeatures;
|
|
536
|
+
(0, utils_1.assert)(core, 'Schema should now be a core schema');
|
|
360
537
|
}
|
|
538
|
+
(0, utils_1.assert)(!core.getByIdentity(federationSpec.identity), 'Schema already set as a federation subgraph');
|
|
539
|
+
schema.schemaDefinition.applyDirective(core.coreItself.nameInSchema, {
|
|
540
|
+
url: federationSpec.url.toString(),
|
|
541
|
+
import: federationSpec_1.FEDERATION2_SPEC_DIRECTIVES.map((spec) => `@${spec.name}`),
|
|
542
|
+
});
|
|
543
|
+
completeSubgraphSchema(schema);
|
|
361
544
|
}
|
|
362
|
-
exports.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
545
|
+
exports.setSchemaAsFed2Subgraph = setSchemaAsFed2Subgraph;
|
|
546
|
+
function asFed2SubgraphDocument(document) {
|
|
547
|
+
const fed2LinkExtension = {
|
|
548
|
+
kind: graphql_1.Kind.SCHEMA_EXTENSION,
|
|
549
|
+
directives: [{
|
|
550
|
+
kind: graphql_1.Kind.DIRECTIVE,
|
|
551
|
+
name: { kind: graphql_1.Kind.NAME, value: coreSpec_1.linkDirectiveDefaultName },
|
|
552
|
+
arguments: [{
|
|
553
|
+
kind: graphql_1.Kind.ARGUMENT,
|
|
554
|
+
name: { kind: graphql_1.Kind.NAME, value: 'url' },
|
|
555
|
+
value: { kind: graphql_1.Kind.STRING, value: federationSpec.url.toString() }
|
|
556
|
+
},
|
|
557
|
+
{
|
|
558
|
+
kind: graphql_1.Kind.ARGUMENT,
|
|
559
|
+
name: { kind: graphql_1.Kind.NAME, value: 'import' },
|
|
560
|
+
value: { kind: graphql_1.Kind.LIST, values: federationSpec_1.FEDERATION2_SPEC_DIRECTIVES.map((spec) => ({ kind: graphql_1.Kind.STRING, value: `@${spec.name}` })) }
|
|
561
|
+
}]
|
|
562
|
+
}]
|
|
563
|
+
};
|
|
564
|
+
return {
|
|
565
|
+
kind: graphql_1.Kind.DOCUMENT,
|
|
566
|
+
loc: document.loc,
|
|
567
|
+
definitions: document.definitions.concat(fed2LinkExtension)
|
|
568
|
+
};
|
|
366
569
|
}
|
|
367
|
-
exports.
|
|
368
|
-
function
|
|
369
|
-
return
|
|
570
|
+
exports.asFed2SubgraphDocument = asFed2SubgraphDocument;
|
|
571
|
+
function printSubgraphNames(names) {
|
|
572
|
+
return (0, utils_1.printHumanReadableList)(names.map(n => `"${n}"`), {
|
|
573
|
+
prefix: 'subgraph',
|
|
574
|
+
prefixPlural: 'subgraphs',
|
|
575
|
+
});
|
|
370
576
|
}
|
|
371
|
-
exports.
|
|
372
|
-
function
|
|
373
|
-
return
|
|
577
|
+
exports.printSubgraphNames = printSubgraphNames;
|
|
578
|
+
function federationMetadata(schema) {
|
|
579
|
+
return schema['_federationMetadata'];
|
|
374
580
|
}
|
|
375
|
-
exports.
|
|
581
|
+
exports.federationMetadata = federationMetadata;
|
|
582
|
+
function isFederationSubgraphSchema(schema) {
|
|
583
|
+
return !!federationMetadata(schema);
|
|
584
|
+
}
|
|
585
|
+
exports.isFederationSubgraphSchema = isFederationSubgraphSchema;
|
|
376
586
|
function isFederationField(field) {
|
|
377
587
|
var _a;
|
|
378
588
|
if (field.parent === ((_a = field.schema().schemaDefinition.root("query")) === null || _a === void 0 ? void 0 : _a.type)) {
|
|
379
|
-
return
|
|
589
|
+
return exports.FEDERATION_OPERATION_FIELDS.includes(field.name);
|
|
380
590
|
}
|
|
381
591
|
return false;
|
|
382
592
|
}
|
|
383
593
|
exports.isFederationField = isFederationField;
|
|
384
|
-
function isFederationDirective(directive) {
|
|
385
|
-
return FEDERATION_DIRECTIVES.includes(directive.name);
|
|
386
|
-
}
|
|
387
|
-
exports.isFederationDirective = isFederationDirective;
|
|
388
594
|
function isEntityType(type) {
|
|
389
|
-
|
|
595
|
+
if (type.kind !== "ObjectType") {
|
|
596
|
+
return false;
|
|
597
|
+
}
|
|
598
|
+
const metadata = federationMetadata(type.schema());
|
|
599
|
+
return !!metadata && type.hasAppliedDirective(metadata.keyDirective());
|
|
390
600
|
}
|
|
391
601
|
exports.isEntityType = isEntityType;
|
|
392
|
-
function buildSubgraph(name, source) {
|
|
602
|
+
function buildSubgraph(name, url, source) {
|
|
603
|
+
const buildOptions = {
|
|
604
|
+
blueprint: federationBlueprint,
|
|
605
|
+
validate: false,
|
|
606
|
+
};
|
|
607
|
+
let subgraph;
|
|
393
608
|
try {
|
|
394
|
-
|
|
395
|
-
? (0, buildSchema_1.buildSchema)(new graphql_1.Source(source, name),
|
|
396
|
-
: (0, buildSchema_1.buildSchemaFromAST)(source,
|
|
609
|
+
const schema = typeof source === 'string'
|
|
610
|
+
? (0, buildSchema_1.buildSchema)(new graphql_1.Source(source, name), buildOptions)
|
|
611
|
+
: (0, buildSchema_1.buildSchemaFromAST)(source, buildOptions);
|
|
612
|
+
subgraph = new Subgraph(name, url, schema);
|
|
397
613
|
}
|
|
398
614
|
catch (e) {
|
|
399
615
|
if (e instanceof graphql_1.GraphQLError) {
|
|
@@ -403,14 +619,73 @@ function buildSubgraph(name, source) {
|
|
|
403
619
|
throw e;
|
|
404
620
|
}
|
|
405
621
|
}
|
|
622
|
+
return subgraph.validate();
|
|
406
623
|
}
|
|
407
624
|
exports.buildSubgraph = buildSubgraph;
|
|
408
|
-
function
|
|
625
|
+
function newEmptyFederation2Schema() {
|
|
626
|
+
const schema = new definitions_1.Schema(federationBlueprint);
|
|
627
|
+
setSchemaAsFed2Subgraph(schema);
|
|
628
|
+
return schema;
|
|
629
|
+
}
|
|
630
|
+
exports.newEmptyFederation2Schema = newEmptyFederation2Schema;
|
|
631
|
+
function completeSubgraphSchema(schema) {
|
|
632
|
+
const coreFeatures = schema.coreFeatures;
|
|
633
|
+
if (coreFeatures) {
|
|
634
|
+
const fedFeature = coreFeatures.getByIdentity(federationSpec_1.federationIdentity);
|
|
635
|
+
if (fedFeature) {
|
|
636
|
+
completeFed2SubgraphSchema(schema);
|
|
637
|
+
}
|
|
638
|
+
else {
|
|
639
|
+
completeFed1SubgraphSchema(schema);
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
const fedLink = schema.schemaDefinition.appliedDirectivesOf(coreSpec_1.linkDirectiveDefaultName).find(isFedSpecLinkDirective);
|
|
644
|
+
if (fedLink) {
|
|
645
|
+
linkSpec.addToSchema(schema);
|
|
646
|
+
completeFed2SubgraphSchema(schema);
|
|
647
|
+
}
|
|
648
|
+
else {
|
|
649
|
+
completeFed1SubgraphSchema(schema);
|
|
650
|
+
}
|
|
651
|
+
}
|
|
652
|
+
}
|
|
653
|
+
function isFedSpecLinkDirective(directive) {
|
|
654
|
+
const args = directive.arguments();
|
|
655
|
+
return directive.name === coreSpec_1.linkDirectiveDefaultName && args['url'] && args['url'].startsWith(federationSpec_1.federationIdentity);
|
|
656
|
+
}
|
|
657
|
+
function completeFed1SubgraphSchema(schema) {
|
|
658
|
+
federationSpec_1.fieldSetTypeSpec.checkOrAdd(schema, '_' + federationSpec_1.fieldSetTypeSpec.name);
|
|
659
|
+
federationSpec_1.keyDirectiveSpec.checkOrAdd(schema);
|
|
660
|
+
federationSpec_1.requiresDirectiveSpec.checkOrAdd(schema);
|
|
661
|
+
federationSpec_1.providesDirectiveSpec.checkOrAdd(schema);
|
|
662
|
+
federationSpec_1.extendsDirectiveSpec.checkOrAdd(schema);
|
|
663
|
+
federationSpec_1.externalDirectiveSpec.checkOrAdd(schema);
|
|
664
|
+
federationSpec_1.tagDirectiveSpec.checkOrAdd(schema);
|
|
665
|
+
}
|
|
666
|
+
function completeFed2SubgraphSchema(schema) {
|
|
667
|
+
const coreFeatures = schema.coreFeatures;
|
|
668
|
+
(0, utils_1.assert)(coreFeatures, 'This method should not have been called on a non-core schema');
|
|
669
|
+
const fedFeature = coreFeatures.getByIdentity(federationSpec_1.federationIdentity);
|
|
670
|
+
(0, utils_1.assert)(fedFeature, 'This method should not have been called on a schema with no @link for federation');
|
|
671
|
+
const spec = federationSpec_1.FEDERATION_VERSIONS.find(fedFeature.url.version);
|
|
672
|
+
if (!spec) {
|
|
673
|
+
throw error_1.ERRORS.UNKNOWN_FEDERATION_LINK_VERSION.err({
|
|
674
|
+
message: `Invalid version ${fedFeature.url.version} for the federation feature in @link direction on schema`,
|
|
675
|
+
nodes: fedFeature.directive.sourceAST
|
|
676
|
+
});
|
|
677
|
+
}
|
|
678
|
+
spec.addElementsToSchema(schema);
|
|
679
|
+
}
|
|
680
|
+
function parseFieldSetArgument({ parentType, directive, fieldAccessor, validate, }) {
|
|
409
681
|
var _a;
|
|
410
682
|
try {
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
683
|
+
return (0, operations_1.parseSelectionSet)({
|
|
684
|
+
parentType,
|
|
685
|
+
source: validateFieldSetValue(directive),
|
|
686
|
+
fieldAccessor,
|
|
687
|
+
validate,
|
|
688
|
+
});
|
|
414
689
|
}
|
|
415
690
|
catch (e) {
|
|
416
691
|
if (!(e instanceof graphql_1.GraphQLError)) {
|
|
@@ -425,14 +700,14 @@ function parseFieldSetArgument(parentType, directive, fieldAccessor = (type, nam
|
|
|
425
700
|
if (msg.endsWith('.')) {
|
|
426
701
|
msg = msg.slice(0, msg.length - 1);
|
|
427
702
|
}
|
|
428
|
-
if (directive.name ===
|
|
703
|
+
if (directive.name === federationSpec_1.keyDirectiveSpec.name) {
|
|
429
704
|
msg = msg + ' (the field should be either be added to this subgraph or, if it should not be resolved by this subgraph, you need to add it to this subgraph with @external).';
|
|
430
705
|
}
|
|
431
706
|
else {
|
|
432
707
|
msg = msg + ' (if the field is defined in another subgraph, you need to add it to this subgraph with @external).';
|
|
433
708
|
}
|
|
434
709
|
}
|
|
435
|
-
const codeDef = (_a = (0, error_1.errorCodeDef)(e)) !== null && _a !== void 0 ? _a :
|
|
710
|
+
const codeDef = (_a = (0, error_1.errorCodeDef)(e)) !== null && _a !== void 0 ? _a : error_1.ERROR_CATEGORIES.DIRECTIVE_INVALID_FIELDS.get(directive.name);
|
|
436
711
|
throw codeDef.err({
|
|
437
712
|
message: `${fieldSetErrorDescriptor(directive)}: ${msg}`,
|
|
438
713
|
nodes,
|
|
@@ -441,12 +716,45 @@ function parseFieldSetArgument(parentType, directive, fieldAccessor = (type, nam
|
|
|
441
716
|
}
|
|
442
717
|
}
|
|
443
718
|
exports.parseFieldSetArgument = parseFieldSetArgument;
|
|
719
|
+
function collectTargetFields({ parentType, directive, includeInterfaceFieldsImplementations, validate = true, }) {
|
|
720
|
+
const fields = [];
|
|
721
|
+
try {
|
|
722
|
+
parseFieldSetArgument({
|
|
723
|
+
parentType,
|
|
724
|
+
directive,
|
|
725
|
+
fieldAccessor: (t, f) => {
|
|
726
|
+
const field = t.field(f);
|
|
727
|
+
if (field) {
|
|
728
|
+
fields.push(field);
|
|
729
|
+
if (includeInterfaceFieldsImplementations && (0, definitions_1.isInterfaceType)(t)) {
|
|
730
|
+
for (const implType of t.possibleRuntimeTypes()) {
|
|
731
|
+
const implField = implType.field(f);
|
|
732
|
+
if (implField) {
|
|
733
|
+
fields.push(implField);
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
return field;
|
|
739
|
+
},
|
|
740
|
+
validate,
|
|
741
|
+
});
|
|
742
|
+
}
|
|
743
|
+
catch (e) {
|
|
744
|
+
const isGraphQLError = (0, definitions_1.errorCauses)(e) !== undefined;
|
|
745
|
+
if (!isGraphQLError || validate) {
|
|
746
|
+
throw e;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
return fields;
|
|
750
|
+
}
|
|
751
|
+
exports.collectTargetFields = collectTargetFields;
|
|
444
752
|
function validateFieldSetValue(directive) {
|
|
445
753
|
var _a;
|
|
446
754
|
const fields = directive.arguments().fields;
|
|
447
755
|
const nodes = directive.sourceAST;
|
|
448
756
|
if (typeof fields !== 'string') {
|
|
449
|
-
throw
|
|
757
|
+
throw error_1.ERROR_CATEGORIES.DIRECTIVE_INVALID_FIELDS_TYPE.get(directive.name).err({
|
|
450
758
|
message: `Invalid value for argument "${directive.definition.argument('fields').name}": must be a string.`,
|
|
451
759
|
nodes,
|
|
452
760
|
});
|
|
@@ -455,7 +763,7 @@ function validateFieldSetValue(directive) {
|
|
|
455
763
|
for (const argNode of (_a = nodes.arguments) !== null && _a !== void 0 ? _a : []) {
|
|
456
764
|
if (argNode.name.value === 'fields') {
|
|
457
765
|
if (argNode.value.kind !== 'StringValue') {
|
|
458
|
-
throw
|
|
766
|
+
throw error_1.ERROR_CATEGORIES.DIRECTIVE_INVALID_FIELDS_TYPE.get(directive.name).err({
|
|
459
767
|
message: `Invalid value for argument "${directive.definition.argument('fields').name}": must be a string.`,
|
|
460
768
|
nodes,
|
|
461
769
|
});
|
|
@@ -472,7 +780,7 @@ function subgraphsFromServiceList(serviceList) {
|
|
|
472
780
|
const subgraphs = new Subgraphs();
|
|
473
781
|
for (const service of serviceList) {
|
|
474
782
|
try {
|
|
475
|
-
subgraphs.add(service.name, (_a = service.url) !== null && _a !== void 0 ? _a : '', service.typeDefs);
|
|
783
|
+
subgraphs.add(buildSubgraph(service.name, (_a = service.url) !== null && _a !== void 0 ? _a : '', service.typeDefs));
|
|
476
784
|
}
|
|
477
785
|
catch (e) {
|
|
478
786
|
const causes = (0, definitions_1.errorCauses)(e);
|
|
@@ -491,18 +799,12 @@ class Subgraphs {
|
|
|
491
799
|
constructor() {
|
|
492
800
|
this.subgraphs = new utils_1.OrderedMap();
|
|
493
801
|
}
|
|
494
|
-
add(
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
: subgraphOrName;
|
|
498
|
-
if (toAdd.name === exports.FEDERATION_RESERVED_SUBGRAPH_NAME) {
|
|
499
|
-
throw error_1.ERRORS.INVALID_SUBGRAPH_NAME.err({ message: `Invalid name ${exports.FEDERATION_RESERVED_SUBGRAPH_NAME} for a subgraph: this name is reserved` });
|
|
500
|
-
}
|
|
501
|
-
if (this.subgraphs.has(toAdd.name)) {
|
|
502
|
-
throw new Error(`A subgraph named ${toAdd.name} already exists` + (toAdd.url ? ` (with url '${toAdd.url}')` : ''));
|
|
802
|
+
add(subgraph) {
|
|
803
|
+
if (this.subgraphs.has(subgraph.name)) {
|
|
804
|
+
throw new Error(`A subgraph named ${subgraph.name} already exists` + (subgraph.url ? ` (with url '${subgraph.url}')` : ''));
|
|
503
805
|
}
|
|
504
|
-
this.subgraphs.add(
|
|
505
|
-
return
|
|
806
|
+
this.subgraphs.add(subgraph.name, subgraph);
|
|
807
|
+
return subgraph;
|
|
506
808
|
}
|
|
507
809
|
get(name) {
|
|
508
810
|
return this.subgraphs.get(name);
|
|
@@ -521,22 +823,123 @@ class Subgraphs {
|
|
|
521
823
|
yield subgraph;
|
|
522
824
|
}
|
|
523
825
|
}
|
|
826
|
+
validate() {
|
|
827
|
+
let errors = [];
|
|
828
|
+
for (const subgraph of this.values()) {
|
|
829
|
+
try {
|
|
830
|
+
subgraph.validate();
|
|
831
|
+
}
|
|
832
|
+
catch (e) {
|
|
833
|
+
const causes = (0, definitions_1.errorCauses)(e);
|
|
834
|
+
if (!causes) {
|
|
835
|
+
throw e;
|
|
836
|
+
}
|
|
837
|
+
errors = errors.concat(causes);
|
|
838
|
+
}
|
|
839
|
+
}
|
|
840
|
+
return errors.length === 0 ? undefined : errors;
|
|
841
|
+
}
|
|
524
842
|
toString() {
|
|
525
843
|
return '[' + this.subgraphs.keys().join(', ') + ']';
|
|
526
844
|
}
|
|
527
845
|
}
|
|
528
846
|
exports.Subgraphs = Subgraphs;
|
|
847
|
+
exports.anyTypeSpec = (0, directiveAndTypeSpecification_1.createScalarTypeSpecification)({ name: '_Any' });
|
|
848
|
+
exports.serviceTypeSpec = (0, directiveAndTypeSpecification_1.createObjectTypeSpecification)({
|
|
849
|
+
name: '_Service',
|
|
850
|
+
fieldsFct: (schema) => [{ name: 'sdl', type: schema.stringType() }],
|
|
851
|
+
});
|
|
852
|
+
exports.entityTypeSpec = (0, directiveAndTypeSpecification_1.createUnionTypeSpecification)({
|
|
853
|
+
name: '_Entity',
|
|
854
|
+
membersFct: (schema) => {
|
|
855
|
+
return schema.types("ObjectType").filter(isEntityType).map((t) => t.name);
|
|
856
|
+
},
|
|
857
|
+
});
|
|
858
|
+
exports.FEDERATION_OPERATION_TYPES = [exports.anyTypeSpec, exports.serviceTypeSpec, exports.entityTypeSpec];
|
|
859
|
+
exports.serviceFieldName = '_service';
|
|
860
|
+
exports.entitiesFieldName = '_entities';
|
|
861
|
+
exports.FEDERATION_OPERATION_FIELDS = [exports.serviceFieldName, exports.entitiesFieldName];
|
|
529
862
|
class Subgraph {
|
|
530
|
-
constructor(name, url, schema
|
|
863
|
+
constructor(name, url, schema) {
|
|
531
864
|
this.name = name;
|
|
532
865
|
this.url = url;
|
|
533
866
|
this.schema = schema;
|
|
534
|
-
if (
|
|
535
|
-
|
|
867
|
+
if (name === exports.FEDERATION_RESERVED_SUBGRAPH_NAME) {
|
|
868
|
+
throw error_1.ERRORS.INVALID_SUBGRAPH_NAME.err({ message: `Invalid name ${exports.FEDERATION_RESERVED_SUBGRAPH_NAME} for a subgraph: this name is reserved` });
|
|
536
869
|
}
|
|
537
870
|
}
|
|
538
|
-
|
|
539
|
-
|
|
871
|
+
metadata() {
|
|
872
|
+
const metadata = federationMetadata(this.schema);
|
|
873
|
+
(0, utils_1.assert)(metadata, 'The subgraph schema should have built with the federation built-ins.');
|
|
874
|
+
return metadata;
|
|
875
|
+
}
|
|
876
|
+
isFed2Subgraph() {
|
|
877
|
+
return this.metadata().isFed2Schema();
|
|
878
|
+
}
|
|
879
|
+
addFederationOperations() {
|
|
880
|
+
const metadata = this.metadata();
|
|
881
|
+
for (const type of exports.FEDERATION_OPERATION_TYPES) {
|
|
882
|
+
type.checkOrAdd(this.schema);
|
|
883
|
+
}
|
|
884
|
+
const queryRoot = this.schema.schemaDefinition.root("query");
|
|
885
|
+
const queryType = queryRoot ? queryRoot.type : this.schema.addType(new definitions_1.ObjectType("Query"));
|
|
886
|
+
const entityField = queryType.field(exports.entitiesFieldName);
|
|
887
|
+
const entityType = metadata.entityType();
|
|
888
|
+
if (entityType) {
|
|
889
|
+
const entityFieldType = new definitions_1.NonNullType(new definitions_1.ListType(entityType));
|
|
890
|
+
if (!entityField) {
|
|
891
|
+
queryType.addField(exports.entitiesFieldName, entityFieldType)
|
|
892
|
+
.addArgument('representations', new definitions_1.NonNullType(new definitions_1.ListType(new definitions_1.NonNullType(metadata.anyType()))));
|
|
893
|
+
}
|
|
894
|
+
else if (!entityField.type) {
|
|
895
|
+
entityField.type = entityType;
|
|
896
|
+
}
|
|
897
|
+
}
|
|
898
|
+
else if (entityField) {
|
|
899
|
+
entityField.remove();
|
|
900
|
+
}
|
|
901
|
+
if (!queryType.field(exports.serviceFieldName)) {
|
|
902
|
+
queryType.addField(exports.serviceFieldName, metadata.serviceType());
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
validate() {
|
|
906
|
+
try {
|
|
907
|
+
this.addFederationOperations();
|
|
908
|
+
this.schema.validate();
|
|
909
|
+
return this;
|
|
910
|
+
}
|
|
911
|
+
catch (e) {
|
|
912
|
+
if (e instanceof graphql_1.GraphQLError) {
|
|
913
|
+
throw addSubgraphToError(e, this.name, error_1.ERRORS.INVALID_GRAPHQL);
|
|
914
|
+
}
|
|
915
|
+
else {
|
|
916
|
+
throw e;
|
|
917
|
+
}
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
isPrintedDirective(d) {
|
|
921
|
+
var _a;
|
|
922
|
+
if (this.metadata().allFederationDirectives().includes(d)) {
|
|
923
|
+
return false;
|
|
924
|
+
}
|
|
925
|
+
const core = this.schema.coreFeatures;
|
|
926
|
+
return !core || ((_a = core.sourceFeature(d)) === null || _a === void 0 ? void 0 : _a.url.identity) !== coreSpec_1.linkIdentity;
|
|
927
|
+
}
|
|
928
|
+
isPrintedType(t) {
|
|
929
|
+
var _a;
|
|
930
|
+
if (this.metadata().allFederationTypes().includes(t)) {
|
|
931
|
+
return false;
|
|
932
|
+
}
|
|
933
|
+
const core = this.schema.coreFeatures;
|
|
934
|
+
return !core || ((_a = core.sourceFeature(t)) === null || _a === void 0 ? void 0 : _a.url.identity) !== coreSpec_1.linkIdentity;
|
|
935
|
+
}
|
|
936
|
+
toString(basePrintOptions = print_1.defaultPrintOptions) {
|
|
937
|
+
return (0, print_1.printSchema)(this.schema, {
|
|
938
|
+
...basePrintOptions,
|
|
939
|
+
directiveDefinitionFilter: (d) => this.isPrintedDirective(d),
|
|
940
|
+
typeFilter: (t) => this.isPrintedType(t),
|
|
941
|
+
fieldFilter: (f) => !isFederationField(f),
|
|
942
|
+
});
|
|
540
943
|
}
|
|
541
944
|
}
|
|
542
945
|
exports.Subgraph = Subgraph;
|
|
@@ -562,48 +965,61 @@ function addSubgraphToError(e, subgraphName, errorCode) {
|
|
|
562
965
|
source: cause.source,
|
|
563
966
|
positions: cause.positions,
|
|
564
967
|
path: cause.path,
|
|
565
|
-
originalError: cause
|
|
968
|
+
originalError: cause,
|
|
566
969
|
extensions: cause.extensions,
|
|
567
970
|
});
|
|
568
971
|
}
|
|
569
972
|
else {
|
|
570
|
-
return new graphql_1.GraphQLError(message, nodes, cause.source, cause.positions, cause.path, cause
|
|
973
|
+
return new graphql_1.GraphQLError(message, nodes, cause.source, cause.positions, cause.path, cause, cause.extensions);
|
|
571
974
|
}
|
|
572
975
|
});
|
|
573
|
-
return (0, definitions_1.ErrGraphQLValidationFailed)(updatedCauses);
|
|
976
|
+
return updatedCauses.length === 1 ? updatedCauses[0] : (0, definitions_1.ErrGraphQLValidationFailed)(updatedCauses);
|
|
574
977
|
}
|
|
575
978
|
exports.addSubgraphToError = addSubgraphToError;
|
|
576
979
|
class ExternalTester {
|
|
577
980
|
constructor(schema) {
|
|
578
981
|
this.schema = schema;
|
|
579
982
|
this.fakeExternalFields = new Set();
|
|
983
|
+
this.providedFields = new Set();
|
|
984
|
+
this.externalDirective = this.metadata().externalDirective();
|
|
580
985
|
this.collectFakeExternals();
|
|
986
|
+
this.collectProvidedFields();
|
|
987
|
+
}
|
|
988
|
+
metadata() {
|
|
989
|
+
const metadata = federationMetadata(this.schema);
|
|
990
|
+
(0, utils_1.assert)(metadata, 'Schema should be a subgraphs schema');
|
|
991
|
+
return metadata;
|
|
581
992
|
}
|
|
582
993
|
collectFakeExternals() {
|
|
583
|
-
const
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
const parent = key.parent;
|
|
589
|
-
if (!(key.ofExtension() || parent.hasAppliedDirective(exports.extendsDirectiveName))) {
|
|
994
|
+
const metadata = this.metadata();
|
|
995
|
+
const extendsDirective = metadata.extendsDirective();
|
|
996
|
+
for (const key of metadata.keyDirective().applications()) {
|
|
997
|
+
const parentType = key.parent;
|
|
998
|
+
if (!(key.ofExtension() || parentType.hasAppliedDirective(extendsDirective))) {
|
|
590
999
|
continue;
|
|
591
1000
|
}
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
1001
|
+
collectTargetFields({
|
|
1002
|
+
parentType,
|
|
1003
|
+
directive: key,
|
|
1004
|
+
includeInterfaceFieldsImplementations: false,
|
|
1005
|
+
validate: false,
|
|
1006
|
+
}).filter((field) => field.hasAppliedDirective(this.externalDirective))
|
|
1007
|
+
.forEach((field) => this.fakeExternalFields.add(field.coordinate));
|
|
1008
|
+
}
|
|
1009
|
+
}
|
|
1010
|
+
collectProvidedFields() {
|
|
1011
|
+
for (const provides of this.metadata().providesDirective().applications()) {
|
|
1012
|
+
const parent = provides.parent;
|
|
1013
|
+
collectTargetFields({
|
|
1014
|
+
parentType: (0, definitions_1.baseType)(parent.type),
|
|
1015
|
+
directive: provides,
|
|
1016
|
+
includeInterfaceFieldsImplementations: true,
|
|
1017
|
+
validate: false,
|
|
1018
|
+
}).forEach((f) => this.providedFields.add(f.coordinate));
|
|
603
1019
|
}
|
|
604
1020
|
}
|
|
605
1021
|
isExternal(field) {
|
|
606
|
-
return field.hasAppliedDirective(
|
|
1022
|
+
return field.hasAppliedDirective(this.externalDirective) && !this.isFakeExternal(field);
|
|
607
1023
|
}
|
|
608
1024
|
isFakeExternal(field) {
|
|
609
1025
|
return this.fakeExternalFields.has(field.coordinate);
|
|
@@ -621,6 +1037,102 @@ class ExternalTester {
|
|
|
621
1037
|
}
|
|
622
1038
|
return false;
|
|
623
1039
|
}
|
|
1040
|
+
isPartiallyExternal(field) {
|
|
1041
|
+
return this.isExternal(field) && this.providedFields.has(field.coordinate);
|
|
1042
|
+
}
|
|
1043
|
+
isFullyExternal(field) {
|
|
1044
|
+
return this.isExternal(field) && !this.providedFields.has(field.coordinate);
|
|
1045
|
+
}
|
|
1046
|
+
}
|
|
1047
|
+
function removeInactiveProvidesAndRequires(schema, onModified = () => { }) {
|
|
1048
|
+
const metadata = federationMetadata(schema);
|
|
1049
|
+
if (!metadata) {
|
|
1050
|
+
return;
|
|
1051
|
+
}
|
|
1052
|
+
const providesDirective = metadata.providesDirective();
|
|
1053
|
+
const requiresDirective = metadata.requiresDirective();
|
|
1054
|
+
for (const type of schema.types()) {
|
|
1055
|
+
if (!(0, definitions_1.isObjectType)(type) && !(0, definitions_1.isInterfaceType)(type)) {
|
|
1056
|
+
continue;
|
|
1057
|
+
}
|
|
1058
|
+
for (const field of type.fields()) {
|
|
1059
|
+
const fieldBaseType = (0, definitions_1.baseType)(field.type);
|
|
1060
|
+
removeInactiveApplications(providesDirective, field, fieldBaseType, onModified);
|
|
1061
|
+
removeInactiveApplications(requiresDirective, field, type, onModified);
|
|
1062
|
+
}
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
exports.removeInactiveProvidesAndRequires = removeInactiveProvidesAndRequires;
|
|
1066
|
+
function removeInactiveApplications(directiveDefinition, field, parentType, onModified) {
|
|
1067
|
+
for (const application of field.appliedDirectivesOf(directiveDefinition)) {
|
|
1068
|
+
let selection;
|
|
1069
|
+
try {
|
|
1070
|
+
selection = parseFieldSetArgument({ parentType, directive: application });
|
|
1071
|
+
}
|
|
1072
|
+
catch (e) {
|
|
1073
|
+
continue;
|
|
1074
|
+
}
|
|
1075
|
+
if (selectsNonExternalLeafField(selection)) {
|
|
1076
|
+
application.remove();
|
|
1077
|
+
const updated = withoutNonExternalLeafFields(selection);
|
|
1078
|
+
if (!updated.isEmpty()) {
|
|
1079
|
+
const updatedDirective = field.applyDirective(directiveDefinition, { fields: updated.toString(true, false) });
|
|
1080
|
+
onModified(field, application, updatedDirective);
|
|
1081
|
+
}
|
|
1082
|
+
else {
|
|
1083
|
+
onModified(field, application);
|
|
1084
|
+
}
|
|
1085
|
+
}
|
|
1086
|
+
}
|
|
1087
|
+
}
|
|
1088
|
+
function isExternalOrHasExternalImplementations(field) {
|
|
1089
|
+
const metadata = federationMetadata(field.schema());
|
|
1090
|
+
if (!metadata) {
|
|
1091
|
+
return false;
|
|
1092
|
+
}
|
|
1093
|
+
if (field.hasAppliedDirective(metadata.externalDirective())) {
|
|
1094
|
+
return true;
|
|
1095
|
+
}
|
|
1096
|
+
const parentType = field.parent;
|
|
1097
|
+
if ((0, definitions_1.isInterfaceType)(parentType)) {
|
|
1098
|
+
for (const implem of parentType.possibleRuntimeTypes()) {
|
|
1099
|
+
const fieldInImplem = implem.field(field.name);
|
|
1100
|
+
if (fieldInImplem && fieldInImplem.hasAppliedDirective(metadata.externalDirective())) {
|
|
1101
|
+
return true;
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
return false;
|
|
1106
|
+
}
|
|
1107
|
+
function selectsNonExternalLeafField(selection) {
|
|
1108
|
+
return selection.selections().some(s => {
|
|
1109
|
+
if (s.kind === 'FieldSelection') {
|
|
1110
|
+
if (isExternalOrHasExternalImplementations(s.field.definition)) {
|
|
1111
|
+
return false;
|
|
1112
|
+
}
|
|
1113
|
+
return !s.selectionSet || selectsNonExternalLeafField(s.selectionSet);
|
|
1114
|
+
}
|
|
1115
|
+
else {
|
|
1116
|
+
return selectsNonExternalLeafField(s.selectionSet);
|
|
1117
|
+
}
|
|
1118
|
+
});
|
|
1119
|
+
}
|
|
1120
|
+
function withoutNonExternalLeafFields(selectionSet) {
|
|
1121
|
+
const newSelectionSet = new operations_1.SelectionSet(selectionSet.parentType);
|
|
1122
|
+
for (const selection of selectionSet.selections()) {
|
|
1123
|
+
if (selection.kind === 'FieldSelection') {
|
|
1124
|
+
if (isExternalOrHasExternalImplementations(selection.field.definition)) {
|
|
1125
|
+
newSelectionSet.add(selection);
|
|
1126
|
+
continue;
|
|
1127
|
+
}
|
|
1128
|
+
}
|
|
1129
|
+
if (selection.selectionSet) {
|
|
1130
|
+
const updated = withoutNonExternalLeafFields(selection.selectionSet);
|
|
1131
|
+
if (!updated.isEmpty()) {
|
|
1132
|
+
newSelectionSet.add((0, operations_1.selectionOfElement)(selection.element(), updated));
|
|
1133
|
+
}
|
|
1134
|
+
}
|
|
1135
|
+
}
|
|
1136
|
+
return newSelectionSet;
|
|
624
1137
|
}
|
|
625
|
-
exports.ExternalTester = ExternalTester;
|
|
626
1138
|
//# sourceMappingURL=federation.js.map
|