@apollo/federation-internals 2.10.0-alpha.1 → 2.10.0-alpha.3
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/dist/argumentCompositionStrategies.d.ts +8 -14
- package/dist/argumentCompositionStrategies.d.ts.map +1 -1
- package/dist/argumentCompositionStrategies.js +48 -48
- package/dist/argumentCompositionStrategies.js.map +1 -1
- package/dist/definitions.d.ts +1 -0
- package/dist/definitions.d.ts.map +1 -1
- package/dist/definitions.js +12 -10
- package/dist/definitions.js.map +1 -1
- package/dist/error.d.ts +5 -0
- package/dist/error.d.ts.map +1 -1
- package/dist/error.js +10 -0
- package/dist/error.js.map +1 -1
- package/dist/extractSubgraphsFromSupergraph.js +68 -63
- package/dist/extractSubgraphsFromSupergraph.js.map +1 -1
- package/dist/federation.d.ts +2 -1
- package/dist/federation.d.ts.map +1 -1
- package/dist/federation.js +77 -4
- package/dist/federation.js.map +1 -1
- package/dist/operations.d.ts +8 -4
- package/dist/operations.d.ts.map +1 -1
- package/dist/operations.js +42 -25
- package/dist/operations.js.map +1 -1
- package/dist/specs/coreSpec.d.ts +2 -2
- package/dist/specs/coreSpec.d.ts.map +1 -1
- package/dist/specs/coreSpec.js +15 -20
- package/dist/specs/coreSpec.js.map +1 -1
- package/dist/specs/costSpec.d.ts +3 -0
- package/dist/specs/costSpec.d.ts.map +1 -1
- package/dist/specs/costSpec.js +6 -0
- package/dist/specs/costSpec.js.map +1 -1
- package/dist/supergraphs.d.ts +8 -1
- package/dist/supergraphs.d.ts.map +1 -1
- package/dist/supergraphs.js +20 -2
- package/dist/supergraphs.js.map +1 -1
- package/dist/values.d.ts.map +1 -1
- package/dist/values.js +14 -5
- package/dist/values.js.map +1 -1
- package/package.json +1 -1
- package/src/argumentCompositionStrategies.ts +68 -50
- package/src/definitions.ts +25 -9
- package/src/error.ts +37 -1
- package/src/extractSubgraphsFromSupergraph.ts +73 -92
- package/src/federation.ts +146 -3
- package/src/operations.ts +55 -26
- package/src/specs/coreSpec.ts +27 -26
- package/src/specs/costSpec.ts +9 -1
- package/src/supergraphs.ts +31 -3
- package/src/values.ts +13 -5
|
@@ -13,87 +13,105 @@ export type ArgumentCompositionStrategy = {
|
|
|
13
13
|
function supportFixedTypes(types: (schema: Schema) => InputType[]): TypeSupportValidator {
|
|
14
14
|
return (schema, type) => {
|
|
15
15
|
const supported = types(schema);
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
return { valid: true };
|
|
16
|
+
return supported.some((t) => sameType(t, type))
|
|
17
|
+
? { valid: true }
|
|
18
|
+
: { valid: false, supportedMsg: `type(s) ${supported.join(', ')}` };
|
|
20
19
|
};
|
|
21
20
|
}
|
|
22
21
|
|
|
23
22
|
function supportAnyNonNullArray(): TypeSupportValidator {
|
|
24
|
-
return (_, type) =>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
return (_, type) => isNonNullType(type) && isListType(type.ofType)
|
|
24
|
+
? { valid: true }
|
|
25
|
+
: { valid: false, supportedMsg: 'non nullable list types of any type' }
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function supportAnyArray(): TypeSupportValidator {
|
|
29
|
+
return (_, type) => isListType(type) || (isNonNullType(type) && isListType(type.ofType))
|
|
30
|
+
? { valid: true }
|
|
31
|
+
: { valid: false, supportedMsg: 'list types of any type' };
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// NOTE: This function makes the assumption that for the directive argument
|
|
35
|
+
// being merged, it is not "nullable with non-null default" in the supergraph
|
|
36
|
+
// schema (this kind of type/default combo is confusing and should be avoided,
|
|
37
|
+
// if possible). This assumption allows this function to replace null with
|
|
38
|
+
// undefined, which makes for a cleaner supergraph schema.
|
|
39
|
+
function mergeNullableValues<T>(
|
|
40
|
+
mergeValues: (values: T[]) => T
|
|
41
|
+
): (values: (T | null | undefined)[]) => T | undefined {
|
|
42
|
+
return (values: (T | null | undefined)[]) => {
|
|
43
|
+
const nonNullValues = values.filter((v) => v !== null && v !== undefined) as T[];
|
|
44
|
+
return nonNullValues.length > 0
|
|
45
|
+
? mergeValues(nonNullValues)
|
|
46
|
+
: undefined;
|
|
29
47
|
};
|
|
30
48
|
}
|
|
31
49
|
|
|
50
|
+
function unionValues(values: any[]): any {
|
|
51
|
+
return values.reduce((acc, next) => {
|
|
52
|
+
const newValues = next.filter((v1: any) => !acc.some((v2: any) => valueEquals(v1, v2)));
|
|
53
|
+
return acc.concat(newValues);
|
|
54
|
+
}, []);
|
|
55
|
+
}
|
|
56
|
+
|
|
32
57
|
export const ARGUMENT_COMPOSITION_STRATEGIES = {
|
|
33
58
|
MAX: {
|
|
34
59
|
name: 'MAX',
|
|
35
60
|
isTypeSupported: supportFixedTypes((schema: Schema) => [new NonNullType(schema.intType())]),
|
|
36
|
-
mergeValues: (values:
|
|
61
|
+
mergeValues: (values: number[]) => Math.max(...values),
|
|
37
62
|
},
|
|
38
63
|
MIN: {
|
|
39
64
|
name: 'MIN',
|
|
40
65
|
isTypeSupported: supportFixedTypes((schema: Schema) => [new NonNullType(schema.intType())]),
|
|
41
|
-
mergeValues: (values:
|
|
42
|
-
},
|
|
43
|
-
SUM: {
|
|
44
|
-
name: 'SUM',
|
|
45
|
-
isTypeSupported: supportFixedTypes((schema: Schema) => [new NonNullType(schema.intType())]),
|
|
46
|
-
mergeValues: (values: any[]) => values.reduce((acc, val) => acc + val, 0),
|
|
66
|
+
mergeValues: (values: number[]) => Math.min(...values),
|
|
47
67
|
},
|
|
68
|
+
// NOTE: This doesn't work today because directive applications are de-duped
|
|
69
|
+
// before being merged, we'd need to modify merge logic if we need this kind
|
|
70
|
+
// of behavior.
|
|
71
|
+
// SUM: {
|
|
72
|
+
// name: 'SUM',
|
|
73
|
+
// isTypeSupported: supportFixedTypes((schema: Schema) => [new NonNullType(schema.intType())]),
|
|
74
|
+
// mergeValues: (values: any[]) => values.reduce((acc, val) => acc + val, 0),
|
|
75
|
+
// },
|
|
48
76
|
INTERSECTION: {
|
|
49
77
|
name: 'INTERSECTION',
|
|
50
78
|
isTypeSupported: supportAnyNonNullArray(),
|
|
51
|
-
mergeValues: (values: any[]) => values.reduce((acc,
|
|
79
|
+
mergeValues: (values: any[]) => values.reduce((acc, next) => {
|
|
80
|
+
if (acc === undefined) {
|
|
81
|
+
return next;
|
|
82
|
+
} else {
|
|
83
|
+
return acc.filter((v1: any) => next.some((v2: any) => valueEquals(v1, v2)));
|
|
84
|
+
}
|
|
85
|
+
}, undefined) ?? [],
|
|
52
86
|
},
|
|
53
87
|
UNION: {
|
|
54
88
|
name: 'UNION',
|
|
55
89
|
isTypeSupported: supportAnyNonNullArray(),
|
|
56
|
-
mergeValues:
|
|
57
|
-
values.reduce((acc, val) => {
|
|
58
|
-
const newValues = val.filter((v1: any) => !acc.some((v2: any) => valueEquals(v1, v2)));
|
|
59
|
-
return acc.concat(newValues);
|
|
60
|
-
}, []),
|
|
90
|
+
mergeValues: unionValues,
|
|
61
91
|
},
|
|
62
92
|
NULLABLE_AND: {
|
|
63
93
|
name: 'NULLABLE_AND',
|
|
64
|
-
isTypeSupported: supportFixedTypes((schema: Schema) => [
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
return acc && next;
|
|
72
|
-
}
|
|
73
|
-
}, undefined),
|
|
94
|
+
isTypeSupported: supportFixedTypes((schema: Schema) => [
|
|
95
|
+
schema.booleanType(),
|
|
96
|
+
new NonNullType(schema.booleanType())
|
|
97
|
+
]),
|
|
98
|
+
mergeValues: mergeNullableValues(
|
|
99
|
+
(values: boolean[]) => values.every((v) => v)
|
|
100
|
+
),
|
|
74
101
|
},
|
|
75
102
|
NULLABLE_MAX: {
|
|
76
103
|
name: 'NULLABLE_MAX',
|
|
77
|
-
isTypeSupported: supportFixedTypes((schema: Schema) => [
|
|
78
|
-
|
|
104
|
+
isTypeSupported: supportFixedTypes((schema: Schema) => [
|
|
105
|
+
schema.intType(),
|
|
106
|
+
new NonNullType(schema.intType())
|
|
107
|
+
]),
|
|
108
|
+
mergeValues: mergeNullableValues(
|
|
109
|
+
(values: number[]) => Math.max(...values)
|
|
110
|
+
)
|
|
79
111
|
},
|
|
80
112
|
NULLABLE_UNION: {
|
|
81
113
|
name: 'NULLABLE_UNION',
|
|
82
|
-
isTypeSupported: (
|
|
83
|
-
mergeValues: (
|
|
84
|
-
if (values.every((v) => v === undefined)) {
|
|
85
|
-
return undefined;
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const combined = new Set();
|
|
89
|
-
for (const subgraphValues of values) {
|
|
90
|
-
if (Array.isArray(subgraphValues)) {
|
|
91
|
-
for (const value of subgraphValues) {
|
|
92
|
-
combined.add(value);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
return Array.from(combined);
|
|
97
|
-
}
|
|
114
|
+
isTypeSupported: supportAnyArray(),
|
|
115
|
+
mergeValues: mergeNullableValues(unionValues),
|
|
98
116
|
}
|
|
99
117
|
}
|
package/src/definitions.ts
CHANGED
|
@@ -984,12 +984,28 @@ export class CoreFeature {
|
|
|
984
984
|
}
|
|
985
985
|
|
|
986
986
|
directiveNameInSchema(name: string): string {
|
|
987
|
-
|
|
987
|
+
return CoreFeature.directiveNameInSchemaForCoreArguments(
|
|
988
|
+
this.url,
|
|
989
|
+
this.nameInSchema,
|
|
990
|
+
this.imports,
|
|
991
|
+
name,
|
|
992
|
+
);
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
static directiveNameInSchemaForCoreArguments(
|
|
996
|
+
specUrl: FeatureUrl,
|
|
997
|
+
specNameInSchema: string,
|
|
998
|
+
imports: CoreImport[],
|
|
999
|
+
directiveNameInSpec: string,
|
|
1000
|
+
): string {
|
|
1001
|
+
const elementImport = imports.find((i) =>
|
|
1002
|
+
i.name.charAt(0) === '@' && i.name.slice(1) === directiveNameInSpec
|
|
1003
|
+
);
|
|
988
1004
|
return elementImport
|
|
989
|
-
? (elementImport.as?.slice(1) ??
|
|
990
|
-
: (
|
|
991
|
-
?
|
|
992
|
-
:
|
|
1005
|
+
? (elementImport.as?.slice(1) ?? directiveNameInSpec)
|
|
1006
|
+
: (directiveNameInSpec === specUrl.name
|
|
1007
|
+
? specNameInSchema
|
|
1008
|
+
: specNameInSchema + '__' + directiveNameInSpec
|
|
993
1009
|
);
|
|
994
1010
|
}
|
|
995
1011
|
|
|
@@ -1064,7 +1080,7 @@ export class CoreFeatures {
|
|
|
1064
1080
|
const feature = this.byAlias.get(splitted[0]);
|
|
1065
1081
|
return feature ? {
|
|
1066
1082
|
feature,
|
|
1067
|
-
nameInFeature: splitted
|
|
1083
|
+
nameInFeature: splitted.slice(1).join('__'),
|
|
1068
1084
|
isImported: false,
|
|
1069
1085
|
} : undefined;
|
|
1070
1086
|
} else {
|
|
@@ -1076,7 +1092,7 @@ export class CoreFeatures {
|
|
|
1076
1092
|
if ((as ?? name) === importName) {
|
|
1077
1093
|
return {
|
|
1078
1094
|
feature,
|
|
1079
|
-
nameInFeature: name.slice(1),
|
|
1095
|
+
nameInFeature: isDirective ? name.slice(1) : name,
|
|
1080
1096
|
isImported: true,
|
|
1081
1097
|
};
|
|
1082
1098
|
}
|
|
@@ -1088,8 +1104,8 @@ export class CoreFeatures {
|
|
|
1088
1104
|
if (directFeature && isDirective) {
|
|
1089
1105
|
return {
|
|
1090
1106
|
feature: directFeature,
|
|
1091
|
-
nameInFeature:
|
|
1092
|
-
isImported:
|
|
1107
|
+
nameInFeature: element.name,
|
|
1108
|
+
isImported: false,
|
|
1093
1109
|
};
|
|
1094
1110
|
}
|
|
1095
1111
|
|
package/src/error.ts
CHANGED
|
@@ -597,6 +597,36 @@ const CONTEXTUAL_ARGUMENT_NOT_CONTEXTUAL_IN_ALL_SUBGRAPHS = makeCodeDefinition(
|
|
|
597
597
|
{ addedIn: '2.7.0' },
|
|
598
598
|
);
|
|
599
599
|
|
|
600
|
+
const COST_APPLIED_TO_INTERFACE_FIELD = makeCodeDefinition(
|
|
601
|
+
'COST_APPLIED_TO_INTERFACE_FIELD',
|
|
602
|
+
'The `@cost` directive must be applied to concrete types',
|
|
603
|
+
{ addedIn: '2.9.2' },
|
|
604
|
+
);
|
|
605
|
+
|
|
606
|
+
const LIST_SIZE_APPLIED_TO_NON_LIST = makeCodeDefinition(
|
|
607
|
+
'LIST_SIZE_APPLIED_TO_NON_LIST',
|
|
608
|
+
'The `@listSize` directive must be applied to list types',
|
|
609
|
+
{ addedIn: '2.9.2' },
|
|
610
|
+
);
|
|
611
|
+
|
|
612
|
+
const LIST_SIZE_INVALID_ASSUMED_SIZE = makeCodeDefinition(
|
|
613
|
+
'LIST_SIZE_INVALID_ASSUMED_SIZE',
|
|
614
|
+
'The `@listSize` directive assumed size cannot be negative',
|
|
615
|
+
{ addedIn: '2.9.2' },
|
|
616
|
+
);
|
|
617
|
+
|
|
618
|
+
const LIST_SIZE_INVALID_SLICING_ARGUMENT = makeCodeDefinition(
|
|
619
|
+
'LIST_SIZE_INVALID_SLICING_ARGUMENT',
|
|
620
|
+
'The `@listSize` directive must have existing integer slicing arguments',
|
|
621
|
+
{ addedIn: '2.9.2' },
|
|
622
|
+
);
|
|
623
|
+
|
|
624
|
+
const LIST_SIZE_INVALID_SIZED_FIELD = makeCodeDefinition(
|
|
625
|
+
'LIST_SIZE_INVALID_SIZED_FIELD',
|
|
626
|
+
'The `@listSize` directive must reference existing list fields as sized fields',
|
|
627
|
+
{ addedIn: '2.9.2' },
|
|
628
|
+
);
|
|
629
|
+
|
|
600
630
|
export const ERROR_CATEGORIES = {
|
|
601
631
|
DIRECTIVE_FIELDS_MISSING_EXTERNAL,
|
|
602
632
|
DIRECTIVE_UNSUPPORTED_ON_INTERFACE,
|
|
@@ -690,7 +720,13 @@ export const ERRORS = {
|
|
|
690
720
|
INTERFACE_OBJECT_USAGE_ERROR,
|
|
691
721
|
INTERFACE_KEY_NOT_ON_IMPLEMENTATION,
|
|
692
722
|
INTERFACE_KEY_MISSING_IMPLEMENTATION_TYPE,
|
|
693
|
-
|
|
723
|
+
CONTEXTUAL_ARGUMENT_NOT_CONTEXTUAL_IN_ALL_SUBGRAPHS,
|
|
724
|
+
// Errors related to demand control
|
|
725
|
+
COST_APPLIED_TO_INTERFACE_FIELD,
|
|
726
|
+
LIST_SIZE_APPLIED_TO_NON_LIST,
|
|
727
|
+
LIST_SIZE_INVALID_ASSUMED_SIZE,
|
|
728
|
+
LIST_SIZE_INVALID_SIZED_FIELD,
|
|
729
|
+
LIST_SIZE_INVALID_SLICING_ARGUMENT,
|
|
694
730
|
};
|
|
695
731
|
|
|
696
732
|
const codeDefByCode = Object.values(ERRORS).reduce((obj: {[code: string]: ErrorCodeDefinition}, codeDef: ErrorCodeDefinition) => { obj[codeDef.code] = codeDef; return obj; }, {});
|
|
@@ -40,7 +40,7 @@ import { parseSelectionSet } from "./operations";
|
|
|
40
40
|
import fs from 'fs';
|
|
41
41
|
import path from 'path';
|
|
42
42
|
import { validateStringContainsBoolean } from "./utils";
|
|
43
|
-
import {
|
|
43
|
+
import { ContextSpecDefinition, CostSpecDefinition, SchemaElement, errorCauses, isFederationDirectiveDefinedInSchema, printErrors } from ".";
|
|
44
44
|
|
|
45
45
|
function filteredTypes(
|
|
46
46
|
supergraph: Schema,
|
|
@@ -194,7 +194,7 @@ function typesUsedInFederationDirective(fieldSet: string | undefined, parentType
|
|
|
194
194
|
}
|
|
195
195
|
|
|
196
196
|
export function extractSubgraphsFromSupergraph(supergraph: Schema, validateExtractedSubgraphs: boolean = true): [Subgraphs, Map<string, string>] {
|
|
197
|
-
const [coreFeatures, joinSpec] = validateSupergraph(supergraph);
|
|
197
|
+
const [coreFeatures, joinSpec, contextSpec, costSpec] = validateSupergraph(supergraph);
|
|
198
198
|
const isFed1 = joinSpec.version.equals(new FeatureVersion(0, 1));
|
|
199
199
|
try {
|
|
200
200
|
// We first collect the subgraphs (creating an empty schema that we'll populate next for each).
|
|
@@ -224,13 +224,13 @@ export function extractSubgraphsFromSupergraph(supergraph: Schema, validateExtra
|
|
|
224
224
|
}
|
|
225
225
|
|
|
226
226
|
const types = filteredTypes(supergraph, joinSpec, coreFeatures.coreDefinition);
|
|
227
|
-
const originalDirectiveNames = getApolloDirectiveNames(supergraph);
|
|
228
227
|
const args: ExtractArguments = {
|
|
229
228
|
supergraph,
|
|
230
229
|
subgraphs,
|
|
231
230
|
joinSpec,
|
|
231
|
+
contextSpec,
|
|
232
|
+
costSpec,
|
|
232
233
|
filteredTypes: types,
|
|
233
|
-
originalDirectiveNames,
|
|
234
234
|
getSubgraph,
|
|
235
235
|
getSubgraphEnumValue,
|
|
236
236
|
};
|
|
@@ -293,8 +293,9 @@ type ExtractArguments = {
|
|
|
293
293
|
supergraph: Schema,
|
|
294
294
|
subgraphs: Subgraphs,
|
|
295
295
|
joinSpec: JoinSpecDefinition,
|
|
296
|
+
contextSpec: ContextSpecDefinition | undefined,
|
|
297
|
+
costSpec: CostSpecDefinition | undefined,
|
|
296
298
|
filteredTypes: NamedType[],
|
|
297
|
-
originalDirectiveNames: Record<string, string>,
|
|
298
299
|
getSubgraph: (application: Directive<any, { graph?: string }>) => Subgraph | undefined,
|
|
299
300
|
getSubgraphEnumValue: (subgraphName: string) => string
|
|
300
301
|
}
|
|
@@ -352,7 +353,9 @@ function addAllEmptySubgraphTypes(args: ExtractArguments): TypesInfo {
|
|
|
352
353
|
const subgraph = getSubgraph(application);
|
|
353
354
|
assert(subgraph, () => `Should have found the subgraph for ${application}`);
|
|
354
355
|
const subgraphType = subgraph.schema.addType(newNamedType(type.kind, type.name));
|
|
355
|
-
|
|
356
|
+
if (args.costSpec) {
|
|
357
|
+
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec);
|
|
358
|
+
}
|
|
356
359
|
}
|
|
357
360
|
break;
|
|
358
361
|
}
|
|
@@ -401,17 +404,8 @@ function addEmptyType<T extends NamedType>(
|
|
|
401
404
|
}
|
|
402
405
|
}
|
|
403
406
|
}
|
|
404
|
-
|
|
405
|
-
const
|
|
406
|
-
assert(coreFeatures, 'Should have core features');
|
|
407
|
-
const contextFeature = coreFeatures.getByIdentity(ContextSpecDefinition.identity);
|
|
408
|
-
let supergraphContextDirective: DirectiveDefinition<{ name: string}> | undefined;
|
|
409
|
-
if (contextFeature) {
|
|
410
|
-
const contextSpec = CONTEXT_VERSIONS.find(contextFeature.url.version);
|
|
411
|
-
assert(contextSpec, 'Should have context spec');
|
|
412
|
-
supergraphContextDirective = contextSpec.contextDirective(supergraph);
|
|
413
|
-
}
|
|
414
|
-
|
|
407
|
+
|
|
408
|
+
const supergraphContextDirective = args.contextSpec?.contextDirective(supergraph);
|
|
415
409
|
if (supergraphContextDirective) {
|
|
416
410
|
const contextApplications = type.appliedDirectivesOf(supergraphContextDirective);
|
|
417
411
|
// for every application, apply the context directive to the correct subgraph
|
|
@@ -438,8 +432,6 @@ function extractObjOrItfContent(args: ExtractArguments, info: TypeInfo<ObjectTyp
|
|
|
438
432
|
const implementsDirective = args.joinSpec.implementsDirective(args.supergraph);
|
|
439
433
|
assert(implementsDirective, '@join__implements should existing for a fed2 supergraph');
|
|
440
434
|
|
|
441
|
-
const originalDirectiveNames = args.originalDirectiveNames;
|
|
442
|
-
|
|
443
435
|
for (const { type, subgraphsInfo } of info) {
|
|
444
436
|
const implementsApplications = type.appliedDirectivesOf(implementsDirective);
|
|
445
437
|
for (const application of implementsApplications) {
|
|
@@ -450,8 +442,10 @@ function extractObjOrItfContent(args: ExtractArguments, info: TypeInfo<ObjectTyp
|
|
|
450
442
|
subgraphInfo.type.addImplementedInterface(args.interface);
|
|
451
443
|
}
|
|
452
444
|
|
|
453
|
-
|
|
454
|
-
|
|
445
|
+
if (args.costSpec) {
|
|
446
|
+
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) {
|
|
447
|
+
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec);
|
|
448
|
+
}
|
|
455
449
|
}
|
|
456
450
|
|
|
457
451
|
for (const field of type.fields()) {
|
|
@@ -460,7 +454,13 @@ function extractObjOrItfContent(args: ExtractArguments, info: TypeInfo<ObjectTyp
|
|
|
460
454
|
// In fed2 subgraph, no @join__field means that the field is in all the subgraphs in which the type is.
|
|
461
455
|
const isShareable = isObjectType(type) && subgraphsInfo.size > 1;
|
|
462
456
|
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) {
|
|
463
|
-
addSubgraphField({
|
|
457
|
+
addSubgraphField({
|
|
458
|
+
field,
|
|
459
|
+
type: subgraphType,
|
|
460
|
+
subgraph,
|
|
461
|
+
isShareable,
|
|
462
|
+
costSpec: args.costSpec
|
|
463
|
+
});
|
|
464
464
|
}
|
|
465
465
|
} else {
|
|
466
466
|
const isShareable = isObjectType(type)
|
|
@@ -478,58 +478,21 @@ function extractObjOrItfContent(args: ExtractArguments, info: TypeInfo<ObjectTyp
|
|
|
478
478
|
}
|
|
479
479
|
|
|
480
480
|
const { type: subgraphType, subgraph } = subgraphsInfo.get(joinFieldArgs.graph)!;
|
|
481
|
-
addSubgraphField({
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
/**
|
|
489
|
-
* Builds a map of original name to new name for Apollo feature directives. This is
|
|
490
|
-
* used to handle cases where a directive is renamed via an import statement. For
|
|
491
|
-
* example, importing a directive with a custom name like
|
|
492
|
-
* ```graphql
|
|
493
|
-
* @link(url: "https://specs.apollo.dev/cost/v0.1", import: [{ name: "@cost", as: "@renamedCost" }])
|
|
494
|
-
* ```
|
|
495
|
-
* results in a map entry of `cost -> renamedCost` with the `@` prefix removed.
|
|
496
|
-
*
|
|
497
|
-
* If the directive is imported under its default name, that also results in an entry. So,
|
|
498
|
-
* ```graphql
|
|
499
|
-
* @link(url: "https://specs.apollo.dev/cost/v0.1", import: ["@cost"])
|
|
500
|
-
* ```
|
|
501
|
-
* results in a map entry of `cost -> cost`. This duals as a way to check if a directive
|
|
502
|
-
* is included in the supergraph schema.
|
|
503
|
-
*
|
|
504
|
-
* **Important:** This map does _not_ include directives imported from identities other
|
|
505
|
-
* than `specs.apollo.dev`. This helps us avoid extracting directives to subgraphs
|
|
506
|
-
* when a custom directive's name conflicts with that of a default one.
|
|
507
|
-
*/
|
|
508
|
-
function getApolloDirectiveNames(supergraph: Schema): Record<string, string> {
|
|
509
|
-
const originalDirectiveNames: Record<string, string> = {};
|
|
510
|
-
for (const linkDirective of supergraph.schemaDefinition.appliedDirectivesOf("link")) {
|
|
511
|
-
if (linkDirective.arguments().url && linkDirective.arguments().import) {
|
|
512
|
-
const url = FeatureUrl.maybeParse(linkDirective.arguments().url);
|
|
513
|
-
if (!url?.identity.includes("specs.apollo.dev")) {
|
|
514
|
-
continue;
|
|
515
|
-
}
|
|
516
|
-
|
|
517
|
-
for (const importedDirective of linkDirective.arguments().import) {
|
|
518
|
-
if (importedDirective.name && importedDirective.as) {
|
|
519
|
-
originalDirectiveNames[importedDirective.name.replace('@', '')] = importedDirective.as.replace('@', '');
|
|
520
|
-
} else if (typeof importedDirective === 'string') {
|
|
521
|
-
originalDirectiveNames[importedDirective.replace('@', '')] = importedDirective.replace('@', '');
|
|
481
|
+
addSubgraphField({
|
|
482
|
+
field,
|
|
483
|
+
type: subgraphType,
|
|
484
|
+
subgraph, isShareable,
|
|
485
|
+
joinFieldArgs,
|
|
486
|
+
costSpec: args.costSpec
|
|
487
|
+
});
|
|
522
488
|
}
|
|
523
489
|
}
|
|
524
490
|
}
|
|
525
491
|
}
|
|
526
|
-
|
|
527
|
-
return originalDirectiveNames;
|
|
528
492
|
}
|
|
529
493
|
|
|
530
494
|
function extractInputObjContent(args: ExtractArguments, info: TypeInfo<InputObjectType>[]) {
|
|
531
495
|
const fieldDirective = args.joinSpec.fieldDirective(args.supergraph);
|
|
532
|
-
const originalDirectiveNames = args.originalDirectiveNames;
|
|
533
496
|
|
|
534
497
|
for (const { type, subgraphsInfo } of info) {
|
|
535
498
|
for (const field of type.fields()) {
|
|
@@ -537,19 +500,30 @@ function extractInputObjContent(args: ExtractArguments, info: TypeInfo<InputObje
|
|
|
537
500
|
if (fieldApplications.length === 0) {
|
|
538
501
|
// In fed2 subgraph, no @join__field means that the field is in all the subgraphs in which the type is.
|
|
539
502
|
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) {
|
|
540
|
-
addSubgraphInputField({
|
|
503
|
+
addSubgraphInputField({
|
|
504
|
+
field,
|
|
505
|
+
type: subgraphType,
|
|
506
|
+
subgraph,
|
|
507
|
+
costSpec: args.costSpec
|
|
508
|
+
});
|
|
541
509
|
}
|
|
542
510
|
} else {
|
|
543
511
|
for (const application of fieldApplications) {
|
|
544
|
-
const
|
|
512
|
+
const joinFieldArgs = application.arguments();
|
|
545
513
|
// We use a @join__field with no graph to indicates when a field in the supergraph does not come
|
|
546
514
|
// directly from any subgraph and there is thus nothing to do to "extract" it.
|
|
547
|
-
if (!
|
|
515
|
+
if (!joinFieldArgs.graph) {
|
|
548
516
|
continue;
|
|
549
517
|
}
|
|
550
518
|
|
|
551
|
-
const { type: subgraphType, subgraph } = subgraphsInfo.get(
|
|
552
|
-
addSubgraphInputField({
|
|
519
|
+
const { type: subgraphType, subgraph } = subgraphsInfo.get(joinFieldArgs.graph)!;
|
|
520
|
+
addSubgraphInputField({
|
|
521
|
+
field,
|
|
522
|
+
type: subgraphType,
|
|
523
|
+
subgraph,
|
|
524
|
+
joinFieldArgs,
|
|
525
|
+
costSpec: args.costSpec
|
|
526
|
+
});
|
|
553
527
|
}
|
|
554
528
|
}
|
|
555
529
|
}
|
|
@@ -559,11 +533,12 @@ function extractInputObjContent(args: ExtractArguments, info: TypeInfo<InputObje
|
|
|
559
533
|
function extractEnumTypeContent(args: ExtractArguments, info: TypeInfo<EnumType>[]) {
|
|
560
534
|
// This was added in join 0.3, so it can genuinely be undefined.
|
|
561
535
|
const enumValueDirective = args.joinSpec.enumValueDirective(args.supergraph);
|
|
562
|
-
const originalDirectiveNames = args.originalDirectiveNames;
|
|
563
536
|
|
|
564
537
|
for (const { type, subgraphsInfo } of info) {
|
|
565
|
-
|
|
566
|
-
|
|
538
|
+
if (args.costSpec) {
|
|
539
|
+
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) {
|
|
540
|
+
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec);
|
|
541
|
+
}
|
|
567
542
|
}
|
|
568
543
|
|
|
569
544
|
for (const value of type.values) {
|
|
@@ -678,20 +653,20 @@ function maybeDumpSubgraphSchema(subgraph: Subgraph): string {
|
|
|
678
653
|
}
|
|
679
654
|
}
|
|
680
655
|
|
|
681
|
-
function propagateDemandControlDirectives(source: SchemaElement<any, any>, dest: SchemaElement<any, any>, subgraph: Subgraph,
|
|
682
|
-
const
|
|
683
|
-
if (
|
|
684
|
-
const
|
|
685
|
-
if (
|
|
686
|
-
dest.applyDirective(subgraph.metadata().costDirective().name,
|
|
656
|
+
function propagateDemandControlDirectives(source: SchemaElement<any, any>, dest: SchemaElement<any, any>, subgraph: Subgraph, costSpec: CostSpecDefinition) {
|
|
657
|
+
const costDirective = costSpec.costDirective(source.schema());
|
|
658
|
+
if (costDirective) {
|
|
659
|
+
const application = source.appliedDirectivesOf(costDirective)[0];
|
|
660
|
+
if (application) {
|
|
661
|
+
dest.applyDirective(subgraph.metadata().costDirective().name, application.arguments());
|
|
687
662
|
}
|
|
688
663
|
}
|
|
689
664
|
|
|
690
|
-
const
|
|
691
|
-
if (
|
|
692
|
-
const
|
|
693
|
-
if (
|
|
694
|
-
dest.applyDirective(subgraph.metadata().listSizeDirective().name,
|
|
665
|
+
const listSizeDirective = costSpec.listSizeDirective(source.schema());
|
|
666
|
+
if (listSizeDirective) {
|
|
667
|
+
const application = source.appliedDirectivesOf(listSizeDirective)[0];
|
|
668
|
+
if (application) {
|
|
669
|
+
dest.applyDirective(subgraph.metadata().listSizeDirective().name, application.arguments());
|
|
695
670
|
}
|
|
696
671
|
}
|
|
697
672
|
}
|
|
@@ -707,14 +682,14 @@ function addSubgraphField({
|
|
|
707
682
|
subgraph,
|
|
708
683
|
isShareable,
|
|
709
684
|
joinFieldArgs,
|
|
710
|
-
|
|
685
|
+
costSpec,
|
|
711
686
|
}: {
|
|
712
687
|
field: FieldDefinition<ObjectType | InterfaceType>,
|
|
713
688
|
type: ObjectType | InterfaceType,
|
|
714
689
|
subgraph: Subgraph,
|
|
715
690
|
isShareable: boolean,
|
|
716
691
|
joinFieldArgs?: JoinFieldDirectiveArguments,
|
|
717
|
-
|
|
692
|
+
costSpec?: CostSpecDefinition,
|
|
718
693
|
}): FieldDefinition<ObjectType | InterfaceType> {
|
|
719
694
|
const copiedFieldType = joinFieldArgs?.type
|
|
720
695
|
? decodeType(joinFieldArgs.type, subgraph.schema, subgraph.name)
|
|
@@ -723,7 +698,9 @@ function addSubgraphField({
|
|
|
723
698
|
const subgraphField = type.addField(field.name, copiedFieldType);
|
|
724
699
|
for (const arg of field.arguments()) {
|
|
725
700
|
const argDef = subgraphField.addArgument(arg.name, copyType(arg.type!, subgraph.schema, subgraph.name), arg.defaultValue);
|
|
726
|
-
|
|
701
|
+
if (costSpec) {
|
|
702
|
+
propagateDemandControlDirectives(arg, argDef, subgraph, costSpec);
|
|
703
|
+
}
|
|
727
704
|
}
|
|
728
705
|
if (joinFieldArgs?.requires) {
|
|
729
706
|
subgraphField.applyDirective(subgraph.metadata().requiresDirective(), {'fields': joinFieldArgs.requires});
|
|
@@ -769,7 +746,9 @@ function addSubgraphField({
|
|
|
769
746
|
subgraphField.applyDirective(subgraph.metadata().shareableDirective());
|
|
770
747
|
}
|
|
771
748
|
|
|
772
|
-
|
|
749
|
+
if (costSpec) {
|
|
750
|
+
propagateDemandControlDirectives(field, subgraphField, subgraph, costSpec);
|
|
751
|
+
}
|
|
773
752
|
|
|
774
753
|
return subgraphField;
|
|
775
754
|
}
|
|
@@ -779,13 +758,13 @@ function addSubgraphInputField({
|
|
|
779
758
|
type,
|
|
780
759
|
subgraph,
|
|
781
760
|
joinFieldArgs,
|
|
782
|
-
|
|
761
|
+
costSpec,
|
|
783
762
|
}: {
|
|
784
763
|
field: InputFieldDefinition,
|
|
785
764
|
type: InputObjectType,
|
|
786
765
|
subgraph: Subgraph,
|
|
787
766
|
joinFieldArgs?: JoinFieldDirectiveArguments,
|
|
788
|
-
|
|
767
|
+
costSpec?: CostSpecDefinition,
|
|
789
768
|
}): InputFieldDefinition {
|
|
790
769
|
const copiedType = joinFieldArgs?.type
|
|
791
770
|
? decodeType(joinFieldArgs?.type, subgraph.schema, subgraph.name)
|
|
@@ -794,7 +773,9 @@ function addSubgraphInputField({
|
|
|
794
773
|
const inputField = type.addField(field.name, copiedType);
|
|
795
774
|
inputField.defaultValue = field.defaultValue
|
|
796
775
|
|
|
797
|
-
|
|
776
|
+
if (costSpec) {
|
|
777
|
+
propagateDemandControlDirectives(field, inputField, subgraph, costSpec);
|
|
778
|
+
}
|
|
798
779
|
|
|
799
780
|
return inputField;
|
|
800
781
|
}
|