@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
|
@@ -166,29 +166,29 @@ export function ListSizeRule(context) {
|
|
|
166
166
|
const outputType = namedTypeFromTypeNode(fieldDef.type);
|
|
167
167
|
const targetType = knownObjectsAndInterfaces.get(outputType.name.value);
|
|
168
168
|
if (!targetType) {
|
|
169
|
-
context.reportError(new GraphQLError(`${coordinate} has @listSize(sizedFields:) applied, but the output type is not
|
|
169
|
+
context.reportError(new GraphQLError(`${coordinate} has @listSize(sizedFields:) applied, but the output type is not a composite type`, {
|
|
170
170
|
extensions: {
|
|
171
171
|
code: "LIST_SIZE_INVALID_SIZED_FIELD",
|
|
172
172
|
},
|
|
173
173
|
}));
|
|
174
174
|
return;
|
|
175
175
|
}
|
|
176
|
-
const
|
|
176
|
+
const nonListFields = [];
|
|
177
177
|
const nonExistingFields = [];
|
|
178
178
|
for (const sizedField of sizedFields) {
|
|
179
179
|
const targetField = targetType.fields?.find((f) => f.name.value === sizedField);
|
|
180
180
|
if (!targetField) {
|
|
181
181
|
nonExistingFields.push(sizedField);
|
|
182
182
|
}
|
|
183
|
-
else if (!
|
|
184
|
-
|
|
183
|
+
else if (!isListType(targetField.type)) {
|
|
184
|
+
nonListFields.push(sizedField);
|
|
185
185
|
}
|
|
186
186
|
}
|
|
187
|
-
if (
|
|
188
|
-
|
|
189
|
-
context.reportError(new GraphQLError(`${coordinate} references "${fieldName}" field in @listSize(sizedFields:) argument that is not
|
|
187
|
+
if (nonListFields.length || nonExistingFields.length) {
|
|
188
|
+
nonListFields.forEach((fieldName) => {
|
|
189
|
+
context.reportError(new GraphQLError(`${coordinate} references "${fieldName}" field in @listSize(sizedFields:) argument that is not a list.`, {
|
|
190
190
|
extensions: {
|
|
191
|
-
code: "
|
|
191
|
+
code: "LIST_SIZE_APPLIED_TO_NON_LIST",
|
|
192
192
|
},
|
|
193
193
|
}));
|
|
194
194
|
});
|
|
@@ -235,6 +235,7 @@ export function ListSizeRule(context) {
|
|
|
235
235
|
if (typeDef.kind === Kind.OBJECT_TYPE_DEFINITION ||
|
|
236
236
|
typeDef.kind === Kind.OBJECT_TYPE_EXTENSION) {
|
|
237
237
|
context.stateBuilder.objectType.field.setListSize(typeDef.name.value, parent.name.value, {
|
|
238
|
+
printRequireOneSlicingArgument: false,
|
|
238
239
|
assumedSize,
|
|
239
240
|
slicingArguments,
|
|
240
241
|
sizedFields,
|
|
@@ -244,6 +245,7 @@ export function ListSizeRule(context) {
|
|
|
244
245
|
if (typeDef.kind === Kind.INTERFACE_TYPE_DEFINITION ||
|
|
245
246
|
typeDef.kind === Kind.INTERFACE_TYPE_EXTENSION) {
|
|
246
247
|
context.stateBuilder.interfaceType.field.setListSize(typeDef.name.value, parent.name.value, {
|
|
248
|
+
printRequireOneSlicingArgument: false,
|
|
247
249
|
assumedSize,
|
|
248
250
|
slicingArguments,
|
|
249
251
|
sizedFields,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Kind } from "graphql";
|
|
1
|
+
import { GraphQLError, Kind } from "graphql";
|
|
2
2
|
import { print } from "../../../../graphql/printer.js";
|
|
3
3
|
import { validateDirectiveAgainstOriginal } from "../../../helpers.js";
|
|
4
4
|
export function PolicyRule(context) {
|
|
@@ -52,21 +52,41 @@ export function PolicyRule(context) {
|
|
|
52
52
|
if (!typeDef) {
|
|
53
53
|
throw new Error("Could not find the parent type of the field annotated with @policy");
|
|
54
54
|
}
|
|
55
|
-
if (
|
|
56
|
-
typeDef.kind === Kind.
|
|
57
|
-
|
|
55
|
+
if (typeDef.kind === Kind.INTERFACE_TYPE_DEFINITION ||
|
|
56
|
+
typeDef.kind === Kind.INTERFACE_TYPE_EXTENSION) {
|
|
57
|
+
context.reportError(new GraphQLError(`Invalid use of @policy on field "${typeDef.name.value}.${parent.name.value}": @policy cannot be applied on interfaces, interface fields and interface objects`, {
|
|
58
|
+
extensions: {
|
|
59
|
+
code: "AUTH_REQUIREMENTS_APPLIED_ON_INTERFACE",
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
if (typeDef.kind === Kind.OBJECT_TYPE_DEFINITION ||
|
|
65
|
+
typeDef.kind === Kind.OBJECT_TYPE_EXTENSION) {
|
|
58
66
|
context.stateBuilder.objectType.field.setPolicies(typeDef.name.value, parent.name.value, policies);
|
|
59
67
|
}
|
|
60
68
|
break;
|
|
61
69
|
}
|
|
62
70
|
case Kind.OBJECT_TYPE_DEFINITION:
|
|
63
71
|
case Kind.OBJECT_TYPE_EXTENSION:
|
|
72
|
+
if (context.stateBuilder.isInterfaceObject(parent.name.value)) {
|
|
73
|
+
context.reportError(new GraphQLError(`Invalid use of @policy on interface object "${parent.name.value}": @policy cannot be applied on interfaces, interface fields and interface objects`, {
|
|
74
|
+
extensions: {
|
|
75
|
+
code: "AUTH_REQUIREMENTS_APPLIED_ON_INTERFACE",
|
|
76
|
+
},
|
|
77
|
+
}));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
64
80
|
context.stateBuilder.objectType.setPolicies(parent.name.value, policies);
|
|
65
81
|
break;
|
|
66
82
|
case Kind.INTERFACE_TYPE_DEFINITION:
|
|
67
83
|
case Kind.INTERFACE_TYPE_DEFINITION:
|
|
68
|
-
context.
|
|
69
|
-
|
|
84
|
+
context.reportError(new GraphQLError(`Invalid use of @policy on interface "${parent.name.value}": @policy cannot be applied on interfaces, interface fields and interface objects`, {
|
|
85
|
+
extensions: {
|
|
86
|
+
code: "AUTH_REQUIREMENTS_APPLIED_ON_INTERFACE",
|
|
87
|
+
},
|
|
88
|
+
}));
|
|
89
|
+
return;
|
|
70
90
|
case Kind.SCALAR_TYPE_DEFINITION:
|
|
71
91
|
case Kind.SCALAR_TYPE_EXTENSION:
|
|
72
92
|
context.stateBuilder.scalarType.setPolicies(parent.name.value, policies);
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Kind } from "graphql";
|
|
1
|
+
import { GraphQLError, Kind } from "graphql";
|
|
2
2
|
import { print } from "../../../../graphql/printer.js";
|
|
3
3
|
import { validateDirectiveAgainstOriginal } from "../../../helpers.js";
|
|
4
4
|
export function RequiresScopesRule(context) {
|
|
@@ -52,6 +52,15 @@ export function RequiresScopesRule(context) {
|
|
|
52
52
|
if (!typeDef) {
|
|
53
53
|
throw new Error("Could not find the parent type of the field annotated with @requiresScopes");
|
|
54
54
|
}
|
|
55
|
+
if (typeDef.kind === Kind.INTERFACE_TYPE_DEFINITION ||
|
|
56
|
+
typeDef.kind === Kind.INTERFACE_TYPE_EXTENSION) {
|
|
57
|
+
context.reportError(new GraphQLError(`Invalid use of @requiresScopes on field "${typeDef.name.value}.${parent.name.value}": @requiresScopes cannot be applied on interfaces, interface fields and interface objects`, {
|
|
58
|
+
extensions: {
|
|
59
|
+
code: "AUTH_REQUIREMENTS_APPLIED_ON_INTERFACE",
|
|
60
|
+
},
|
|
61
|
+
}));
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
55
64
|
if (typeDef.kind === Kind.OBJECT_TYPE_DEFINITION ||
|
|
56
65
|
typeDef.kind === Kind.OBJECT_TYPE_EXTENSION) {
|
|
57
66
|
context.stateBuilder.objectType.field.setScopes(typeDef.name.value, parent.name.value, scopes);
|
|
@@ -60,12 +69,24 @@ export function RequiresScopesRule(context) {
|
|
|
60
69
|
}
|
|
61
70
|
case Kind.OBJECT_TYPE_DEFINITION:
|
|
62
71
|
case Kind.OBJECT_TYPE_EXTENSION:
|
|
72
|
+
if (context.stateBuilder.isInterfaceObject(parent.name.value)) {
|
|
73
|
+
context.reportError(new GraphQLError(`Invalid use of @requiresScopes on interface object "${parent.name.value}": @requiresScopes cannot be applied on interfaces, interface fields and interface objects`, {
|
|
74
|
+
extensions: {
|
|
75
|
+
code: "AUTH_REQUIREMENTS_APPLIED_ON_INTERFACE",
|
|
76
|
+
},
|
|
77
|
+
}));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
63
80
|
context.stateBuilder.objectType.setScopes(parent.name.value, scopes);
|
|
64
81
|
break;
|
|
65
82
|
case Kind.INTERFACE_TYPE_DEFINITION:
|
|
66
83
|
case Kind.INTERFACE_TYPE_DEFINITION:
|
|
67
|
-
context.
|
|
68
|
-
|
|
84
|
+
context.reportError(new GraphQLError(`Invalid use of @requiresScopes on interface "${parent.name.value}": @requiresScopes cannot be applied on interfaces, interface fields and interface objects`, {
|
|
85
|
+
extensions: {
|
|
86
|
+
code: "AUTH_REQUIREMENTS_APPLIED_ON_INTERFACE",
|
|
87
|
+
},
|
|
88
|
+
}));
|
|
89
|
+
return;
|
|
69
90
|
case Kind.SCALAR_TYPE_DEFINITION:
|
|
70
91
|
case Kind.SCALAR_TYPE_EXTENSION:
|
|
71
92
|
context.stateBuilder.scalarType.setScopes(parent.name.value, scopes);
|
|
@@ -515,16 +515,6 @@ function createAuthenticatedDirectiveNode() {
|
|
|
515
515
|
arguments: [],
|
|
516
516
|
};
|
|
517
517
|
}
|
|
518
|
-
function deduplicatePoliciesOrScopes(items) {
|
|
519
|
-
const stringified = items.map((group) => group.sort().join("ɵ"));
|
|
520
|
-
const indexesToRemove = [];
|
|
521
|
-
for (let index = 0; index < stringified.length; index++) {
|
|
522
|
-
if (stringified.indexOf(stringified[index]) !== index) {
|
|
523
|
-
indexesToRemove.push(index);
|
|
524
|
-
}
|
|
525
|
-
}
|
|
526
|
-
return items.filter((_, index) => !indexesToRemove.includes(index));
|
|
527
|
-
}
|
|
528
518
|
function createPolicyDirectiveNode(policies) {
|
|
529
519
|
return {
|
|
530
520
|
kind: Kind.DIRECTIVE,
|
|
@@ -541,7 +531,7 @@ function createPolicyDirectiveNode(policies) {
|
|
|
541
531
|
},
|
|
542
532
|
value: {
|
|
543
533
|
kind: Kind.LIST,
|
|
544
|
-
values:
|
|
534
|
+
values: policies.map((group) => ({
|
|
545
535
|
kind: Kind.LIST,
|
|
546
536
|
values: group.map((policy) => ({
|
|
547
537
|
kind: Kind.STRING,
|
|
@@ -569,7 +559,7 @@ function createRequiresScopesDirectiveNode(scopes) {
|
|
|
569
559
|
},
|
|
570
560
|
value: {
|
|
571
561
|
kind: Kind.LIST,
|
|
572
|
-
values:
|
|
562
|
+
values: scopes.map((group) => ({
|
|
573
563
|
kind: Kind.LIST,
|
|
574
564
|
values: group.map((scope) => ({
|
|
575
565
|
kind: Kind.STRING,
|
|
@@ -713,7 +703,8 @@ function createCostDirectiveNode(input) {
|
|
|
713
703
|
}
|
|
714
704
|
function createListSizeDirectiveNode(input) {
|
|
715
705
|
const args = [];
|
|
716
|
-
if (input.requireOneSlicingArgument === false
|
|
706
|
+
if (input.requireOneSlicingArgument === false ||
|
|
707
|
+
input.printRequireOneSlicingArgument === true) {
|
|
717
708
|
args.push({
|
|
718
709
|
kind: Kind.ARGUMENT,
|
|
719
710
|
name: {
|
|
@@ -722,7 +713,7 @@ function createListSizeDirectiveNode(input) {
|
|
|
722
713
|
},
|
|
723
714
|
value: {
|
|
724
715
|
kind: Kind.BOOLEAN,
|
|
725
|
-
value:
|
|
716
|
+
value: input.requireOneSlicingArgument === true,
|
|
726
717
|
},
|
|
727
718
|
});
|
|
728
719
|
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ensureValue, mathMax } from "../../utils/helpers.js";
|
|
2
2
|
import { createEnumTypeNode } from "./ast.js";
|
|
3
3
|
import { convertToConst } from "./common.js";
|
|
4
|
+
import { mergeScopePolicies } from "../../utils/auth.js";
|
|
4
5
|
export function enumTypeBuilder() {
|
|
5
6
|
return {
|
|
6
7
|
visitSubgraphState(graph, state, typeName, type) {
|
|
@@ -13,10 +14,10 @@ export function enumTypeBuilder() {
|
|
|
13
14
|
enumTypeState.authenticated = true;
|
|
14
15
|
}
|
|
15
16
|
if (type.policies) {
|
|
16
|
-
enumTypeState.policies.
|
|
17
|
+
enumTypeState.policies = mergeScopePolicies(enumTypeState.policies, type.policies);
|
|
17
18
|
}
|
|
18
19
|
if (type.scopes) {
|
|
19
|
-
enumTypeState.scopes.
|
|
20
|
+
enumTypeState.scopes = mergeScopePolicies(enumTypeState.scopes, type.scopes);
|
|
20
21
|
}
|
|
21
22
|
if (type.cost !== null) {
|
|
22
23
|
enumTypeState.cost = mathMax(type.cost, enumTypeState.cost);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { ensureValue, mathMax, mathMaxNullable, nullableArrayUnion, } from "../../utils/helpers.js";
|
|
1
|
+
import { ensureValue, isDefined, mathMax, mathMaxNullable, nullableArrayUnion, } from "../../utils/helpers.js";
|
|
2
2
|
import { createInterfaceTypeNode } from "./ast.js";
|
|
3
3
|
import { convertToConst } from "./common.js";
|
|
4
|
+
import { mergeScopePolicies } from "../../utils/auth.js";
|
|
4
5
|
export function interfaceTypeBuilder() {
|
|
5
6
|
return {
|
|
6
7
|
visitSubgraphState(graph, state, typeName, type) {
|
|
@@ -9,15 +10,6 @@ export function interfaceTypeBuilder() {
|
|
|
9
10
|
if (type.inaccessible) {
|
|
10
11
|
interfaceTypeState.inaccessible = true;
|
|
11
12
|
}
|
|
12
|
-
if (type.authenticated) {
|
|
13
|
-
interfaceTypeState.authenticated = true;
|
|
14
|
-
}
|
|
15
|
-
if (type.policies) {
|
|
16
|
-
interfaceTypeState.policies.push(...type.policies);
|
|
17
|
-
}
|
|
18
|
-
if (type.scopes) {
|
|
19
|
-
interfaceTypeState.scopes.push(...type.scopes);
|
|
20
|
-
}
|
|
21
13
|
if (type.isDefinition) {
|
|
22
14
|
interfaceTypeState.hasDefinition = true;
|
|
23
15
|
}
|
|
@@ -59,16 +51,17 @@ export function interfaceTypeBuilder() {
|
|
|
59
51
|
fieldState.authenticated = true;
|
|
60
52
|
}
|
|
61
53
|
if (field.policies) {
|
|
62
|
-
fieldState.policies.
|
|
54
|
+
fieldState.policies = mergeScopePolicies(fieldState.policies, field.policies);
|
|
63
55
|
}
|
|
64
56
|
if (field.scopes) {
|
|
65
|
-
fieldState.scopes.
|
|
57
|
+
fieldState.scopes = mergeScopePolicies(fieldState.scopes, field.scopes);
|
|
66
58
|
}
|
|
67
59
|
if (field.cost !== null) {
|
|
68
60
|
fieldState.cost = mathMax(field.cost, fieldState.cost);
|
|
69
61
|
}
|
|
70
62
|
if (field.listSize !== null) {
|
|
71
63
|
fieldState.listSize = {
|
|
64
|
+
printRequireOneSlicingArgument: false,
|
|
72
65
|
assumedSize: mathMaxNullable(fieldState.listSize?.assumedSize, field.listSize.assumedSize),
|
|
73
66
|
requireOneSlicingArgument: (fieldState.listSize?.requireOneSlicingArgument ?? true) &&
|
|
74
67
|
field.listSize.requireOneSlicingArgument,
|
|
@@ -98,6 +91,9 @@ export function interfaceTypeBuilder() {
|
|
|
98
91
|
version: graph.version,
|
|
99
92
|
external: field.external,
|
|
100
93
|
shareable: field.shareable,
|
|
94
|
+
authenticated: field.authenticated,
|
|
95
|
+
policies: field.policies,
|
|
96
|
+
scopes: field.scopes,
|
|
101
97
|
usedAsKey,
|
|
102
98
|
});
|
|
103
99
|
for (const arg of field.args.values()) {
|
|
@@ -131,6 +127,31 @@ export function interfaceTypeBuilder() {
|
|
|
131
127
|
}
|
|
132
128
|
}
|
|
133
129
|
},
|
|
130
|
+
composeSupergraphState(interfaceType, graphs, { supergraphState }) {
|
|
131
|
+
const implementors = Array.from(interfaceType.implementedBy)
|
|
132
|
+
.map((typeName) => supergraphState.objectTypes.get(typeName))
|
|
133
|
+
.filter(isDefined);
|
|
134
|
+
for (const implementor of implementors) {
|
|
135
|
+
interfaceType.scopes = mergeScopePolicies(interfaceType.scopes, implementor.scopes);
|
|
136
|
+
interfaceType.policies = mergeScopePolicies(interfaceType.policies, implementor.policies);
|
|
137
|
+
if (implementor.authenticated) {
|
|
138
|
+
interfaceType.authenticated = true;
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
for (const field of interfaceType.fields.values()) {
|
|
142
|
+
for (const implementor of implementors) {
|
|
143
|
+
let implementorField = implementor.fields.get(field.name);
|
|
144
|
+
if (!implementorField) {
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
field.scopes = mergeScopePolicies(field.scopes, implementorField.scopes);
|
|
148
|
+
field.policies = mergeScopePolicies(field.policies, implementorField.policies);
|
|
149
|
+
if (implementorField.authenticated) {
|
|
150
|
+
field.authenticated = true;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
},
|
|
134
155
|
composeSupergraphNode(interfaceType, graphs, { supergraphState }) {
|
|
135
156
|
return createInterfaceTypeNode({
|
|
136
157
|
name: interfaceType.name,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ensureValue, isDefined, mathMax, mathMaxNullable, nullableArrayUnion, } from "../../utils/helpers.js";
|
|
2
2
|
import { createObjectTypeNode } from "./ast.js";
|
|
3
3
|
import { convertToConst } from "./common.js";
|
|
4
|
+
import { mergeScopePolicies } from "../../utils/auth.js";
|
|
4
5
|
export function isRealExtension(meta, version) {
|
|
5
6
|
const hasExtendsDirective = meta.extensionType === "@extends";
|
|
6
7
|
if (meta.extension) {
|
|
@@ -29,10 +30,10 @@ export function objectTypeBuilder() {
|
|
|
29
30
|
objectTypeState.authenticated = true;
|
|
30
31
|
}
|
|
31
32
|
if (type.policies) {
|
|
32
|
-
objectTypeState.policies.
|
|
33
|
+
objectTypeState.policies = mergeScopePolicies(objectTypeState.policies, type.policies);
|
|
33
34
|
}
|
|
34
35
|
if (type.scopes) {
|
|
35
|
-
objectTypeState.scopes.
|
|
36
|
+
objectTypeState.scopes = mergeScopePolicies(objectTypeState.scopes, type.scopes);
|
|
36
37
|
}
|
|
37
38
|
if (type.cost !== null) {
|
|
38
39
|
objectTypeState.cost = mathMax(type.cost, objectTypeState.cost);
|
|
@@ -64,6 +65,9 @@ export function objectTypeBuilder() {
|
|
|
64
65
|
shareable: type.shareable,
|
|
65
66
|
interfaces: type.interfaces,
|
|
66
67
|
version: graph.version,
|
|
68
|
+
authenticated: type.authenticated,
|
|
69
|
+
policies: type.policies.slice(),
|
|
70
|
+
scopes: type.scopes.slice(),
|
|
67
71
|
});
|
|
68
72
|
const typeInGraph = objectTypeState.byGraph.get(graph.id);
|
|
69
73
|
for (const field of type.fields.values()) {
|
|
@@ -96,16 +100,17 @@ export function objectTypeBuilder() {
|
|
|
96
100
|
fieldState.authenticated = true;
|
|
97
101
|
}
|
|
98
102
|
if (field.policies) {
|
|
99
|
-
fieldState.policies.
|
|
103
|
+
fieldState.policies = mergeScopePolicies(fieldState.policies, field.policies);
|
|
100
104
|
}
|
|
101
105
|
if (field.scopes) {
|
|
102
|
-
fieldState.scopes.
|
|
106
|
+
fieldState.scopes = mergeScopePolicies(fieldState.scopes, field.scopes);
|
|
103
107
|
}
|
|
104
108
|
if (field.cost !== null) {
|
|
105
109
|
fieldState.cost = mathMax(field.cost, fieldState.cost);
|
|
106
110
|
}
|
|
107
111
|
if (field.listSize !== null) {
|
|
108
112
|
fieldState.listSize = {
|
|
113
|
+
printRequireOneSlicingArgument: false,
|
|
109
114
|
assumedSize: mathMaxNullable(fieldState.listSize?.assumedSize, field.listSize.assumedSize),
|
|
110
115
|
requireOneSlicingArgument: (fieldState.listSize?.requireOneSlicingArgument ?? true) &&
|
|
111
116
|
field.listSize.requireOneSlicingArgument,
|
|
@@ -141,6 +146,9 @@ export function objectTypeBuilder() {
|
|
|
141
146
|
required: field.required,
|
|
142
147
|
shareable: field.shareable,
|
|
143
148
|
extension: field.extension,
|
|
149
|
+
authenticated: field.authenticated,
|
|
150
|
+
policies: field.policies,
|
|
151
|
+
scopes: field.scopes,
|
|
144
152
|
used: field.used,
|
|
145
153
|
usedAsKey,
|
|
146
154
|
version: graph.version,
|
|
@@ -180,6 +188,27 @@ export function objectTypeBuilder() {
|
|
|
180
188
|
}
|
|
181
189
|
}
|
|
182
190
|
},
|
|
191
|
+
composeSupergraphState(objectType, _graphs, { supergraphState }) {
|
|
192
|
+
for (const interfaceName of objectType.interfaces) {
|
|
193
|
+
const interfaceState = supergraphState.interfaceTypes.get(interfaceName);
|
|
194
|
+
if (!interfaceState) {
|
|
195
|
+
throw new Error(`Interface "${interfaceName}" not found in Supergraph state`);
|
|
196
|
+
}
|
|
197
|
+
for (const [interfaceFieldName, interfaceField,] of interfaceState.fields) {
|
|
198
|
+
if (!interfaceState.hasInterfaceObject) {
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
const fieldState = objectType.fields.get(interfaceFieldName);
|
|
202
|
+
if (fieldState) {
|
|
203
|
+
fieldState.scopes = mergeScopePolicies(fieldState.scopes, interfaceField.scopes);
|
|
204
|
+
fieldState.policies = mergeScopePolicies(fieldState.policies, interfaceField.policies);
|
|
205
|
+
if (interfaceField.authenticated) {
|
|
206
|
+
fieldState.authenticated = true;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
},
|
|
183
212
|
composeSupergraphNode(objectType, graphs, { graphNameToId, supergraphState }) {
|
|
184
213
|
const isQuery = objectType.name === "Query";
|
|
185
214
|
const joinTypes = isQuery
|
|
@@ -569,13 +598,23 @@ export function objectTypeBuilder() {
|
|
|
569
598
|
.concat(resolvableFieldsFromInterfaceObjects
|
|
570
599
|
.filter((f) => !objectType.fields.has(f.name))
|
|
571
600
|
.map((field) => {
|
|
601
|
+
let scopes = [];
|
|
602
|
+
let policies = [];
|
|
603
|
+
let authenticated = false;
|
|
604
|
+
for (const fieldInGraph of field.byGraph.values()) {
|
|
605
|
+
scopes = mergeScopePolicies(scopes, fieldInGraph.scopes);
|
|
606
|
+
policies = mergeScopePolicies(policies, fieldInGraph.policies);
|
|
607
|
+
if (fieldInGraph.authenticated) {
|
|
608
|
+
authenticated = true;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
572
611
|
return {
|
|
573
612
|
name: field.name,
|
|
574
613
|
type: field.type,
|
|
575
614
|
inaccessible: field.inaccessible,
|
|
576
|
-
authenticated
|
|
577
|
-
policies
|
|
578
|
-
scopes
|
|
615
|
+
authenticated,
|
|
616
|
+
policies,
|
|
617
|
+
scopes,
|
|
579
618
|
cost: field.cost !== null
|
|
580
619
|
? {
|
|
581
620
|
cost: field.cost,
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { ensureValue, mathMax } from "../../utils/helpers.js";
|
|
2
2
|
import { createScalarTypeNode } from "./ast.js";
|
|
3
3
|
import { convertToConst } from "./common.js";
|
|
4
|
+
import { mergeScopePolicies } from "../../utils/auth.js";
|
|
4
5
|
export function scalarTypeBuilder() {
|
|
5
6
|
return {
|
|
6
7
|
visitSubgraphState(graph, state, typeName, type) {
|
|
@@ -13,10 +14,10 @@ export function scalarTypeBuilder() {
|
|
|
13
14
|
scalarTypeState.authenticated = true;
|
|
14
15
|
}
|
|
15
16
|
if (type.policies) {
|
|
16
|
-
scalarTypeState.policies.
|
|
17
|
+
scalarTypeState.policies = mergeScopePolicies(scalarTypeState.policies, type.policies);
|
|
17
18
|
}
|
|
18
19
|
if (type.scopes) {
|
|
19
|
-
scalarTypeState.scopes.
|
|
20
|
+
scalarTypeState.scopes = mergeScopePolicies(scalarTypeState.scopes, type.scopes);
|
|
20
21
|
}
|
|
21
22
|
if (type.cost !== null) {
|
|
22
23
|
scalarTypeState.cost = mathMax(type.cost, scalarTypeState.cost);
|
package/esm/supergraph/state.js
CHANGED
|
@@ -46,6 +46,12 @@ export function createSupergraphStateBuilder() {
|
|
|
46
46
|
const unionType = unionTypeBuilder();
|
|
47
47
|
const subgraphStates = new Map();
|
|
48
48
|
const graphNameToIdMap = {};
|
|
49
|
+
const helpers = {
|
|
50
|
+
graphNameToId(graphName) {
|
|
51
|
+
return graphNameToIdMap[graphName] ?? null;
|
|
52
|
+
},
|
|
53
|
+
supergraphState: state,
|
|
54
|
+
};
|
|
49
55
|
return {
|
|
50
56
|
addSubgraph(subgraph) {
|
|
51
57
|
if (state.subgraphs.has(subgraph.graph.id)) {
|
|
@@ -57,6 +63,43 @@ export function createSupergraphStateBuilder() {
|
|
|
57
63
|
getGraph(id) {
|
|
58
64
|
return state.subgraphs.get(id);
|
|
59
65
|
},
|
|
66
|
+
composeSupergraphState() {
|
|
67
|
+
if (directive.composeSupergraphState) {
|
|
68
|
+
state.directives.forEach((s) => {
|
|
69
|
+
directive.composeSupergraphState(s, state.subgraphs, helpers);
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
if (scalarType.composeSupergraphState) {
|
|
73
|
+
state.scalarTypes.forEach((s) => {
|
|
74
|
+
scalarType.composeSupergraphState(s, state.subgraphs, helpers);
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
if (enumType.composeSupergraphState) {
|
|
78
|
+
state.enumTypes.forEach((s) => {
|
|
79
|
+
enumType.composeSupergraphState(s, state.subgraphs, helpers);
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
if (inputObjectType.composeSupergraphState) {
|
|
83
|
+
state.inputObjectTypes.forEach((s) => {
|
|
84
|
+
inputObjectType.composeSupergraphState(s, state.subgraphs, helpers);
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
if (objectType.composeSupergraphState) {
|
|
88
|
+
state.objectTypes.forEach((s) => {
|
|
89
|
+
objectType.composeSupergraphState(s, state.subgraphs, helpers);
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
if (interfaceType.composeSupergraphState) {
|
|
93
|
+
state.interfaceTypes.forEach((s) => {
|
|
94
|
+
interfaceType.composeSupergraphState(s, state.subgraphs, helpers);
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (unionType.composeSupergraphState) {
|
|
98
|
+
state.unionTypes.forEach((s) => {
|
|
99
|
+
unionType.composeSupergraphState(s, state.subgraphs, helpers);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
},
|
|
60
103
|
visitSubgraphState(subgraphState) {
|
|
61
104
|
subgraphStates.set(subgraphState.graph.id, subgraphState);
|
|
62
105
|
for (const link of subgraphState.links) {
|
|
@@ -131,12 +174,6 @@ export function createSupergraphStateBuilder() {
|
|
|
131
174
|
},
|
|
132
175
|
build() {
|
|
133
176
|
const transformFields = createFieldsTransformer(state);
|
|
134
|
-
const helpers = {
|
|
135
|
-
graphNameToId(graphName) {
|
|
136
|
-
return graphNameToIdMap[graphName] ?? null;
|
|
137
|
-
},
|
|
138
|
-
supergraphState: state,
|
|
139
|
-
};
|
|
140
177
|
for (const directiveState of state.directives.values()) {
|
|
141
178
|
if (!directiveState.isExecutable || !directiveState.byGraph.size) {
|
|
142
179
|
continue;
|