@theguild/federation-composition 0.9.0 → 0.10.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/README.md +2 -1
- package/cjs/specifications/link.js +40 -5
- package/cjs/subgraph/helpers.js +2 -2
- package/cjs/subgraph/state.js +8 -0
- package/cjs/subgraph/validation/rules/elements/provides.js +8 -6
- package/cjs/subgraph/validation/rules/elements/requires.js +9 -7
- package/cjs/subgraph/validation/validate-state.js +9 -3
- package/cjs/subgraph/validation/validate-subgraph.js +4 -2
- package/cjs/subgraph/validation/validation-context.js +10 -0
- package/cjs/supergraph/composition/directive.js +3 -0
- package/cjs/supergraph/composition/enum-type.js +3 -0
- package/cjs/supergraph/composition/input-object-type.js +3 -0
- package/cjs/supergraph/composition/interface-type.js +4 -0
- package/cjs/supergraph/composition/object-type.js +18 -20
- package/cjs/supergraph/composition/scalar-type.js +2 -0
- package/cjs/supergraph/composition/union-type.js +2 -0
- package/cjs/supergraph/state.js +24 -26
- package/cjs/supergraph/validation/rules/fields-of-the-same-type-rule.js +35 -0
- package/cjs/supergraph/validation/rules/invalid-field-sharing-rule.js +4 -1
- package/cjs/supergraph/validation/rules/satisfiablity/constants.js +4 -0
- package/cjs/supergraph/validation/rules/satisfiablity/edge.js +64 -0
- package/cjs/supergraph/validation/rules/satisfiablity/errors.js +44 -0
- package/cjs/supergraph/validation/rules/satisfiablity/fields.js +147 -0
- package/cjs/supergraph/validation/rules/satisfiablity/finder.js +267 -0
- package/cjs/supergraph/validation/rules/satisfiablity/graph.js +675 -0
- package/cjs/supergraph/validation/rules/satisfiablity/helpers.js +41 -0
- package/cjs/supergraph/validation/rules/satisfiablity/move-validator.js +337 -0
- package/cjs/supergraph/validation/rules/satisfiablity/moves.js +52 -0
- package/cjs/supergraph/validation/rules/satisfiablity/node.js +89 -0
- package/cjs/supergraph/validation/rules/satisfiablity/operation-path.js +70 -0
- package/cjs/supergraph/validation/rules/satisfiablity/supergraph.js +37 -0
- package/cjs/supergraph/validation/rules/satisfiablity/walker.js +306 -0
- package/cjs/supergraph/validation/rules/satisfiablity-rule.js +45 -1081
- package/cjs/supergraph/validation/validate-supergraph.js +1 -1
- package/cjs/utils/logger.js +127 -0
- package/esm/specifications/link.js +40 -5
- package/esm/subgraph/helpers.js +2 -2
- package/esm/subgraph/state.js +8 -0
- package/esm/subgraph/validation/rules/elements/provides.js +8 -6
- package/esm/subgraph/validation/rules/elements/requires.js +9 -7
- package/esm/subgraph/validation/validate-state.js +9 -3
- package/esm/subgraph/validation/validate-subgraph.js +4 -2
- package/esm/subgraph/validation/validation-context.js +11 -1
- package/esm/supergraph/composition/directive.js +3 -0
- package/esm/supergraph/composition/enum-type.js +3 -0
- package/esm/supergraph/composition/input-object-type.js +3 -0
- package/esm/supergraph/composition/interface-type.js +4 -0
- package/esm/supergraph/composition/object-type.js +18 -20
- package/esm/supergraph/composition/scalar-type.js +2 -0
- package/esm/supergraph/composition/union-type.js +2 -0
- package/esm/supergraph/state.js +24 -26
- package/esm/supergraph/validation/rules/fields-of-the-same-type-rule.js +35 -0
- package/esm/supergraph/validation/rules/invalid-field-sharing-rule.js +4 -1
- package/esm/supergraph/validation/rules/satisfiablity/constants.js +1 -0
- package/esm/supergraph/validation/rules/satisfiablity/edge.js +54 -0
- package/esm/supergraph/validation/rules/satisfiablity/errors.js +40 -0
- package/esm/supergraph/validation/rules/satisfiablity/fields.js +142 -0
- package/esm/supergraph/validation/rules/satisfiablity/finder.js +261 -0
- package/esm/supergraph/validation/rules/satisfiablity/graph.js +671 -0
- package/esm/supergraph/validation/rules/satisfiablity/helpers.js +35 -0
- package/esm/supergraph/validation/rules/satisfiablity/move-validator.js +333 -0
- package/esm/supergraph/validation/rules/satisfiablity/moves.js +46 -0
- package/esm/supergraph/validation/rules/satisfiablity/node.js +85 -0
- package/esm/supergraph/validation/rules/satisfiablity/operation-path.js +66 -0
- package/esm/supergraph/validation/rules/satisfiablity/supergraph.js +33 -0
- package/esm/supergraph/validation/rules/satisfiablity/walker.js +301 -0
- package/esm/supergraph/validation/rules/satisfiablity-rule.js +40 -1076
- package/esm/supergraph/validation/validate-supergraph.js +1 -1
- package/esm/utils/logger.js +119 -0
- package/package.json +2 -1
- package/typings/subgraph/state.d.cts +2 -0
- package/typings/subgraph/state.d.ts +2 -0
- package/typings/subgraph/validation/validate-state.d.cts +2 -1
- package/typings/subgraph/validation/validate-state.d.ts +2 -1
- package/typings/subgraph/validation/validation-context.d.cts +3 -0
- package/typings/subgraph/validation/validation-context.d.ts +3 -0
- package/typings/supergraph/composition/common.d.cts +2 -1
- package/typings/supergraph/composition/common.d.ts +2 -1
- package/typings/supergraph/composition/directive.d.cts +4 -0
- package/typings/supergraph/composition/directive.d.ts +4 -0
- package/typings/supergraph/composition/enum-type.d.cts +4 -0
- package/typings/supergraph/composition/enum-type.d.ts +4 -0
- package/typings/supergraph/composition/input-object-type.d.cts +4 -0
- package/typings/supergraph/composition/input-object-type.d.ts +4 -0
- package/typings/supergraph/composition/interface-type.d.cts +5 -0
- package/typings/supergraph/composition/interface-type.d.ts +5 -0
- package/typings/supergraph/composition/object-type.d.cts +5 -0
- package/typings/supergraph/composition/object-type.d.ts +5 -0
- package/typings/supergraph/composition/scalar-type.d.cts +3 -0
- package/typings/supergraph/composition/scalar-type.d.ts +3 -0
- package/typings/supergraph/composition/union-type.d.cts +3 -0
- package/typings/supergraph/composition/union-type.d.ts +3 -0
- package/typings/supergraph/state.d.cts +4 -4
- package/typings/supergraph/state.d.ts +4 -4
- package/typings/supergraph/validation/rules/satisfiablity/constants.d.cts +2 -0
- package/typings/supergraph/validation/rules/satisfiablity/constants.d.ts +2 -0
- package/typings/supergraph/validation/rules/satisfiablity/edge.d.cts +31 -0
- package/typings/supergraph/validation/rules/satisfiablity/edge.d.ts +31 -0
- package/typings/supergraph/validation/rules/satisfiablity/errors.d.cts +17 -0
- package/typings/supergraph/validation/rules/satisfiablity/errors.d.ts +17 -0
- package/typings/supergraph/validation/rules/satisfiablity/fields.d.cts +33 -0
- package/typings/supergraph/validation/rules/satisfiablity/fields.d.ts +33 -0
- package/typings/supergraph/validation/rules/satisfiablity/finder.d.cts +28 -0
- package/typings/supergraph/validation/rules/satisfiablity/finder.d.ts +28 -0
- package/typings/supergraph/validation/rules/satisfiablity/graph.d.cts +63 -0
- package/typings/supergraph/validation/rules/satisfiablity/graph.d.ts +63 -0
- package/typings/supergraph/validation/rules/satisfiablity/helpers.d.cts +7 -0
- package/typings/supergraph/validation/rules/satisfiablity/helpers.d.ts +7 -0
- package/typings/supergraph/validation/rules/satisfiablity/move-validator.d.cts +25 -0
- package/typings/supergraph/validation/rules/satisfiablity/move-validator.d.ts +25 -0
- package/typings/supergraph/validation/rules/satisfiablity/moves.d.cts +24 -0
- package/typings/supergraph/validation/rules/satisfiablity/moves.d.ts +24 -0
- package/typings/supergraph/validation/rules/satisfiablity/node.d.cts +31 -0
- package/typings/supergraph/validation/rules/satisfiablity/node.d.ts +31 -0
- package/typings/supergraph/validation/rules/satisfiablity/operation-path.d.cts +29 -0
- package/typings/supergraph/validation/rules/satisfiablity/operation-path.d.ts +29 -0
- package/typings/supergraph/validation/rules/satisfiablity/supergraph.d.cts +14 -0
- package/typings/supergraph/validation/rules/satisfiablity/supergraph.d.ts +14 -0
- package/typings/supergraph/validation/rules/satisfiablity/walker.d.cts +35 -0
- package/typings/supergraph/validation/rules/satisfiablity/walker.d.ts +35 -0
- package/typings/utils/logger.d.cts +33 -0
- package/typings/utils/logger.d.ts +33 -0
- package/cjs/utils/dependency-graph.js +0 -227
- package/esm/utils/dependency-graph.js +0 -222
- package/typings/utils/dependency-graph.d.cts +0 -31
- package/typings/utils/dependency-graph.d.ts +0 -31
|
@@ -30,7 +30,7 @@ const validation_context_js_1 = require("./validation-context.js");
|
|
|
30
30
|
function validateSupergraph(subgraphStates, state, __internal) {
|
|
31
31
|
const context = (0, validation_context_js_1.createSupergraphValidationContext)(subgraphStates);
|
|
32
32
|
for (const subgraphState of subgraphStates.values()) {
|
|
33
|
-
state.
|
|
33
|
+
state.addSubgraph(subgraphState);
|
|
34
34
|
}
|
|
35
35
|
const preSupergraphRules = [required_query_rule_js_1.RequiredQueryRule, types_of_the_same_kind_rule_js_1.TypesOfTheSameKindRule];
|
|
36
36
|
const rulesToSkip = __internal?.disableValidationRules ?? [];
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Logger = exports.LoggerContext = void 0;
|
|
7
|
+
const debug_1 = __importDefault(require("debug"));
|
|
8
|
+
const originalFormatArgs = debug_1.default.formatArgs;
|
|
9
|
+
debug_1.default.formatArgs = function (args) {
|
|
10
|
+
originalFormatArgs.call(this, args);
|
|
11
|
+
const line = args[0];
|
|
12
|
+
const noColorsPrefix = ` ${this.namespace} `;
|
|
13
|
+
const colorsPrefix = `${this.namespace}`;
|
|
14
|
+
const noColorsStartsAt = line.indexOf(noColorsPrefix);
|
|
15
|
+
if (noColorsStartsAt > -1) {
|
|
16
|
+
args[0] = line.slice(noColorsStartsAt + noColorsPrefix.length);
|
|
17
|
+
}
|
|
18
|
+
else {
|
|
19
|
+
const colorsStartsAt = line.indexOf(colorsPrefix);
|
|
20
|
+
args[0] = line.slice(colorsStartsAt + colorsPrefix.length);
|
|
21
|
+
}
|
|
22
|
+
};
|
|
23
|
+
class LoggerContext {
|
|
24
|
+
indent = {
|
|
25
|
+
level: 0,
|
|
26
|
+
str: '',
|
|
27
|
+
times: [],
|
|
28
|
+
};
|
|
29
|
+
maxIdLength = 0;
|
|
30
|
+
idUpdateFns = [];
|
|
31
|
+
logger = (0, debug_1.default)('composition');
|
|
32
|
+
firstLoggerAt = 0;
|
|
33
|
+
down(time) {
|
|
34
|
+
this.updateIndent(+1, time);
|
|
35
|
+
}
|
|
36
|
+
up(time) {
|
|
37
|
+
if (this.indent.level > 0) {
|
|
38
|
+
this.updateIndent(-1, time);
|
|
39
|
+
return this.indent.times.pop();
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
getTime() {
|
|
43
|
+
if (!this.firstLoggerAt) {
|
|
44
|
+
this.firstLoggerAt = Date.now();
|
|
45
|
+
return 0;
|
|
46
|
+
}
|
|
47
|
+
return Date.now() - this.firstLoggerAt;
|
|
48
|
+
}
|
|
49
|
+
getIndent() {
|
|
50
|
+
return this.indent;
|
|
51
|
+
}
|
|
52
|
+
register(id, idUpdateFn) {
|
|
53
|
+
idUpdateFn(this.maxIdLength);
|
|
54
|
+
this.idUpdateFns.push(idUpdateFn);
|
|
55
|
+
const newMaxIdLength = Math.max(this.maxIdLength, id.length);
|
|
56
|
+
if (newMaxIdLength > this.maxIdLength) {
|
|
57
|
+
this.maxIdLength = newMaxIdLength;
|
|
58
|
+
this.idUpdateFns.forEach(fn => fn(newMaxIdLength));
|
|
59
|
+
}
|
|
60
|
+
return this.logger.extend(id);
|
|
61
|
+
}
|
|
62
|
+
updateIndent(delta, time) {
|
|
63
|
+
if (this.indent.level + delta < 0) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
if (delta > 0) {
|
|
67
|
+
this.indent.times.push(time);
|
|
68
|
+
}
|
|
69
|
+
this.indent.level += delta;
|
|
70
|
+
this.indent.str = '│ '.repeat(this.indent.level);
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
exports.LoggerContext = LoggerContext;
|
|
74
|
+
class Logger {
|
|
75
|
+
id;
|
|
76
|
+
context;
|
|
77
|
+
isEnabled;
|
|
78
|
+
debug;
|
|
79
|
+
idPrefix;
|
|
80
|
+
constructor(id, context) {
|
|
81
|
+
this.id = id;
|
|
82
|
+
this.context = context;
|
|
83
|
+
this.id = id;
|
|
84
|
+
this.context = context;
|
|
85
|
+
this.idPrefix = `${id}`;
|
|
86
|
+
this.debug = this.context.register(this.id, this._updateIdPrefix.bind(this));
|
|
87
|
+
this.isEnabled = this.debug.enabled;
|
|
88
|
+
this.debug.log = console.log;
|
|
89
|
+
}
|
|
90
|
+
log(msg, prefix = '- ') {
|
|
91
|
+
if (this.isEnabled) {
|
|
92
|
+
this._log(prefix, msg);
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
group(msg) {
|
|
96
|
+
if (this.isEnabled) {
|
|
97
|
+
this.log(msg, '┌ ');
|
|
98
|
+
this.context.down(Date.now());
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
groupEnd(msg) {
|
|
102
|
+
if (this.isEnabled) {
|
|
103
|
+
const time = this.context.up(Date.now());
|
|
104
|
+
let message = msg ? (typeof msg === 'string' ? msg : msg()) : '';
|
|
105
|
+
if (time) {
|
|
106
|
+
message += ` (${Date.now() - time}ms)`;
|
|
107
|
+
}
|
|
108
|
+
this.log(message, '└ ');
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
create(id) {
|
|
112
|
+
return new Logger(id, this.context);
|
|
113
|
+
}
|
|
114
|
+
_log(prefix, msg) {
|
|
115
|
+
const indent = this.context.getIndent().str;
|
|
116
|
+
const message = typeof msg === 'string' ? msg : msg();
|
|
117
|
+
if (this.isEnabled) {
|
|
118
|
+
const sinceStart = this.context.getTime();
|
|
119
|
+
const text = this.idPrefix + ' ' + indent + prefix + message + ` +${sinceStart}ms`;
|
|
120
|
+
this.debug(text);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
_updateIdPrefix(maxLength) {
|
|
124
|
+
this.idPrefix = this.id.padEnd(maxLength, ' ');
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
exports.Logger = Logger;
|
|
@@ -28,10 +28,41 @@ export function printLink(link) {
|
|
|
28
28
|
},
|
|
29
29
|
value: {
|
|
30
30
|
kind: Kind.LIST,
|
|
31
|
-
values: link.imports.map(im =>
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
values: link.imports.map(im => {
|
|
32
|
+
if (!im.alias) {
|
|
33
|
+
return {
|
|
34
|
+
kind: Kind.STRING,
|
|
35
|
+
value: im.name,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
return {
|
|
39
|
+
kind: Kind.OBJECT,
|
|
40
|
+
fields: [
|
|
41
|
+
{
|
|
42
|
+
kind: Kind.OBJECT_FIELD,
|
|
43
|
+
name: {
|
|
44
|
+
kind: Kind.NAME,
|
|
45
|
+
value: 'name',
|
|
46
|
+
},
|
|
47
|
+
value: {
|
|
48
|
+
kind: Kind.STRING,
|
|
49
|
+
value: im.name,
|
|
50
|
+
},
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
kind: Kind.OBJECT_FIELD,
|
|
54
|
+
name: {
|
|
55
|
+
kind: Kind.NAME,
|
|
56
|
+
value: 'as',
|
|
57
|
+
},
|
|
58
|
+
value: {
|
|
59
|
+
kind: Kind.STRING,
|
|
60
|
+
value: im.alias,
|
|
61
|
+
},
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
};
|
|
65
|
+
}),
|
|
35
66
|
},
|
|
36
67
|
},
|
|
37
68
|
],
|
|
@@ -153,7 +184,11 @@ export function mergeLinks(links) {
|
|
|
153
184
|
return Array.from(groupByIdentity.entries()).map(([identity, link]) => ({
|
|
154
185
|
identity,
|
|
155
186
|
version: link.highestVersion,
|
|
156
|
-
imports: Array.from(link.imports).map(link => ({
|
|
187
|
+
imports: Array.from(link.imports).map(link => ({
|
|
188
|
+
kind: link.kind,
|
|
189
|
+
name: link.name,
|
|
190
|
+
alias: link.alias,
|
|
191
|
+
})),
|
|
157
192
|
name: link.name,
|
|
158
193
|
}));
|
|
159
194
|
}
|
package/esm/subgraph/helpers.js
CHANGED
|
@@ -34,10 +34,10 @@ export function validateDirectiveAgainstOriginal(providedDirectiveNode, directiv
|
|
|
34
34
|
if (expectedType !== providedType) {
|
|
35
35
|
const isNonNullableString = providedType === 'String!';
|
|
36
36
|
const allowedFieldSetTypes = isFederationV2
|
|
37
|
-
? ['FieldSet!', 'federation__FieldSet!', '_FieldSet!']
|
|
37
|
+
? ['FieldSet!', 'federation__FieldSet!', '_FieldSet!', '[String!]!']
|
|
38
38
|
: ['_FieldSet!', 'String', 'String!'];
|
|
39
39
|
const fieldSetTypesInSpec = isFederationV2
|
|
40
|
-
? ['FieldSet!', 'federation__FieldSet!', '_FieldSet!']
|
|
40
|
+
? ['FieldSet!', 'federation__FieldSet!', '_FieldSet!', '[String!]!']
|
|
41
41
|
: ['_FieldSet!', 'FieldSet!', 'String'];
|
|
42
42
|
const expectsFieldSet = fieldSetTypesInSpec.includes(expectedType);
|
|
43
43
|
if (!isNonNullableString && expectsFieldSet) {
|
package/esm/subgraph/state.js
CHANGED
|
@@ -101,6 +101,9 @@ export function createSubgraphStateBuilder(graph, typeDefs, version, links) {
|
|
|
101
101
|
throw new Error(`Expected to find an object type`);
|
|
102
102
|
}
|
|
103
103
|
objectTypeBuilder.field.setType(typeDef.name.value, node.name.value, printOutputType(node.type));
|
|
104
|
+
if (typeDef.kind === Kind.OBJECT_TYPE_EXTENSION) {
|
|
105
|
+
objectTypeBuilder.field.setExtension(typeDef.name.value, node.name.value);
|
|
106
|
+
}
|
|
104
107
|
if (version === 'v1.0' &&
|
|
105
108
|
(typeDef.kind === Kind.OBJECT_TYPE_DEFINITION ||
|
|
106
109
|
typeDef.name.value === expectedQueryTypeName ||
|
|
@@ -590,6 +593,9 @@ function objectTypeFactory(state, renameObject) {
|
|
|
590
593
|
setType(typeName, fieldName, fieldType) {
|
|
591
594
|
getOrCreateObjectField(state, renameObject, typeName, fieldName).type = fieldType;
|
|
592
595
|
},
|
|
596
|
+
setExtension(typeName, fieldName) {
|
|
597
|
+
getOrCreateObjectField(state, renameObject, typeName, fieldName).extension = true;
|
|
598
|
+
},
|
|
593
599
|
setDirective(typeName, fieldName, directive) {
|
|
594
600
|
getOrCreateObjectField(state, renameObject, typeName, fieldName).ast.directives.push(directive);
|
|
595
601
|
},
|
|
@@ -1102,6 +1108,7 @@ function getOrCreateObjectField(state, renameObject, typeName, fieldName) {
|
|
|
1102
1108
|
external: false,
|
|
1103
1109
|
inaccessible: false,
|
|
1104
1110
|
authenticated: false,
|
|
1111
|
+
extension: false,
|
|
1105
1112
|
policies: [],
|
|
1106
1113
|
scopes: [],
|
|
1107
1114
|
used: false,
|
|
@@ -1141,6 +1148,7 @@ function getOrCreateInterfaceField(state, typeName, fieldName) {
|
|
|
1141
1148
|
required: false,
|
|
1142
1149
|
provided: false,
|
|
1143
1150
|
shareable: false,
|
|
1151
|
+
extension: false,
|
|
1144
1152
|
tags: new Set(),
|
|
1145
1153
|
args: new Map(),
|
|
1146
1154
|
ast: {
|
|
@@ -103,12 +103,14 @@ export function ProvidesRules(context) {
|
|
|
103
103
|
context.reportError(new GraphQLError(`On field "${fieldCoordinate}", for @provides(fields: ${printedFieldsValue}): field ${info.typeDefinition.name.value}.${info.fieldName} cannot be included because it has arguments (fields with argument are not allowed in @provides)`, { nodes: directiveNode, extensions: { code: 'PROVIDES_FIELDS_HAS_ARGS' } }));
|
|
104
104
|
},
|
|
105
105
|
interceptNonExternalField(info) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
if (context.satisfiesVersionRange('> v1.0')) {
|
|
107
|
+
isValid = false;
|
|
108
|
+
context.reportError(new GraphQLError(`On field "${fieldCoordinate}", for @provides(fields: ${printedFieldsValue}): field "${info.typeDefinition.name.value}.${info.fieldName}" should not be part of a @provides since it is already provided by this subgraph (it is not marked @external)`, {
|
|
109
|
+
extensions: {
|
|
110
|
+
code: 'PROVIDES_FIELDS_MISSING_EXTERNAL',
|
|
111
|
+
},
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
112
114
|
},
|
|
113
115
|
interceptExternalField(info) {
|
|
114
116
|
const keyDirectives = info.typeDefinition.directives?.filter(directive => context.isAvailableFederationDirective('key', directive));
|
|
@@ -30,7 +30,7 @@ export function RequiresRules(context) {
|
|
|
30
30
|
return;
|
|
31
31
|
}
|
|
32
32
|
const printedFieldsValue = print(fieldsArg.value);
|
|
33
|
-
if (fieldsArg.value.kind !== Kind.STRING) {
|
|
33
|
+
if (fieldsArg.value.kind !== Kind.STRING && fieldsArg.value.kind !== Kind.ENUM) {
|
|
34
34
|
context.reportError(new GraphQLError(`On field "${fieldCoordinate}", for @requires(fields: ${printedFieldsValue}): Invalid value for argument "fields": must be a string.`, {
|
|
35
35
|
nodes: directiveNode,
|
|
36
36
|
extensions: {
|
|
@@ -103,12 +103,14 @@ export function RequiresRules(context) {
|
|
|
103
103
|
}
|
|
104
104
|
},
|
|
105
105
|
interceptNonExternalField(info) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
106
|
+
if (context.satisfiesVersionRange('> v1.0')) {
|
|
107
|
+
isValid = false;
|
|
108
|
+
context.reportError(new GraphQLError(`On field "${fieldCoordinate}", for @requires(fields: ${printedFieldsValue}): field "${info.typeDefinition.name.value}.${info.fieldName}" should not be part of a @requires since it is already provided by this subgraph (it is not marked @external)`, {
|
|
109
|
+
extensions: {
|
|
110
|
+
code: 'REQUIRES_FIELDS_MISSING_EXTERNAL',
|
|
111
|
+
},
|
|
112
|
+
}));
|
|
113
|
+
}
|
|
112
114
|
},
|
|
113
115
|
});
|
|
114
116
|
if (isValid) {
|
|
@@ -3,7 +3,7 @@ import { andList } from '../../utils/format.js';
|
|
|
3
3
|
import { isList, isNonNull, stripList, stripNonNull, stripTypeModifiers, } from '../../utils/state.js';
|
|
4
4
|
import { TypeKind, } from '../state.js';
|
|
5
5
|
const specifiedScalars = new Set(specifiedScalarTypes.map(t => t.name));
|
|
6
|
-
export function validateSubgraphState(state) {
|
|
6
|
+
export function validateSubgraphState(state, context) {
|
|
7
7
|
const errors = [];
|
|
8
8
|
function reportError(message) {
|
|
9
9
|
errors.push(new GraphQLError(message, {
|
|
@@ -13,7 +13,7 @@ export function validateSubgraphState(state) {
|
|
|
13
13
|
}));
|
|
14
14
|
}
|
|
15
15
|
validateRootTypes(state, reportError);
|
|
16
|
-
validateDirectives(state, reportError);
|
|
16
|
+
validateDirectives(state, reportError, context);
|
|
17
17
|
validateTypes(state, reportError);
|
|
18
18
|
return errors;
|
|
19
19
|
}
|
|
@@ -54,13 +54,19 @@ function validateRootTypes(state, reportError) {
|
|
|
54
54
|
function capitalize(str) {
|
|
55
55
|
return str.charAt(0).toUpperCase() + str.slice(1).toLowerCase();
|
|
56
56
|
}
|
|
57
|
-
function validateDirectives(state, reportError) {
|
|
57
|
+
function validateDirectives(state, reportError, context) {
|
|
58
58
|
for (const directive of state.types.values()) {
|
|
59
59
|
if (isDirective(directive)) {
|
|
60
|
+
if (context.isLinkSpecDirective(directive.name)) {
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
60
63
|
validateName(reportError, directive.name);
|
|
61
64
|
for (const [argName, arg] of directive.args) {
|
|
62
65
|
validateName(reportError, argName);
|
|
63
66
|
const argInputTypeName = stripTypeModifiers(arg.type);
|
|
67
|
+
if (context.isLinkSpecType(argInputTypeName)) {
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
64
70
|
if (!isInputType(state, argInputTypeName)) {
|
|
65
71
|
reportError(`The type of @${directive.name}(${arg.name}:) must be Input Type ` +
|
|
66
72
|
`but got: ${arg.type}.`);
|
|
@@ -157,7 +157,7 @@ export function validateSubgraph(subgraph, stateBuilder, federation, __internal)
|
|
|
157
157
|
: null,
|
|
158
158
|
subgraph.typeDefs,
|
|
159
159
|
].filter(onlyDocumentNode));
|
|
160
|
-
const subgraphStateErrors = validateSubgraphState(stateBuilder.state);
|
|
160
|
+
const subgraphStateErrors = validateSubgraphState(stateBuilder.state, validationContext);
|
|
161
161
|
const simpleValidationContext = createSimpleValidationContext(fullTypeDefs, typeNodeInfo);
|
|
162
162
|
visit(fullTypeDefs, visitInParallel(graphqlRules.map(rule => {
|
|
163
163
|
if (rulesToSkip.includes(rule.name)) {
|
|
@@ -316,7 +316,9 @@ function cleanSubgraphTypeDefsFromSubgraphSpec(typeDefs) {
|
|
|
316
316
|
const queryTypeName = schemaDef?.operationTypes?.find(op => op.operation === OperationTypeNode.QUERY)?.type.name
|
|
317
317
|
.value ?? 'Query';
|
|
318
318
|
typeDefs.definitions = typeDefs.definitions.filter(def => {
|
|
319
|
-
if (def.kind === Kind.SCALAR_TYPE_DEFINITION
|
|
319
|
+
if ((def.kind === Kind.SCALAR_TYPE_DEFINITION ||
|
|
320
|
+
def.kind === Kind.INPUT_OBJECT_TYPE_DEFINITION) &&
|
|
321
|
+
def.name.value === '_Any') {
|
|
320
322
|
return false;
|
|
321
323
|
}
|
|
322
324
|
if (def.kind === Kind.UNION_TYPE_DEFINITION && def.name.value === '_Entity') {
|
|
@@ -1,7 +1,11 @@
|
|
|
1
|
-
import { Kind, specifiedScalarTypes, } from 'graphql';
|
|
1
|
+
import { isTypeDefinitionNode, Kind, parse, specifiedScalarTypes, } from 'graphql';
|
|
2
2
|
import { createSpecSchema } from '../../specifications/federation.js';
|
|
3
|
+
import { sdl as linkSpecSdl } from '../../specifications/link.js';
|
|
3
4
|
import { stripTypeModifiers } from '../../utils/state.js';
|
|
4
5
|
import { TypeKind } from '../state.js';
|
|
6
|
+
const linkSpec = parse(linkSpecSdl);
|
|
7
|
+
const linkSpecDirectives = linkSpec.definitions.filter((def) => def.kind === Kind.DIRECTIVE_DEFINITION);
|
|
8
|
+
const linkSpecTypes = linkSpec.definitions.filter(isTypeDefinitionNode);
|
|
5
9
|
export function createSimpleValidationContext(typeDefs, typeNodeInfo) {
|
|
6
10
|
let reportedErrors = [];
|
|
7
11
|
const directiveDefinitionMap = new Map();
|
|
@@ -151,6 +155,12 @@ export function createSubgraphValidationContext(subgraph, federation, typeNodeIn
|
|
|
151
155
|
}
|
|
152
156
|
return {
|
|
153
157
|
stateBuilder,
|
|
158
|
+
isLinkSpecDirective(name) {
|
|
159
|
+
return linkSpecDirectives.some(d => d.name.value === name);
|
|
160
|
+
},
|
|
161
|
+
isLinkSpecType(name) {
|
|
162
|
+
return linkSpecTypes.some(t => t.name.value === name);
|
|
163
|
+
},
|
|
154
164
|
isAvailableFederationType(name) {
|
|
155
165
|
const alternativeNames = typeAlternativeNamesMap.get(name);
|
|
156
166
|
if (alternativeNames) {
|
|
@@ -28,11 +28,13 @@ export function directiveBuilder() {
|
|
|
28
28
|
argState.byGraph.set(graph.id, {
|
|
29
29
|
type: arg.type,
|
|
30
30
|
defaultValue: arg.defaultValue,
|
|
31
|
+
version: graph.version,
|
|
31
32
|
});
|
|
32
33
|
}
|
|
33
34
|
directiveState.byGraph.set(graph.id, {
|
|
34
35
|
locations: directive.locations,
|
|
35
36
|
repeatable: directive.repeatable,
|
|
37
|
+
version: graph.version,
|
|
36
38
|
});
|
|
37
39
|
},
|
|
38
40
|
composeSupergraphNode(directive) {
|
|
@@ -60,6 +62,7 @@ function getOrCreateDirective(state, directiveName) {
|
|
|
60
62
|
return existing;
|
|
61
63
|
}
|
|
62
64
|
const def = {
|
|
65
|
+
kind: 'directive',
|
|
63
66
|
name: directiveName,
|
|
64
67
|
locations: new Set(),
|
|
65
68
|
byGraph: new Map(),
|
|
@@ -36,6 +36,7 @@ export function enumTypeBuilder() {
|
|
|
36
36
|
}
|
|
37
37
|
enumTypeState.byGraph.set(graph.id, {
|
|
38
38
|
inaccessible: type.inaccessible,
|
|
39
|
+
version: graph.version,
|
|
39
40
|
});
|
|
40
41
|
for (const value of type.values.values()) {
|
|
41
42
|
const valueState = getOrCreateEnumValue(enumTypeState, value.name);
|
|
@@ -51,6 +52,7 @@ export function enumTypeBuilder() {
|
|
|
51
52
|
}
|
|
52
53
|
valueState.byGraph.set(graph.id, {
|
|
53
54
|
inaccessible: value.inaccessible,
|
|
55
|
+
version: graph.version,
|
|
54
56
|
});
|
|
55
57
|
}
|
|
56
58
|
},
|
|
@@ -110,6 +112,7 @@ function getOrCreateEnumType(state, typeName) {
|
|
|
110
112
|
return existing;
|
|
111
113
|
}
|
|
112
114
|
const def = {
|
|
115
|
+
kind: 'enum',
|
|
113
116
|
name: typeName,
|
|
114
117
|
values: new Map(),
|
|
115
118
|
tags: new Set(),
|
|
@@ -16,6 +16,7 @@ export function inputObjectTypeBuilder() {
|
|
|
16
16
|
}
|
|
17
17
|
inputObjectTypeState.byGraph.set(graph.id, {
|
|
18
18
|
inaccessible: type.inaccessible,
|
|
19
|
+
version: graph.version,
|
|
19
20
|
});
|
|
20
21
|
for (const field of type.fields.values()) {
|
|
21
22
|
const fieldState = getOrCreateField(inputObjectTypeState, field.name, field.type);
|
|
@@ -39,6 +40,7 @@ export function inputObjectTypeBuilder() {
|
|
|
39
40
|
type: field.type,
|
|
40
41
|
inaccessible: field.inaccessible,
|
|
41
42
|
defaultValue: field.defaultValue,
|
|
43
|
+
version: graph.version,
|
|
42
44
|
});
|
|
43
45
|
}
|
|
44
46
|
},
|
|
@@ -91,6 +93,7 @@ function getOrCreateInputObjectType(state, typeName) {
|
|
|
91
93
|
return existing;
|
|
92
94
|
}
|
|
93
95
|
const def = {
|
|
96
|
+
kind: 'input',
|
|
94
97
|
name: typeName,
|
|
95
98
|
tags: new Set(),
|
|
96
99
|
hasDefinition: false,
|
|
@@ -33,6 +33,7 @@ export function interfaceTypeBuilder() {
|
|
|
33
33
|
keys: type.keys,
|
|
34
34
|
interfaces: type.interfaces,
|
|
35
35
|
implementedBy: type.implementedBy,
|
|
36
|
+
version: graph.version,
|
|
36
37
|
});
|
|
37
38
|
for (const field of type.fields.values()) {
|
|
38
39
|
const fieldState = getOrCreateInterfaceField(interfaceTypeState, field.name, field.type);
|
|
@@ -66,6 +67,7 @@ export function interfaceTypeBuilder() {
|
|
|
66
67
|
override: field.override,
|
|
67
68
|
provides: field.provides,
|
|
68
69
|
requires: field.requires,
|
|
70
|
+
version: graph.version,
|
|
69
71
|
});
|
|
70
72
|
for (const arg of field.args.values()) {
|
|
71
73
|
const argState = getOrCreateArg(fieldState, arg.name, arg.type);
|
|
@@ -88,6 +90,7 @@ export function interfaceTypeBuilder() {
|
|
|
88
90
|
argState.byGraph.set(graph.id, {
|
|
89
91
|
type: arg.type,
|
|
90
92
|
defaultValue: arg.defaultValue,
|
|
93
|
+
version: graph.version,
|
|
91
94
|
});
|
|
92
95
|
}
|
|
93
96
|
}
|
|
@@ -193,6 +196,7 @@ function getOrCreateInterfaceType(state, typeName) {
|
|
|
193
196
|
return existing;
|
|
194
197
|
}
|
|
195
198
|
const def = {
|
|
199
|
+
kind: 'interface',
|
|
196
200
|
name: typeName,
|
|
197
201
|
tags: new Set(),
|
|
198
202
|
inaccessible: false,
|
|
@@ -59,6 +59,7 @@ export function objectTypeBuilder() {
|
|
|
59
59
|
inaccessible: type.inaccessible,
|
|
60
60
|
shareable: type.shareable,
|
|
61
61
|
interfaces: type.interfaces,
|
|
62
|
+
version: graph.version,
|
|
62
63
|
});
|
|
63
64
|
const typeInGraph = objectTypeState.byGraph.get(graph.id);
|
|
64
65
|
for (const field of type.fields.values()) {
|
|
@@ -116,8 +117,10 @@ export function objectTypeBuilder() {
|
|
|
116
117
|
provided: field.provided,
|
|
117
118
|
required: field.required,
|
|
118
119
|
shareable: field.shareable,
|
|
120
|
+
extension: field.extension,
|
|
119
121
|
used: field.used,
|
|
120
122
|
usedAsKey,
|
|
123
|
+
version: graph.version,
|
|
121
124
|
});
|
|
122
125
|
for (const arg of field.args.values()) {
|
|
123
126
|
const argState = getOrCreateArg(fieldState, arg.name, arg.type);
|
|
@@ -144,6 +147,7 @@ export function objectTypeBuilder() {
|
|
|
144
147
|
type: arg.type,
|
|
145
148
|
inaccessible: arg.inaccessible,
|
|
146
149
|
defaultValue: arg.defaultValue,
|
|
150
|
+
version: graph.version,
|
|
147
151
|
});
|
|
148
152
|
}
|
|
149
153
|
}
|
|
@@ -153,7 +157,7 @@ export function objectTypeBuilder() {
|
|
|
153
157
|
const joinTypes = isQuery
|
|
154
158
|
?
|
|
155
159
|
Array.from(graphs.values()).map(graph => ({
|
|
156
|
-
graph: graph.id,
|
|
160
|
+
graph: graph.graph.id,
|
|
157
161
|
}))
|
|
158
162
|
:
|
|
159
163
|
Array.from(objectType.byGraph.entries())
|
|
@@ -216,12 +220,12 @@ export function objectTypeBuilder() {
|
|
|
216
220
|
}
|
|
217
221
|
return true;
|
|
218
222
|
}
|
|
219
|
-
function createJoinFields(fieldInGraphs, field, { hasDifferentOutputType,
|
|
223
|
+
function createJoinFields(fieldInGraphs, field, { hasDifferentOutputType, }) {
|
|
220
224
|
return fieldInGraphs
|
|
221
225
|
.map(([graphId, meta]) => {
|
|
222
226
|
const type = hasDifferentOutputType ? meta.type : undefined;
|
|
223
227
|
const override = meta.override ?? undefined;
|
|
224
|
-
const usedOverridden = provideUsedOverriddenValue(field
|
|
228
|
+
const usedOverridden = provideUsedOverriddenValue(field, meta, fieldNamesOfImplementedInterfaces, graphId, graphNameToId);
|
|
225
229
|
const external = shouldSetExternalOnJoinField(meta, graphId, field);
|
|
226
230
|
const provides = meta.provides ?? undefined;
|
|
227
231
|
const requires = meta.requires ?? undefined;
|
|
@@ -257,7 +261,6 @@ export function objectTypeBuilder() {
|
|
|
257
261
|
const hasDifferentOutputType = fieldInGraphs.some(([_, meta]) => meta.type !== field.type);
|
|
258
262
|
const isDefinedEverywhere = field.byGraph.size === (isQuery ? graphs.size : objectType.byGraph.size);
|
|
259
263
|
let joinFields = [];
|
|
260
|
-
const overridesMap = {};
|
|
261
264
|
const differencesBetweenGraphs = {
|
|
262
265
|
override: false,
|
|
263
266
|
type: false,
|
|
@@ -273,10 +276,6 @@ export function objectTypeBuilder() {
|
|
|
273
276
|
}
|
|
274
277
|
if (meta.override !== null) {
|
|
275
278
|
differencesBetweenGraphs.override = true;
|
|
276
|
-
const originalGraphId = graphNameToId(meta.override);
|
|
277
|
-
if (originalGraphId) {
|
|
278
|
-
overridesMap[originalGraphId] = graphId;
|
|
279
|
-
}
|
|
280
279
|
}
|
|
281
280
|
if (meta.provides !== null) {
|
|
282
281
|
differencesBetweenGraphs.provides = true;
|
|
@@ -295,7 +294,7 @@ export function objectTypeBuilder() {
|
|
|
295
294
|
!fieldInGraph.usedAsKey &&
|
|
296
295
|
!fieldInGraph.required &&
|
|
297
296
|
!fieldInGraph.provided &&
|
|
298
|
-
!provideUsedOverriddenValue(field
|
|
297
|
+
!provideUsedOverriddenValue(field, fieldInGraph, fieldNamesOfImplementedInterfaces, graphId, graphNameToId) &&
|
|
299
298
|
graphs.get(graphId).version === 'v1.0') {
|
|
300
299
|
return null;
|
|
301
300
|
}
|
|
@@ -311,7 +310,7 @@ export function objectTypeBuilder() {
|
|
|
311
310
|
joinFields = graphsWithOverride.map(([graphId, meta]) => ({
|
|
312
311
|
graph: graphId,
|
|
313
312
|
override: meta.override ?? undefined,
|
|
314
|
-
usedOverridden: provideUsedOverriddenValue(field
|
|
313
|
+
usedOverridden: provideUsedOverriddenValue(field, meta, fieldNamesOfImplementedInterfaces, graphId, graphNameToId),
|
|
315
314
|
type: differencesBetweenGraphs.type ? meta.type : undefined,
|
|
316
315
|
external: meta.external ?? undefined,
|
|
317
316
|
provides: meta.provides ?? undefined,
|
|
@@ -339,7 +338,7 @@ export function objectTypeBuilder() {
|
|
|
339
338
|
const graphsToEmit = fieldInGraphs.filter(([graphId, f]) => {
|
|
340
339
|
const isExternal = f.external === true;
|
|
341
340
|
const isOverridden = overriddenGraphs.includes(graphId);
|
|
342
|
-
const needsToPrintUsedOverridden = provideUsedOverriddenValue(field
|
|
341
|
+
const needsToPrintUsedOverridden = provideUsedOverriddenValue(field, f, fieldNamesOfImplementedInterfaces, graphId, graphNameToId);
|
|
343
342
|
const isRequired = f.required === true;
|
|
344
343
|
return (isExternal && isRequired) || needsToPrintUsedOverridden || !isOverridden;
|
|
345
344
|
});
|
|
@@ -349,7 +348,7 @@ export function objectTypeBuilder() {
|
|
|
349
348
|
joinFields = graphsToEmit.map(([graphId, meta]) => ({
|
|
350
349
|
graph: graphId,
|
|
351
350
|
override: meta.override ?? undefined,
|
|
352
|
-
usedOverridden: provideUsedOverriddenValue(field
|
|
351
|
+
usedOverridden: provideUsedOverriddenValue(field, meta, fieldNamesOfImplementedInterfaces, graphId, graphNameToId),
|
|
353
352
|
type: differencesBetweenGraphs.type ? meta.type : undefined,
|
|
354
353
|
external: meta.external ?? undefined,
|
|
355
354
|
provides: meta.provides ?? undefined,
|
|
@@ -360,7 +359,6 @@ export function objectTypeBuilder() {
|
|
|
360
359
|
else if (hasDifferencesBetweenGraphs) {
|
|
361
360
|
joinFields = createJoinFields(fieldInGraphs, field, {
|
|
362
361
|
hasDifferentOutputType,
|
|
363
|
-
overridesMap,
|
|
364
362
|
});
|
|
365
363
|
}
|
|
366
364
|
}
|
|
@@ -372,11 +370,11 @@ export function objectTypeBuilder() {
|
|
|
372
370
|
const graphsToPrintJoinField = fieldInGraphs.filter(([graphId, meta]) => meta.override !== null ||
|
|
373
371
|
meta.external === true ||
|
|
374
372
|
(meta.shareable && !overriddenGraphs.includes(graphId)) ||
|
|
375
|
-
provideUsedOverriddenValue(field
|
|
373
|
+
provideUsedOverriddenValue(field, meta, fieldNamesOfImplementedInterfaces, graphId, graphNameToId));
|
|
376
374
|
joinFields = graphsToPrintJoinField.map(([graphId, meta]) => ({
|
|
377
375
|
graph: graphId,
|
|
378
376
|
override: meta.override ?? undefined,
|
|
379
|
-
usedOverridden: provideUsedOverriddenValue(field
|
|
377
|
+
usedOverridden: provideUsedOverriddenValue(field, meta, fieldNamesOfImplementedInterfaces, graphId, graphNameToId),
|
|
380
378
|
type: differencesBetweenGraphs.type ? meta.type : undefined,
|
|
381
379
|
external: meta.external ?? undefined,
|
|
382
380
|
provides: meta.provides ?? undefined,
|
|
@@ -386,7 +384,6 @@ export function objectTypeBuilder() {
|
|
|
386
384
|
else {
|
|
387
385
|
joinFields = createJoinFields(fieldInGraphs, field, {
|
|
388
386
|
hasDifferentOutputType,
|
|
389
|
-
overridesMap,
|
|
390
387
|
});
|
|
391
388
|
}
|
|
392
389
|
}
|
|
@@ -465,12 +462,12 @@ export function objectTypeBuilder() {
|
|
|
465
462
|
},
|
|
466
463
|
};
|
|
467
464
|
}
|
|
468
|
-
function provideUsedOverriddenValue(
|
|
469
|
-
const inGraphs = fieldNamesOfImplementedInterfaces[
|
|
465
|
+
function provideUsedOverriddenValue(field, fieldStateInGraph, fieldNamesOfImplementedInterfaces, graphId, graphNameToId) {
|
|
466
|
+
const inGraphs = fieldNamesOfImplementedInterfaces[field.name];
|
|
470
467
|
const hasMatchingInterfaceFieldInGraph = inGraphs && inGraphs.has(graphId);
|
|
471
468
|
const isUsedAsNonExternalKey = fieldStateInGraph.usedAsKey && !fieldStateInGraph.external;
|
|
472
|
-
const
|
|
473
|
-
if (
|
|
469
|
+
const isOverridden = field.override && graphNameToId(field.override) === graphId;
|
|
470
|
+
if (isOverridden && (isUsedAsNonExternalKey || hasMatchingInterfaceFieldInGraph)) {
|
|
474
471
|
return true;
|
|
475
472
|
}
|
|
476
473
|
return false;
|
|
@@ -481,6 +478,7 @@ function getOrCreateObjectType(state, typeName) {
|
|
|
481
478
|
return existing;
|
|
482
479
|
}
|
|
483
480
|
const def = {
|
|
481
|
+
kind: 'object',
|
|
484
482
|
name: typeName,
|
|
485
483
|
tags: new Set(),
|
|
486
484
|
hasDefinition: false,
|