@theguild/federation-composition 0.20.2 → 0.21.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/cjs/subgraph/state.js +21 -32
- package/cjs/subgraph/validation/rules/elements/authenticated.js +23 -2
- package/cjs/subgraph/validation/rules/elements/cost.js +12 -2
- package/cjs/subgraph/validation/rules/elements/list-size.js +10 -8
- package/cjs/subgraph/validation/rules/elements/policy.js +25 -5
- package/cjs/subgraph/validation/rules/elements/requires-scopes.js +23 -2
- package/cjs/supergraph/composition/ast.js +5 -14
- package/cjs/supergraph/composition/enum-type.js +3 -2
- package/cjs/supergraph/composition/interface-type.js +32 -11
- package/cjs/supergraph/composition/object-type.js +46 -7
- package/cjs/supergraph/composition/scalar-type.js +3 -2
- package/cjs/supergraph/state.js +43 -6
- package/cjs/supergraph/validation/rules/auth-on-requires-rule.js +211 -0
- package/cjs/supergraph/validation/rules/external-missing-on-base-rule.js +26 -1
- package/cjs/supergraph/validation/rules/list-size-slicing-arguments-rule.js +69 -0
- package/cjs/supergraph/validation/validate-supergraph.js +5 -0
- package/cjs/utils/auth.js +34 -0
- package/esm/subgraph/state.js +21 -32
- package/esm/subgraph/validation/rules/elements/authenticated.js +24 -3
- package/esm/subgraph/validation/rules/elements/cost.js +12 -2
- package/esm/subgraph/validation/rules/elements/list-size.js +10 -8
- package/esm/subgraph/validation/rules/elements/policy.js +26 -6
- package/esm/subgraph/validation/rules/elements/requires-scopes.js +24 -3
- package/esm/supergraph/composition/ast.js +5 -14
- package/esm/supergraph/composition/enum-type.js +3 -2
- package/esm/supergraph/composition/interface-type.js +33 -12
- package/esm/supergraph/composition/object-type.js +46 -7
- package/esm/supergraph/composition/scalar-type.js +3 -2
- package/esm/supergraph/state.js +43 -6
- package/esm/supergraph/validation/rules/auth-on-requires-rule.js +208 -0
- package/esm/supergraph/validation/rules/external-missing-on-base-rule.js +26 -1
- package/esm/supergraph/validation/rules/list-size-slicing-arguments-rule.js +66 -0
- package/esm/supergraph/validation/validate-supergraph.js +5 -0
- package/esm/utils/auth.js +31 -0
- package/package.json +1 -1
- package/typings/subgraph/state.d.cts +1 -6
- package/typings/subgraph/state.d.ts +1 -6
- package/typings/subgraph/validation/validation-context.d.cts +0 -3
- package/typings/subgraph/validation/validation-context.d.ts +0 -3
- package/typings/supergraph/composition/ast.d.cts +1 -0
- package/typings/supergraph/composition/ast.d.ts +1 -0
- package/typings/supergraph/composition/common.d.cts +4 -0
- package/typings/supergraph/composition/common.d.ts +4 -0
- package/typings/supergraph/composition/interface-type.d.cts +3 -0
- package/typings/supergraph/composition/interface-type.d.ts +3 -0
- package/typings/supergraph/composition/object-type.d.cts +6 -0
- package/typings/supergraph/composition/object-type.d.ts +6 -0
- package/typings/supergraph/state.d.cts +1 -0
- package/typings/supergraph/state.d.ts +1 -0
- package/typings/supergraph/validation/rules/auth-on-requires-rule.d.cts +5 -0
- package/typings/supergraph/validation/rules/auth-on-requires-rule.d.ts +5 -0
- package/typings/supergraph/validation/rules/external-missing-on-base-rule.d.cts +2 -1
- package/typings/supergraph/validation/rules/external-missing-on-base-rule.d.ts +2 -1
- package/typings/supergraph/validation/rules/list-size-slicing-arguments-rule.d.cts +5 -0
- package/typings/supergraph/validation/rules/list-size-slicing-arguments-rule.d.ts +5 -0
- package/typings/utils/auth.d.cts +2 -0
- package/typings/utils/auth.d.ts +2 -0
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
import { GraphQLError, Kind, specifiedScalarTypes, } from "graphql";
|
|
2
|
+
import { parseFields } from "../../../subgraph/helpers.js";
|
|
3
|
+
import { mergeScopePolicies } from "../../../utils/auth.js";
|
|
4
|
+
export function AuthOnRequiresRule(context, supergraph) {
|
|
5
|
+
return {
|
|
6
|
+
ObjectTypeField(objectTypeState, fieldState) {
|
|
7
|
+
for (const [graphId, fieldInGraph] of fieldState.byGraph) {
|
|
8
|
+
if (!fieldInGraph.requires) {
|
|
9
|
+
continue;
|
|
10
|
+
}
|
|
11
|
+
const selectionSet = parseFields(fieldInGraph.requires);
|
|
12
|
+
if (!selectionSet) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const provisionedAccess = new ProvisionedAccess(objectTypeState, fieldState);
|
|
16
|
+
let fieldLackingAccess = ensureAccessToSelectionSet(supergraph, objectTypeState, selectionSet, provisionedAccess);
|
|
17
|
+
if (fieldLackingAccess) {
|
|
18
|
+
context.reportError(createAccessRequirementError(context.graphIdToName(graphId), `${objectTypeState.name}.${fieldState.name}`, fieldLackingAccess));
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function ensureAccessToSelectionSet(supergraph, currentType, selectionSet, provisionedAccess) {
|
|
26
|
+
for (const selection of selectionSet.selections) {
|
|
27
|
+
switch (selection.kind) {
|
|
28
|
+
case Kind.FIELD: {
|
|
29
|
+
if (currentType.kind === "union") {
|
|
30
|
+
throw new Error("Cannot select fields directly on union types.");
|
|
31
|
+
}
|
|
32
|
+
const fieldLackingAccess = ensureAccessToField(supergraph, currentType, selection, provisionedAccess);
|
|
33
|
+
if (fieldLackingAccess) {
|
|
34
|
+
return fieldLackingAccess;
|
|
35
|
+
}
|
|
36
|
+
break;
|
|
37
|
+
}
|
|
38
|
+
case Kind.INLINE_FRAGMENT: {
|
|
39
|
+
const fieldLackingAccess = ensureAccessToInlineFragment(supergraph, currentType, selection, provisionedAccess);
|
|
40
|
+
if (fieldLackingAccess) {
|
|
41
|
+
return fieldLackingAccess;
|
|
42
|
+
}
|
|
43
|
+
break;
|
|
44
|
+
}
|
|
45
|
+
case Kind.FRAGMENT_SPREAD: {
|
|
46
|
+
throw new Error("Fragment spreads are not supported in @requires.");
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
function ensureAccessToField(supergraph, currentType, fieldNode, provisionedAccess) {
|
|
52
|
+
if (fieldNode.name.value === "__typename") {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
let fieldName = fieldNode.name.value;
|
|
56
|
+
let fieldState = currentType.fields.get(fieldNode.name.value);
|
|
57
|
+
let fieldDetails = null;
|
|
58
|
+
if (fieldState) {
|
|
59
|
+
fieldDetails = {
|
|
60
|
+
type: fieldState.type,
|
|
61
|
+
scopes: fieldState.scopes,
|
|
62
|
+
policies: fieldState.policies,
|
|
63
|
+
authenticated: fieldState.authenticated,
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
if (currentType.kind === "interface") {
|
|
68
|
+
throw new Error(`Field "${fieldNode.name.value}" not found on interface type "${currentType.name}".`);
|
|
69
|
+
}
|
|
70
|
+
for (const interfaceName of currentType.interfaces) {
|
|
71
|
+
const interfaceType = supergraph.interfaceTypes.get(interfaceName);
|
|
72
|
+
if (!interfaceType) {
|
|
73
|
+
throw new Error(`Interface "${interfaceName}" implemented by "${currentType.name}" not found in supergraph.`);
|
|
74
|
+
}
|
|
75
|
+
const interfaceFieldState = interfaceType.fields.get(fieldNode.name.value);
|
|
76
|
+
if (interfaceFieldState) {
|
|
77
|
+
if (!fieldDetails) {
|
|
78
|
+
fieldDetails = {
|
|
79
|
+
type: interfaceFieldState.type,
|
|
80
|
+
scopes: mergeScopePolicies([], interfaceFieldState.scopes),
|
|
81
|
+
policies: mergeScopePolicies([], interfaceFieldState.policies),
|
|
82
|
+
authenticated: interfaceFieldState.authenticated,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
fieldDetails = {
|
|
87
|
+
type: interfaceFieldState.type,
|
|
88
|
+
scopes: mergeScopePolicies(fieldDetails.scopes, interfaceFieldState.scopes),
|
|
89
|
+
policies: mergeScopePolicies(fieldDetails.policies, interfaceFieldState.policies),
|
|
90
|
+
authenticated: interfaceFieldState.authenticated
|
|
91
|
+
? true
|
|
92
|
+
: fieldDetails.authenticated,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
if (fieldDetails === null) {
|
|
99
|
+
throw new Error(`Field "${fieldName}" not found on type "${currentType.name}".`);
|
|
100
|
+
}
|
|
101
|
+
if (!provisionedAccess.canAccess(fieldDetails)) {
|
|
102
|
+
return `${currentType.name}.${fieldName}`;
|
|
103
|
+
}
|
|
104
|
+
const outputTypeName = extractNamedTypeName(fieldDetails.type);
|
|
105
|
+
if (!outputTypeName) {
|
|
106
|
+
throw new Error(`Unable to extract output type name from field "${currentType.name}.${fieldName}" type "${fieldDetails.type}".`);
|
|
107
|
+
}
|
|
108
|
+
if (specifiedScalarTypes.some((s) => s.name === outputTypeName)) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const outputType = supergraph.objectTypes.get(outputTypeName) ??
|
|
112
|
+
supergraph.interfaceTypes.get(outputTypeName) ??
|
|
113
|
+
supergraph.enumTypes.get(outputTypeName) ??
|
|
114
|
+
supergraph.scalarTypes.get(outputTypeName) ??
|
|
115
|
+
supergraph.unionTypes.get(outputTypeName);
|
|
116
|
+
if (!outputType) {
|
|
117
|
+
throw new Error(`Output type "${outputTypeName}" of field "${currentType.name}.${fieldName}" not found in supergraph.`);
|
|
118
|
+
}
|
|
119
|
+
if (outputType.kind !== "union" && !provisionedAccess.canAccess(outputType)) {
|
|
120
|
+
return `${currentType.name}.${fieldName}`;
|
|
121
|
+
}
|
|
122
|
+
if (!fieldNode.selectionSet) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (outputType.kind === "enum" || outputType.kind === "scalar") {
|
|
126
|
+
throw new Error(`Field "${currentType.name}.${fieldName}" of type "${outputType.name}" cannot have a selection set.`);
|
|
127
|
+
}
|
|
128
|
+
return ensureAccessToSelectionSet(supergraph, outputType, fieldNode.selectionSet, provisionedAccess);
|
|
129
|
+
}
|
|
130
|
+
function ensureAccessToInlineFragment(supergraph, currentType, inlineFragment, provisionedAccess) {
|
|
131
|
+
const concreteType = inlineFragment.typeCondition
|
|
132
|
+
? (supergraph.objectTypes.get(inlineFragment.typeCondition.name.value) ??
|
|
133
|
+
supergraph.interfaceTypes.get(inlineFragment.typeCondition.name.value))
|
|
134
|
+
: currentType;
|
|
135
|
+
if (!concreteType) {
|
|
136
|
+
throw new Error(`Type "${currentType.name}" not found in supergraph for inline fragment.`);
|
|
137
|
+
}
|
|
138
|
+
if (concreteType.kind == "union") {
|
|
139
|
+
throw new Error("Cannot have inline fragments on union types without type conditions.");
|
|
140
|
+
}
|
|
141
|
+
if (!provisionedAccess.canAccess(concreteType)) {
|
|
142
|
+
return concreteType.name;
|
|
143
|
+
}
|
|
144
|
+
return ensureAccessToSelectionSet(supergraph, concreteType, inlineFragment.selectionSet, provisionedAccess);
|
|
145
|
+
}
|
|
146
|
+
function createAccessRequirementError(graphName, fieldWithRequiresCoordinate, authCoordinate) {
|
|
147
|
+
const strDataRef = authCoordinate.includes(".")
|
|
148
|
+
? `field "${authCoordinate}"`
|
|
149
|
+
: `type "${authCoordinate}"`;
|
|
150
|
+
return new GraphQLError(`[${graphName}] Field "${fieldWithRequiresCoordinate}" does not specify necessary @authenticated, @requiresScopes and/or @policy auth requirements to access the transitive ${strDataRef} data from @requires selection set.`, {
|
|
151
|
+
extensions: {
|
|
152
|
+
code: "MISSING_TRANSITIVE_AUTH_REQUIREMENTS",
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function extractNamedTypeName(typeStr) {
|
|
157
|
+
let typeName = typeStr;
|
|
158
|
+
if (!typeName)
|
|
159
|
+
return null;
|
|
160
|
+
typeName = typeName.replace(/[![\]]/g, "");
|
|
161
|
+
return typeName || null;
|
|
162
|
+
}
|
|
163
|
+
class ProvisionedAccess {
|
|
164
|
+
scopes;
|
|
165
|
+
policies;
|
|
166
|
+
authenticated;
|
|
167
|
+
constructor(objectTypeState, fieldState) {
|
|
168
|
+
this.scopes = [];
|
|
169
|
+
this.policies = [];
|
|
170
|
+
this.authenticated = false;
|
|
171
|
+
if (objectTypeState.authenticated) {
|
|
172
|
+
this.authenticated = true;
|
|
173
|
+
}
|
|
174
|
+
if (objectTypeState.scopes.length > 0) {
|
|
175
|
+
this.scopes = objectTypeState.scopes.slice();
|
|
176
|
+
}
|
|
177
|
+
if (objectTypeState.policies.length > 0) {
|
|
178
|
+
this.policies = objectTypeState.policies.slice();
|
|
179
|
+
}
|
|
180
|
+
if (fieldState.authenticated) {
|
|
181
|
+
this.authenticated = true;
|
|
182
|
+
}
|
|
183
|
+
if (fieldState.scopes.length > 0) {
|
|
184
|
+
this.scopes = mergeScopePolicies(this.scopes, fieldState.scopes);
|
|
185
|
+
}
|
|
186
|
+
if (fieldState.policies.length > 0) {
|
|
187
|
+
this.policies = mergeScopePolicies(this.policies, fieldState.policies);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
canAccess(required) {
|
|
191
|
+
if (required.authenticated && !this.authenticated) {
|
|
192
|
+
return false;
|
|
193
|
+
}
|
|
194
|
+
for (const requiredScopeGroup of required.scopes) {
|
|
195
|
+
const satisfiedByAny = this.scopes.some((providedGroup) => requiredScopeGroup.every((scope) => providedGroup.includes(scope)));
|
|
196
|
+
if (!satisfiedByAny) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
for (const requiredPolicyGroup of required.policies) {
|
|
201
|
+
const satisfiedByAny = this.policies.some((providedGroup) => requiredPolicyGroup.every((policy) => providedGroup.includes(policy)));
|
|
202
|
+
if (!satisfiedByAny) {
|
|
203
|
+
return false;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
return true;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { GraphQLError } from "graphql";
|
|
2
2
|
import { andList } from "../../../utils/format.js";
|
|
3
|
-
export function ExternalMissingOnBaseRule(context) {
|
|
3
|
+
export function ExternalMissingOnBaseRule(context, supergraph) {
|
|
4
4
|
return {
|
|
5
5
|
ObjectType(objectTypeState) {
|
|
6
6
|
if (Array.from(objectTypeState.byGraph).every(([_, stateInGraph]) => stateInGraph.external === true)) {
|
|
@@ -29,6 +29,31 @@ export function ExternalMissingOnBaseRule(context) {
|
|
|
29
29
|
}
|
|
30
30
|
return fieldStateInGraph.external === true;
|
|
31
31
|
})) {
|
|
32
|
+
for (let interfaceName of objectState.interfaces) {
|
|
33
|
+
let interfaceState = supergraph.interfaceTypes.get(interfaceName);
|
|
34
|
+
if (!interfaceState) {
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (!interfaceState.hasInterfaceObject) {
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
for (let [graphId, interfaceStateInGraph] of interfaceState.byGraph) {
|
|
41
|
+
if (!interfaceStateInGraph.isInterfaceObject) {
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
let interfaceFieldState = interfaceState.fields.get(fieldState.name);
|
|
45
|
+
if (!interfaceFieldState) {
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
let interfaceFieldInGraph = interfaceFieldState.byGraph.get(graphId);
|
|
49
|
+
if (!interfaceFieldInGraph) {
|
|
50
|
+
continue;
|
|
51
|
+
}
|
|
52
|
+
if (interfaceFieldInGraph.external !== true) {
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
32
57
|
const subgraphs = fieldState.byGraph.size > 1 ? "subgraphs" : "subgraph";
|
|
33
58
|
context.reportError(new GraphQLError(`Field "${objectState.name}.${fieldState.name}" is marked @external on all the subgraphs in which it is listed (${subgraphs} ${andList(Array.from(fieldState.byGraph.keys()).map(context.graphIdToName), true, '"')}).`, {
|
|
34
59
|
extensions: {
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { GraphQLError } from "graphql";
|
|
2
|
+
export function ListSizeSlicingArgumentsRule(context, supergraph) {
|
|
3
|
+
return {
|
|
4
|
+
ObjectTypeField(objectState, fieldState) {
|
|
5
|
+
let error = ensureSlicingArgumentsExist(objectState.name, fieldState);
|
|
6
|
+
if (error) {
|
|
7
|
+
context.reportError(error);
|
|
8
|
+
}
|
|
9
|
+
},
|
|
10
|
+
InterfaceTypeField(interfaceState, fieldState) {
|
|
11
|
+
let error = ensureSlicingArgumentsExist(interfaceState.name, fieldState);
|
|
12
|
+
if (error) {
|
|
13
|
+
context.reportError(error);
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
if (!fieldState?.listSize ||
|
|
17
|
+
!fieldState?.listSize.slicingArguments?.length) {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
for (const implementorName of interfaceState.implementedBy) {
|
|
21
|
+
const implementorType = supergraph.objectTypes.get(implementorName);
|
|
22
|
+
if (!implementorType) {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
let implementorField = implementorType.fields.get(fieldState.name);
|
|
26
|
+
if (!implementorField) {
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (!implementorField.listSize) {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
if (!implementorField.listSize.slicingArguments?.length) {
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
fieldState.listSize.printRequireOneSlicingArgument = true;
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
}
|
|
40
|
+
function ensureSlicingArgumentsExist(typeName, fieldState) {
|
|
41
|
+
if (!fieldState.listSize?.slicingArguments?.length) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const newslicingArguments = [];
|
|
45
|
+
for (const argName of fieldState.listSize.slicingArguments) {
|
|
46
|
+
const argState = fieldState.args.get(argName);
|
|
47
|
+
if (!argState) {
|
|
48
|
+
throw new Error("Could not find the argument in the field annotated with @listSize");
|
|
49
|
+
}
|
|
50
|
+
if (argState.byGraph.size !== fieldState.byGraph.size) {
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
newslicingArguments.push(argName);
|
|
54
|
+
}
|
|
55
|
+
if (!newslicingArguments.length) {
|
|
56
|
+
return new GraphQLError([
|
|
57
|
+
`All arguments for @listSize(slicingArguments:) on field "${typeName}.${fieldState.name}" were disregarded.`,
|
|
58
|
+
`For an argument to be valid, it must be defined in every subgraph where the field exists.`,
|
|
59
|
+
].join(" "), {
|
|
60
|
+
extensions: {
|
|
61
|
+
code: "LIST_SIZE_INVALID_SLICING_ARGUMENT",
|
|
62
|
+
},
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
fieldState.listSize.slicingArguments = newslicingArguments;
|
|
66
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { visitSupergraphState } from "../composition/visitor.js";
|
|
2
|
+
import { AuthOnRequiresRule } from "./rules/auth-on-requires-rule.js";
|
|
2
3
|
import { DefaultValueUsesInaccessibleRule } from "./rules/default-value-uses-inaccessible-rule.js";
|
|
3
4
|
import { DirectiveCompositionRule } from "./rules/directive-composition-rule.js";
|
|
4
5
|
import { EnumValuesRule } from "./rules/enum-values-rule.js";
|
|
@@ -17,6 +18,7 @@ import { InterfaceObjectUsageErrorRule } from "./rules/interface-object-usage-er
|
|
|
17
18
|
import { InterfaceSubtypeRule } from "./rules/interface-subtype-rule.js";
|
|
18
19
|
import { InvalidFieldSharingRule } from "./rules/invalid-field-sharing-rule.js";
|
|
19
20
|
import { LinkImportNameMismatchRule } from "./rules/link-import-name-mismatch-rule.js";
|
|
21
|
+
import { ListSizeSlicingArgumentsRule } from "./rules/list-size-slicing-arguments-rule.js";
|
|
20
22
|
import { NoInaccessibleOnImplementedInterfaceFieldsRule } from "./rules/no-inaccessible-on-implemented-interface-fields-rule.js";
|
|
21
23
|
import { OnlyInaccessibleChildrenRule } from "./rules/only-inaccessible-children-rule.js";
|
|
22
24
|
import { OverrideSourceHasOverrideRule } from "./rules/override-source-has-override.js";
|
|
@@ -49,6 +51,7 @@ export function validateSupergraph(subgraphStates, state, __internal) {
|
|
|
49
51
|
for (const subgraphState of subgraphStates.values()) {
|
|
50
52
|
state.visitSubgraphState(subgraphState);
|
|
51
53
|
}
|
|
54
|
+
state.composeSupergraphState();
|
|
52
55
|
const postSupergraphRules = [
|
|
53
56
|
InterfaceFieldNoImplementationRule,
|
|
54
57
|
ExtensionWithBaseRule,
|
|
@@ -76,6 +79,8 @@ export function validateSupergraph(subgraphStates, state, __internal) {
|
|
|
76
79
|
RequiredArgumentOrFieldIsNotInaccessibleRule,
|
|
77
80
|
InterfaceSubtypeRule,
|
|
78
81
|
NoInaccessibleOnImplementedInterfaceFieldsRule,
|
|
82
|
+
ListSizeSlicingArgumentsRule,
|
|
83
|
+
AuthOnRequiresRule,
|
|
79
84
|
];
|
|
80
85
|
const supergraph = state.getSupergraphState();
|
|
81
86
|
visitSupergraphState(supergraph, postSupergraphRules.map((rule) => {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
export function mergeScopePolicies(policyA, policyB) {
|
|
2
|
+
if (policyA.length === 0 || policyB.length === 0) {
|
|
3
|
+
return policyA.length
|
|
4
|
+
? policyA.map((group) => [...group])
|
|
5
|
+
: policyB.map((group) => [...group]);
|
|
6
|
+
}
|
|
7
|
+
const groupMap = new Map();
|
|
8
|
+
for (const groupA of policyA) {
|
|
9
|
+
for (const groupB of policyB) {
|
|
10
|
+
const merged = Array.from(new Set([...groupA, ...groupB])).sort();
|
|
11
|
+
const key = merged.join(",");
|
|
12
|
+
groupMap.set(key, merged);
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
const candidates = Array.from(groupMap.values()).sort((a, b) => a.length - b.length);
|
|
16
|
+
const finalGroups = [];
|
|
17
|
+
for (const candidate of candidates) {
|
|
18
|
+
if (!finalGroups.some((existing) => isSubset(existing, candidate))) {
|
|
19
|
+
finalGroups.push(candidate);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
finalGroups.sort((a, b) => a.length - b.length || a.join(",").localeCompare(b.join(",")));
|
|
23
|
+
return finalGroups;
|
|
24
|
+
}
|
|
25
|
+
function isSubset(subset, superset) {
|
|
26
|
+
if (subset.length > superset.length) {
|
|
27
|
+
return false;
|
|
28
|
+
}
|
|
29
|
+
const supersetSet = new Set(superset);
|
|
30
|
+
return subset.every((item) => supersetSet.has(item));
|
|
31
|
+
}
|
package/package.json
CHANGED
|
@@ -73,9 +73,6 @@ export interface InterfaceType {
|
|
|
73
73
|
extension: boolean;
|
|
74
74
|
keys: Key[];
|
|
75
75
|
inaccessible: boolean;
|
|
76
|
-
authenticated: boolean;
|
|
77
|
-
policies: string[][];
|
|
78
|
-
scopes: string[][];
|
|
79
76
|
tags: Set<string>;
|
|
80
77
|
interfaces: Set<string>;
|
|
81
78
|
isDefinition: boolean;
|
|
@@ -211,6 +208,7 @@ export interface Deprecated {
|
|
|
211
208
|
deprecated: true;
|
|
212
209
|
}
|
|
213
210
|
export type ListSize = {
|
|
211
|
+
printRequireOneSlicingArgument: boolean;
|
|
214
212
|
assumedSize: number | null;
|
|
215
213
|
slicingArguments: string[] | null;
|
|
216
214
|
sizedFields: string[] | null;
|
|
@@ -339,9 +337,6 @@ export declare function createSubgraphStateBuilder(graph: {
|
|
|
339
337
|
setInterfaceObject(typeName: string): void;
|
|
340
338
|
setKey(typeName: string, fields: string, fieldsUsedInKey: Set<string>, resolvable: boolean): void;
|
|
341
339
|
setInaccessible(typeName: string): void;
|
|
342
|
-
setAuthenticated(typeName: string): void;
|
|
343
|
-
setPolicies(typeName: string, policies: string[][]): void;
|
|
344
|
-
setScopes(typeName: string, scopes: string[][]): void;
|
|
345
340
|
setTag(typeName: string, tag: string): void;
|
|
346
341
|
setDirective(typeName: string, directive: DirectiveNode): void;
|
|
347
342
|
setDescription(typeName: string, description: Description): void;
|
|
@@ -73,9 +73,6 @@ export interface InterfaceType {
|
|
|
73
73
|
extension: boolean;
|
|
74
74
|
keys: Key[];
|
|
75
75
|
inaccessible: boolean;
|
|
76
|
-
authenticated: boolean;
|
|
77
|
-
policies: string[][];
|
|
78
|
-
scopes: string[][];
|
|
79
76
|
tags: Set<string>;
|
|
80
77
|
interfaces: Set<string>;
|
|
81
78
|
isDefinition: boolean;
|
|
@@ -211,6 +208,7 @@ export interface Deprecated {
|
|
|
211
208
|
deprecated: true;
|
|
212
209
|
}
|
|
213
210
|
export type ListSize = {
|
|
211
|
+
printRequireOneSlicingArgument: boolean;
|
|
214
212
|
assumedSize: number | null;
|
|
215
213
|
slicingArguments: string[] | null;
|
|
216
214
|
sizedFields: string[] | null;
|
|
@@ -339,9 +337,6 @@ export declare function createSubgraphStateBuilder(graph: {
|
|
|
339
337
|
setInterfaceObject(typeName: string): void;
|
|
340
338
|
setKey(typeName: string, fields: string, fieldsUsedInKey: Set<string>, resolvable: boolean): void;
|
|
341
339
|
setInaccessible(typeName: string): void;
|
|
342
|
-
setAuthenticated(typeName: string): void;
|
|
343
|
-
setPolicies(typeName: string, policies: string[][]): void;
|
|
344
|
-
setScopes(typeName: string, scopes: string[][]): void;
|
|
345
340
|
setTag(typeName: string, tag: string): void;
|
|
346
341
|
setDirective(typeName: string, directive: DirectiveNode): void;
|
|
347
342
|
setDescription(typeName: string, description: Description): void;
|
|
@@ -105,9 +105,6 @@ export declare function createSubgraphValidationContext(subgraph: {
|
|
|
105
105
|
setInterfaceObject(typeName: string): void;
|
|
106
106
|
setKey(typeName: string, fields: string, fieldsUsedInKey: Set<string>, resolvable: boolean): void;
|
|
107
107
|
setInaccessible(typeName: string): void;
|
|
108
|
-
setAuthenticated(typeName: string): void;
|
|
109
|
-
setPolicies(typeName: string, policies: string[][]): void;
|
|
110
|
-
setScopes(typeName: string, scopes: string[][]): void;
|
|
111
108
|
setTag(typeName: string, tag: string): void;
|
|
112
109
|
setDirective(typeName: string, directive: import("graphql").DirectiveNode): void;
|
|
113
110
|
setDescription(typeName: string, description: import("../state.js").Description): void;
|
|
@@ -105,9 +105,6 @@ export declare function createSubgraphValidationContext(subgraph: {
|
|
|
105
105
|
setInterfaceObject(typeName: string): void;
|
|
106
106
|
setKey(typeName: string, fields: string, fieldsUsedInKey: Set<string>, resolvable: boolean): void;
|
|
107
107
|
setInaccessible(typeName: string): void;
|
|
108
|
-
setAuthenticated(typeName: string): void;
|
|
109
|
-
setPolicies(typeName: string, policies: string[][]): void;
|
|
110
|
-
setScopes(typeName: string, scopes: string[][]): void;
|
|
111
108
|
setTag(typeName: string, tag: string): void;
|
|
112
109
|
setDirective(typeName: string, directive: import("graphql").DirectiveNode): void;
|
|
113
110
|
setDescription(typeName: string, description: import("../state.js").Description): void;
|
|
@@ -236,6 +236,7 @@ declare function createListSizeDirectiveNode(input: {
|
|
|
236
236
|
slicingArguments: string[] | null;
|
|
237
237
|
sizedFields: string[] | null;
|
|
238
238
|
requireOneSlicingArgument: boolean;
|
|
239
|
+
printRequireOneSlicingArgument: boolean;
|
|
239
240
|
directiveName: string;
|
|
240
241
|
}): ConstDirectiveNode;
|
|
241
242
|
declare function createLinkDirectiveNode(link: {
|
|
@@ -236,6 +236,7 @@ declare function createListSizeDirectiveNode(input: {
|
|
|
236
236
|
slicingArguments: string[] | null;
|
|
237
237
|
sizedFields: string[] | null;
|
|
238
238
|
requireOneSlicingArgument: boolean;
|
|
239
|
+
printRequireOneSlicingArgument: boolean;
|
|
239
240
|
directiveName: string;
|
|
240
241
|
}): ConstDirectiveNode;
|
|
241
242
|
declare function createLinkDirectiveNode(link: {
|
|
@@ -15,6 +15,10 @@ export interface Graph {
|
|
|
15
15
|
}
|
|
16
16
|
export interface TypeBuilder<T, S> {
|
|
17
17
|
visitSubgraphState(graph: Graph, state: Map<string, S>, typeName: string, type: T): void;
|
|
18
|
+
composeSupergraphState?(type: S, graphMap: Map<string, SubgraphState>, helpers: {
|
|
19
|
+
graphNameToId(graphName: string): string | null;
|
|
20
|
+
supergraphState: SupergraphState;
|
|
21
|
+
}): void;
|
|
18
22
|
composeSupergraphNode(type: S, graphMap: Map<string, SubgraphState>, helpers: {
|
|
19
23
|
graphNameToId(graphName: string): string | null;
|
|
20
24
|
supergraphState: SupergraphState;
|
|
@@ -15,6 +15,10 @@ export interface Graph {
|
|
|
15
15
|
}
|
|
16
16
|
export interface TypeBuilder<T, S> {
|
|
17
17
|
visitSubgraphState(graph: Graph, state: Map<string, S>, typeName: string, type: T): void;
|
|
18
|
+
composeSupergraphState?(type: S, graphMap: Map<string, SubgraphState>, helpers: {
|
|
19
|
+
graphNameToId(graphName: string): string | null;
|
|
20
|
+
supergraphState: SupergraphState;
|
|
21
|
+
}): void;
|
|
18
22
|
composeSupergraphNode(type: S, graphMap: Map<string, SubgraphState>, helpers: {
|
|
19
23
|
graphNameToId(graphName: string): string | null;
|
|
20
24
|
supergraphState: SupergraphState;
|
|
@@ -68,6 +68,9 @@ export type ObjectTypeStateInGraph = {
|
|
|
68
68
|
extension: boolean;
|
|
69
69
|
extensionType?: "@extends" | "extend";
|
|
70
70
|
external: boolean;
|
|
71
|
+
authenticated: boolean;
|
|
72
|
+
policies: string[][];
|
|
73
|
+
scopes: string[][];
|
|
71
74
|
keys: Key[];
|
|
72
75
|
interfaces: Set<string>;
|
|
73
76
|
inaccessible: boolean;
|
|
@@ -86,6 +89,9 @@ type FieldStateInGraph = {
|
|
|
86
89
|
requires: string | null;
|
|
87
90
|
provided: boolean;
|
|
88
91
|
required: boolean;
|
|
92
|
+
authenticated: boolean;
|
|
93
|
+
policies: string[][];
|
|
94
|
+
scopes: string[][];
|
|
89
95
|
shareable: boolean;
|
|
90
96
|
usedAsKey: boolean;
|
|
91
97
|
extension: boolean;
|
|
@@ -68,6 +68,9 @@ export type ObjectTypeStateInGraph = {
|
|
|
68
68
|
extension: boolean;
|
|
69
69
|
extensionType?: "@extends" | "extend";
|
|
70
70
|
external: boolean;
|
|
71
|
+
authenticated: boolean;
|
|
72
|
+
policies: string[][];
|
|
73
|
+
scopes: string[][];
|
|
71
74
|
keys: Key[];
|
|
72
75
|
interfaces: Set<string>;
|
|
73
76
|
inaccessible: boolean;
|
|
@@ -86,6 +89,9 @@ type FieldStateInGraph = {
|
|
|
86
89
|
requires: string | null;
|
|
87
90
|
provided: boolean;
|
|
88
91
|
required: boolean;
|
|
92
|
+
authenticated: boolean;
|
|
93
|
+
policies: string[][];
|
|
94
|
+
scopes: string[][];
|
|
89
95
|
shareable: boolean;
|
|
90
96
|
usedAsKey: boolean;
|
|
91
97
|
extension: boolean;
|
|
@@ -39,6 +39,7 @@ export type SupergraphStateBuilder = ReturnType<typeof createSupergraphStateBuil
|
|
|
39
39
|
export declare function createSupergraphStateBuilder(): {
|
|
40
40
|
addSubgraph(subgraph: SubgraphState): void;
|
|
41
41
|
getGraph(id: string): SubgraphState | undefined;
|
|
42
|
+
composeSupergraphState(): void;
|
|
42
43
|
visitSubgraphState(subgraphState: SubgraphState): void;
|
|
43
44
|
getSupergraphState(): SupergraphState;
|
|
44
45
|
getSubgraphState(graphId: string): SubgraphState | undefined;
|
|
@@ -39,6 +39,7 @@ export type SupergraphStateBuilder = ReturnType<typeof createSupergraphStateBuil
|
|
|
39
39
|
export declare function createSupergraphStateBuilder(): {
|
|
40
40
|
addSubgraph(subgraph: SubgraphState): void;
|
|
41
41
|
getGraph(id: string): SubgraphState | undefined;
|
|
42
|
+
composeSupergraphState(): void;
|
|
42
43
|
visitSubgraphState(subgraphState: SubgraphState): void;
|
|
43
44
|
getSupergraphState(): SupergraphState;
|
|
44
45
|
getSubgraphState(graphId: string): SubgraphState | undefined;
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SupergraphVisitorMap } from "../../composition/visitor.cjs";
|
|
2
|
+
import type { SupergraphState } from "../../state.cjs";
|
|
3
|
+
import type { SupergraphValidationContext } from "../validation-context.cjs";
|
|
4
|
+
export declare function AuthOnRequiresRule(context: SupergraphValidationContext, supergraph: SupergraphState): SupergraphVisitorMap;
|
|
5
|
+
//# sourceMappingURL=auth-on-requires-rule.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SupergraphVisitorMap } from "../../composition/visitor.js";
|
|
2
|
+
import type { SupergraphState } from "../../state.js";
|
|
3
|
+
import type { SupergraphValidationContext } from "../validation-context.js";
|
|
4
|
+
export declare function AuthOnRequiresRule(context: SupergraphValidationContext, supergraph: SupergraphState): SupergraphVisitorMap;
|
|
5
|
+
//# sourceMappingURL=auth-on-requires-rule.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SupergraphVisitorMap } from "../../composition/visitor.cjs";
|
|
2
2
|
import { SupergraphValidationContext } from "../validation-context.cjs";
|
|
3
|
-
|
|
3
|
+
import { SupergraphState } from "../../state.cjs";
|
|
4
|
+
export declare function ExternalMissingOnBaseRule(context: SupergraphValidationContext, supergraph: SupergraphState): SupergraphVisitorMap;
|
|
4
5
|
//# sourceMappingURL=external-missing-on-base-rule.d.ts.map
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { SupergraphVisitorMap } from "../../composition/visitor.js";
|
|
2
2
|
import { SupergraphValidationContext } from "../validation-context.js";
|
|
3
|
-
|
|
3
|
+
import { SupergraphState } from "../../state.js";
|
|
4
|
+
export declare function ExternalMissingOnBaseRule(context: SupergraphValidationContext, supergraph: SupergraphState): SupergraphVisitorMap;
|
|
4
5
|
//# sourceMappingURL=external-missing-on-base-rule.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SupergraphVisitorMap } from "../../composition/visitor.cjs";
|
|
2
|
+
import type { SupergraphState } from "../../state.cjs";
|
|
3
|
+
import type { SupergraphValidationContext } from "../validation-context.cjs";
|
|
4
|
+
export declare function ListSizeSlicingArgumentsRule(context: SupergraphValidationContext, supergraph: SupergraphState): SupergraphVisitorMap;
|
|
5
|
+
//# sourceMappingURL=list-size-slicing-arguments-rule.d.ts.map
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { SupergraphVisitorMap } from "../../composition/visitor.js";
|
|
2
|
+
import type { SupergraphState } from "../../state.js";
|
|
3
|
+
import type { SupergraphValidationContext } from "../validation-context.js";
|
|
4
|
+
export declare function ListSizeSlicingArgumentsRule(context: SupergraphValidationContext, supergraph: SupergraphState): SupergraphVisitorMap;
|
|
5
|
+
//# sourceMappingURL=list-size-slicing-arguments-rule.d.ts.map
|