@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
|
@@ -28,6 +28,7 @@ export function scalarTypeBuilder() {
|
|
|
28
28
|
});
|
|
29
29
|
scalarTypeState.byGraph.set(graph.id, {
|
|
30
30
|
inaccessible: type.inaccessible,
|
|
31
|
+
version: graph.version,
|
|
31
32
|
});
|
|
32
33
|
},
|
|
33
34
|
composeSupergraphNode(scalarType) {
|
|
@@ -58,6 +59,7 @@ function getOrCreateScalarType(state, typeName) {
|
|
|
58
59
|
return existing;
|
|
59
60
|
}
|
|
60
61
|
const def = {
|
|
62
|
+
kind: 'scalar',
|
|
61
63
|
name: typeName,
|
|
62
64
|
tags: new Set(),
|
|
63
65
|
inaccessible: false,
|
|
@@ -15,6 +15,7 @@ export function unionTypeBuilder() {
|
|
|
15
15
|
}
|
|
16
16
|
unionTypeState.byGraph.set(graph.id, {
|
|
17
17
|
members: type.members,
|
|
18
|
+
version: graph.version,
|
|
18
19
|
});
|
|
19
20
|
for (const member of type.members) {
|
|
20
21
|
unionTypeState.members.add(member);
|
|
@@ -48,6 +49,7 @@ function getOrCreateUnionType(state, typeName) {
|
|
|
48
49
|
return existing;
|
|
49
50
|
}
|
|
50
51
|
const def = {
|
|
52
|
+
kind: 'union',
|
|
51
53
|
name: typeName,
|
|
52
54
|
members: new Set(),
|
|
53
55
|
tags: new Set(),
|
package/esm/supergraph/state.js
CHANGED
|
@@ -12,7 +12,7 @@ import { scalarTypeBuilder } from './composition/scalar-type.js';
|
|
|
12
12
|
import { unionTypeBuilder } from './composition/union-type.js';
|
|
13
13
|
export function createSupergraphStateBuilder() {
|
|
14
14
|
const state = {
|
|
15
|
-
|
|
15
|
+
subgraphs: new Map(),
|
|
16
16
|
scalarTypes: new Map(),
|
|
17
17
|
objectTypes: new Map(),
|
|
18
18
|
interfaceTypes: new Map(),
|
|
@@ -36,22 +36,21 @@ export function createSupergraphStateBuilder() {
|
|
|
36
36
|
const interfaceType = interfaceTypeBuilder();
|
|
37
37
|
const objectType = objectTypeBuilder();
|
|
38
38
|
const unionType = unionTypeBuilder();
|
|
39
|
+
const subgraphStates = new Map();
|
|
40
|
+
const graphNameToIdMap = {};
|
|
39
41
|
return {
|
|
40
|
-
|
|
41
|
-
if (state.
|
|
42
|
-
throw new Error(`Graph with ID "${graph.id}" already exists`);
|
|
42
|
+
addSubgraph(subgraph) {
|
|
43
|
+
if (state.subgraphs.has(subgraph.graph.id)) {
|
|
44
|
+
throw new Error(`Graph with ID "${subgraph.graph.id}" already exists`);
|
|
43
45
|
}
|
|
44
|
-
state.
|
|
45
|
-
|
|
46
|
-
name: graph.name,
|
|
47
|
-
url: graph.url,
|
|
48
|
-
version: graph.version,
|
|
49
|
-
});
|
|
46
|
+
state.subgraphs.set(subgraph.graph.id, subgraph);
|
|
47
|
+
graphNameToIdMap[subgraph.graph.name] = subgraph.graph.id;
|
|
50
48
|
},
|
|
51
49
|
getGraph(id) {
|
|
52
|
-
return state.
|
|
50
|
+
return state.subgraphs.get(id);
|
|
53
51
|
},
|
|
54
52
|
visitSubgraphState(subgraphState) {
|
|
53
|
+
subgraphStates.set(subgraphState.graph.id, subgraphState);
|
|
55
54
|
for (const link of subgraphState.links) {
|
|
56
55
|
state.links.push(linkWithGraph(link, subgraphState.graph.id));
|
|
57
56
|
}
|
|
@@ -106,15 +105,14 @@ export function createSupergraphStateBuilder() {
|
|
|
106
105
|
getSupergraphState() {
|
|
107
106
|
return state;
|
|
108
107
|
},
|
|
108
|
+
getSubgraphState(graphId) {
|
|
109
|
+
return subgraphStates.get(graphId);
|
|
110
|
+
},
|
|
109
111
|
links() {
|
|
110
112
|
return mergeLinks(state.links);
|
|
111
113
|
},
|
|
112
114
|
build() {
|
|
113
115
|
const transformFields = createFieldsTransformer(state);
|
|
114
|
-
const graphNameToIdMap = {};
|
|
115
|
-
for (const [id, graph] of state.graphs) {
|
|
116
|
-
graphNameToIdMap[graph.name] = id;
|
|
117
|
-
}
|
|
118
116
|
const helpers = {
|
|
119
117
|
graphNameToId(graphName) {
|
|
120
118
|
return graphNameToIdMap[graphName] ?? null;
|
|
@@ -131,31 +129,31 @@ export function createSupergraphStateBuilder() {
|
|
|
131
129
|
1;
|
|
132
130
|
const nodes = new Array(numberOfExpectedNodes);
|
|
133
131
|
let i = 0;
|
|
134
|
-
nodes[i++] = createJoinGraphEnumTypeNode(Array.from(state.
|
|
135
|
-
name: graph.name,
|
|
136
|
-
enumValue: graph.id,
|
|
137
|
-
url: graph.url ?? '',
|
|
132
|
+
nodes[i++] = createJoinGraphEnumTypeNode(Array.from(state.subgraphs.values()).map(subgraph => ({
|
|
133
|
+
name: subgraph.graph.name,
|
|
134
|
+
enumValue: subgraph.graph.id,
|
|
135
|
+
url: subgraph.graph.url ?? '',
|
|
138
136
|
})));
|
|
139
137
|
for (const directiveState of state.directives.values()) {
|
|
140
|
-
nodes[i++] = directive.composeSupergraphNode(directiveState, state.
|
|
138
|
+
nodes[i++] = directive.composeSupergraphNode(directiveState, state.subgraphs, helpers);
|
|
141
139
|
}
|
|
142
140
|
for (const scalarTypeState of state.scalarTypes.values()) {
|
|
143
|
-
nodes[i++] = scalarType.composeSupergraphNode(scalarTypeState, state.
|
|
141
|
+
nodes[i++] = scalarType.composeSupergraphNode(scalarTypeState, state.subgraphs, helpers);
|
|
144
142
|
}
|
|
145
143
|
for (const objectTypeState of state.objectTypes.values()) {
|
|
146
|
-
nodes[i++] = objectType.composeSupergraphNode(transformFields(objectTypeState), state.
|
|
144
|
+
nodes[i++] = objectType.composeSupergraphNode(transformFields(objectTypeState), state.subgraphs, helpers);
|
|
147
145
|
}
|
|
148
146
|
for (const interfaceTypeState of state.interfaceTypes.values()) {
|
|
149
|
-
nodes[i++] = interfaceType.composeSupergraphNode(interfaceTypeState, state.
|
|
147
|
+
nodes[i++] = interfaceType.composeSupergraphNode(interfaceTypeState, state.subgraphs, helpers);
|
|
150
148
|
}
|
|
151
149
|
for (const unionTypeState of state.unionTypes.values()) {
|
|
152
|
-
nodes[i++] = unionType.composeSupergraphNode(unionTypeState, state.
|
|
150
|
+
nodes[i++] = unionType.composeSupergraphNode(unionTypeState, state.subgraphs, helpers);
|
|
153
151
|
}
|
|
154
152
|
for (const enumTypeState of state.enumTypes.values()) {
|
|
155
|
-
nodes[i++] = enumType.composeSupergraphNode(enumTypeState, state.
|
|
153
|
+
nodes[i++] = enumType.composeSupergraphNode(enumTypeState, state.subgraphs, helpers);
|
|
156
154
|
}
|
|
157
155
|
for (const inputObjectTypeState of state.inputObjectTypes.values()) {
|
|
158
|
-
nodes[i++] = inputObjectType.composeSupergraphNode(inputObjectTypeState, state.
|
|
156
|
+
nodes[i++] = inputObjectType.composeSupergraphNode(inputObjectTypeState, state.subgraphs, helpers);
|
|
159
157
|
}
|
|
160
158
|
return nodes;
|
|
161
159
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { GraphQLError } from 'graphql';
|
|
2
|
+
import { TypeKind } from '../../../subgraph/state.js';
|
|
2
3
|
function stripNonNull(type) {
|
|
3
4
|
return type.replace(/!$/, '');
|
|
4
5
|
}
|
|
@@ -33,7 +34,28 @@ export function FieldsOfTheSameTypeRule(context) {
|
|
|
33
34
|
return {
|
|
34
35
|
ObjectTypeField(objectTypeState, fieldState) {
|
|
35
36
|
const typeToGraphs = new Map();
|
|
37
|
+
const typeNameToPossibleTypeNames = new Map();
|
|
36
38
|
fieldState.byGraph.forEach((field, graphName) => {
|
|
39
|
+
const typeName = field.type.replaceAll('!', '').replaceAll('[', '').replaceAll(']', '');
|
|
40
|
+
const typeState = context.subgraphStates.get(graphName)?.types.get(typeName);
|
|
41
|
+
if (typeState?.kind === TypeKind.UNION) {
|
|
42
|
+
if (!typeNameToPossibleTypeNames.has(typeName)) {
|
|
43
|
+
typeNameToPossibleTypeNames.set(typeName, new Set());
|
|
44
|
+
}
|
|
45
|
+
const list = typeNameToPossibleTypeNames.get(typeName);
|
|
46
|
+
typeState.members.forEach(member => {
|
|
47
|
+
list.add(member);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
else if (typeState?.kind === TypeKind.INTERFACE) {
|
|
51
|
+
if (!typeNameToPossibleTypeNames.has(typeName)) {
|
|
52
|
+
typeNameToPossibleTypeNames.set(typeName, new Set());
|
|
53
|
+
}
|
|
54
|
+
const list = typeNameToPossibleTypeNames.get(typeName);
|
|
55
|
+
typeState.implementedBy.forEach(member => {
|
|
56
|
+
list.add(member);
|
|
57
|
+
});
|
|
58
|
+
}
|
|
37
59
|
const normalizedOutputTypes = normalizeOutputTypeStrings({
|
|
38
60
|
superType: fieldState.type,
|
|
39
61
|
localType: field.type,
|
|
@@ -50,6 +72,19 @@ export function FieldsOfTheSameTypeRule(context) {
|
|
|
50
72
|
}
|
|
51
73
|
});
|
|
52
74
|
if (typeToGraphs.size > 1) {
|
|
75
|
+
if (typeNameToPossibleTypeNames.size === 1) {
|
|
76
|
+
const possibleTypeNames = [];
|
|
77
|
+
typeNameToPossibleTypeNames.forEach((list, unionOrInterfaceName) => {
|
|
78
|
+
possibleTypeNames.push(unionOrInterfaceName);
|
|
79
|
+
for (const typeName of list) {
|
|
80
|
+
possibleTypeNames.push(typeName);
|
|
81
|
+
}
|
|
82
|
+
});
|
|
83
|
+
const outputTypeNames = Array.from(typeToGraphs.keys()).map(t => t.replaceAll('!', '').replaceAll('[', '').replaceAll(']', ''));
|
|
84
|
+
if (outputTypeNames.every(t => possibleTypeNames.includes(t))) {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
53
88
|
const groups = Array.from(typeToGraphs.entries()).map(([outputType, graphs]) => {
|
|
54
89
|
const plural = graphs.length > 1 ? 's' : '';
|
|
55
90
|
return `type "${outputType}" in subgraph${plural} "${graphs
|
|
@@ -33,7 +33,10 @@ export function InvalidFieldSharingRule(context) {
|
|
|
33
33
|
continue;
|
|
34
34
|
}
|
|
35
35
|
}
|
|
36
|
-
|
|
36
|
+
const fedV1FieldInExtension = field.version === 'v1.0' && field.extension;
|
|
37
|
+
if (fieldIsShareable ||
|
|
38
|
+
fieldIsUsedAsKey ||
|
|
39
|
+
(objectTypeState.isEntity && fedV1FieldInExtension)) {
|
|
37
40
|
resolvableIn.push(graphId);
|
|
38
41
|
continue;
|
|
39
42
|
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export const SUPERGRAPH_ID = Symbol('__supergraph__');
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { lazy } from './helpers';
|
|
2
|
+
import { AbstractMove, EntityMove, FieldMove } from './moves';
|
|
3
|
+
export function isEntityEdge(edge) {
|
|
4
|
+
return edge.move instanceof EntityMove;
|
|
5
|
+
}
|
|
6
|
+
export function assertEntityEdge(edge) {
|
|
7
|
+
if (!isEntityEdge(edge)) {
|
|
8
|
+
throw new Error(`Expected edge to be Edge<EntityMove>, but got ${edge}`);
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function isAbstractEdge(edge) {
|
|
12
|
+
return edge.move instanceof AbstractMove;
|
|
13
|
+
}
|
|
14
|
+
export function assertAbstractEdge(edge) {
|
|
15
|
+
if (!isAbstractEdge(edge)) {
|
|
16
|
+
throw new Error(`Expected edge to be Edge<AbstractMove>, but got ${edge}`);
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
export function isFieldEdge(edge) {
|
|
20
|
+
return edge.move instanceof FieldMove;
|
|
21
|
+
}
|
|
22
|
+
export function assertFieldEdge(edge) {
|
|
23
|
+
if (!isFieldEdge(edge)) {
|
|
24
|
+
throw new Error(`Expected edge to be Edge<FieldMove>, but got ${edge}`);
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
export class Edge {
|
|
28
|
+
head;
|
|
29
|
+
move;
|
|
30
|
+
tail;
|
|
31
|
+
resolvable = [];
|
|
32
|
+
_toString = lazy(() => `${this.head} -(${this.move})-> ${this.tail}`);
|
|
33
|
+
constructor(head, move, tail) {
|
|
34
|
+
this.head = head;
|
|
35
|
+
this.move = move;
|
|
36
|
+
this.tail = tail;
|
|
37
|
+
}
|
|
38
|
+
isCrossGraphEdge() {
|
|
39
|
+
return this.head.graphId !== this.tail.graphId;
|
|
40
|
+
}
|
|
41
|
+
toString() {
|
|
42
|
+
return this._toString.get();
|
|
43
|
+
}
|
|
44
|
+
getResolvability(graphNames) {
|
|
45
|
+
return this.resolvable.find(([checkedGraphNames]) => {
|
|
46
|
+
return checkedGraphNames.every(name => graphNames.includes(name));
|
|
47
|
+
})?.[1];
|
|
48
|
+
}
|
|
49
|
+
setResolvable(success, graphNames, error) {
|
|
50
|
+
const result = success ? { success, error: undefined } : { success, error: error };
|
|
51
|
+
this.resolvable.push([graphNames, result]);
|
|
52
|
+
return result;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
export class SatisfiabilityError extends Error {
|
|
2
|
+
kind;
|
|
3
|
+
sourceGraphName;
|
|
4
|
+
typeName;
|
|
5
|
+
fieldName;
|
|
6
|
+
static forKey(sourceGraphName, targetGraphName, typeName, keyFields) {
|
|
7
|
+
return new SatisfiabilityError('KEY', sourceGraphName, typeName, null, `cannot move to subgraph "${targetGraphName}" using @key(fields: "${keyFields}") of "${typeName}", the key field(s) cannot be resolved from subgraph "${sourceGraphName}".`);
|
|
8
|
+
}
|
|
9
|
+
static forRequire(sourceGraphName, typeName, fieldName) {
|
|
10
|
+
return new SatisfiabilityError('REQUIRE', sourceGraphName, typeName, fieldName, `cannot satisfy @require conditions on field "${typeName}.${fieldName}".`);
|
|
11
|
+
}
|
|
12
|
+
static forExternal(sourceGraphName, typeName, fieldName) {
|
|
13
|
+
return new SatisfiabilityError('EXTERNAL', sourceGraphName, typeName, fieldName, `field "${typeName}.${fieldName}" is not resolvable because marked @external.`);
|
|
14
|
+
}
|
|
15
|
+
static forMissingField(sourceGraphName, typeName, fieldName) {
|
|
16
|
+
return new SatisfiabilityError('MISSING_FIELD', sourceGraphName, typeName, fieldName, `cannot find field "${typeName}.${fieldName}".`);
|
|
17
|
+
}
|
|
18
|
+
static forNoKey(sourceGraphName, targetGraphName, typeName, fieldName) {
|
|
19
|
+
return new SatisfiabilityError('NO_KEY', sourceGraphName, typeName, fieldName, `cannot move to subgraph "${targetGraphName}", which has field "${typeName}.${fieldName}", because type "${typeName}" has no @key defined in subgraph "${targetGraphName}".`);
|
|
20
|
+
}
|
|
21
|
+
constructor(kind, sourceGraphName, typeName, fieldName, message) {
|
|
22
|
+
super(message);
|
|
23
|
+
this.kind = kind;
|
|
24
|
+
this.sourceGraphName = sourceGraphName;
|
|
25
|
+
this.typeName = typeName;
|
|
26
|
+
this.fieldName = fieldName;
|
|
27
|
+
}
|
|
28
|
+
isMatchingField(typeName, fieldName) {
|
|
29
|
+
if (this.typeName !== typeName) {
|
|
30
|
+
return false;
|
|
31
|
+
}
|
|
32
|
+
if (this.fieldName) {
|
|
33
|
+
return this.fieldName === fieldName;
|
|
34
|
+
}
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
toString() {
|
|
38
|
+
return this.message;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { Kind } from 'graphql';
|
|
2
|
+
import { parseFields } from '../../../../subgraph/helpers';
|
|
3
|
+
import { stripTypeModifiers } from '../../../../utils/state';
|
|
4
|
+
export class Fields {
|
|
5
|
+
typeName;
|
|
6
|
+
source;
|
|
7
|
+
fields;
|
|
8
|
+
constructor(typeName, source, fields) {
|
|
9
|
+
this.typeName = typeName;
|
|
10
|
+
this.source = source;
|
|
11
|
+
this.fields = fields;
|
|
12
|
+
}
|
|
13
|
+
contains(typeName, fieldName) {
|
|
14
|
+
return this._contains(typeName, fieldName, this.fields);
|
|
15
|
+
}
|
|
16
|
+
equals(other) {
|
|
17
|
+
if (this.typeName !== other.typeName) {
|
|
18
|
+
return false;
|
|
19
|
+
}
|
|
20
|
+
if (this.source === other.source) {
|
|
21
|
+
return true;
|
|
22
|
+
}
|
|
23
|
+
return this._fieldsEqual(this.fields, other.fields);
|
|
24
|
+
}
|
|
25
|
+
_fieldsEqual(fields, otherFields) {
|
|
26
|
+
if (fields.length !== otherFields.length) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
for (let i = 0; i < fields.length; i++) {
|
|
30
|
+
const field = fields[i];
|
|
31
|
+
const otherField = otherFields[i];
|
|
32
|
+
if (field.typeName !== otherField.typeName || field.fieldName !== otherField.fieldName) {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
const areEqual = Array.isArray(field.selectionSet) && Array.isArray(otherField.selectionSet)
|
|
36
|
+
? this._fieldsEqual(field.selectionSet, otherField.selectionSet)
|
|
37
|
+
: field.selectionSet === otherField.selectionSet;
|
|
38
|
+
if (!areEqual) {
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
_contains(typeName, fieldName, fields) {
|
|
45
|
+
return fields.some(f => (f.typeName === typeName && f.fieldName === fieldName) ||
|
|
46
|
+
(f.selectionSet ? this._contains(typeName, fieldName, f.selectionSet) : false));
|
|
47
|
+
}
|
|
48
|
+
toString() {
|
|
49
|
+
return this.source;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
export class FieldsResolver {
|
|
53
|
+
supergraphState;
|
|
54
|
+
cache = new Map();
|
|
55
|
+
constructor(supergraphState) {
|
|
56
|
+
this.supergraphState = supergraphState;
|
|
57
|
+
}
|
|
58
|
+
resolve(typeName, keyFields) {
|
|
59
|
+
const key = this.keyFactory(typeName, keyFields);
|
|
60
|
+
if (this.cache.has(key)) {
|
|
61
|
+
return this.cache.get(key);
|
|
62
|
+
}
|
|
63
|
+
const typeState = this.supergraphState.objectTypes.get(typeName);
|
|
64
|
+
if (!typeState) {
|
|
65
|
+
throw new Error(`Expected an object type when resolving keyFields of ${typeName}`);
|
|
66
|
+
}
|
|
67
|
+
const selectionSetNode = parseFields(keyFields);
|
|
68
|
+
if (!selectionSetNode) {
|
|
69
|
+
throw new Error(`Expected a selection set when resolving keyFields of ${typeName}`);
|
|
70
|
+
}
|
|
71
|
+
const fields = new Fields(typeName, keyFields, this.resolveSelectionSetNode(typeName, selectionSetNode));
|
|
72
|
+
this.cache.set(key, fields);
|
|
73
|
+
return fields;
|
|
74
|
+
}
|
|
75
|
+
keyFactory(typeName, keyFields) {
|
|
76
|
+
return `${typeName}/${keyFields}`;
|
|
77
|
+
}
|
|
78
|
+
resolveFieldNode(typeName, fieldNode, fields) {
|
|
79
|
+
const typeState = this.supergraphState.objectTypes.get(typeName) ??
|
|
80
|
+
this.supergraphState.interfaceTypes.get(typeName);
|
|
81
|
+
if (!typeState) {
|
|
82
|
+
throw new Error(`Type "${typeName}" is not defined.`);
|
|
83
|
+
}
|
|
84
|
+
if (fieldNode.name.value === '__typename') {
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (!typeState.fields.has(fieldNode.name.value)) {
|
|
88
|
+
throw new Error(`Type "${typeName.toString()}" does not have field "${fieldNode.name.value}".`);
|
|
89
|
+
}
|
|
90
|
+
if (fieldNode.selectionSet) {
|
|
91
|
+
const outputType = stripTypeModifiers(typeState.fields.get(fieldNode.name.value).type);
|
|
92
|
+
fields.push({
|
|
93
|
+
fieldName: fieldNode.name.value,
|
|
94
|
+
typeName,
|
|
95
|
+
selectionSet: this.resolveSelectionSetNode(outputType, fieldNode.selectionSet),
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
fields.push({
|
|
100
|
+
typeName,
|
|
101
|
+
fieldName: fieldNode.name.value,
|
|
102
|
+
selectionSet: null,
|
|
103
|
+
});
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
resolveInlineFragmentNode(fragmentNode, fields) {
|
|
107
|
+
if (!fragmentNode.typeCondition?.name.value) {
|
|
108
|
+
throw new Error(`Inline fragment without type condition is not supported.`);
|
|
109
|
+
}
|
|
110
|
+
const typeName = fragmentNode.typeCondition.name.value;
|
|
111
|
+
const typeState = this.supergraphState.objectTypes.get(typeName) ??
|
|
112
|
+
this.supergraphState.interfaceTypes.get(typeName);
|
|
113
|
+
if (!typeState) {
|
|
114
|
+
throw new Error(`Type "${typeName}" is not defined.`);
|
|
115
|
+
}
|
|
116
|
+
for (const selection of fragmentNode.selectionSet.selections) {
|
|
117
|
+
if (selection.kind === Kind.FIELD) {
|
|
118
|
+
this.resolveFieldNode(typeName, selection, fields);
|
|
119
|
+
}
|
|
120
|
+
else {
|
|
121
|
+
throw new Error(`Inline fragment within an inline fragment is not supported.`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
resolveSelectionSetNode(typeName, selectionSetNode, fields = []) {
|
|
126
|
+
for (const selection of selectionSetNode.selections) {
|
|
127
|
+
if (selection.kind === Kind.FIELD) {
|
|
128
|
+
this.resolveFieldNode(typeName, selection, fields);
|
|
129
|
+
}
|
|
130
|
+
else if (selection.kind === Kind.INLINE_FRAGMENT) {
|
|
131
|
+
this.resolveInlineFragmentNode(selection, fields);
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
throw new Error(`Fragment spread is not supported.`);
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
return this.sortFields(fields);
|
|
138
|
+
}
|
|
139
|
+
sortFields(fields) {
|
|
140
|
+
return fields.sort((a, b) => `${a.typeName}.${a.fieldName}`.localeCompare(`${b.typeName}.${b.fieldName}`));
|
|
141
|
+
}
|
|
142
|
+
}
|