@apollo/federation-internals 2.9.0 → 2.9.1
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/extractSubgraphsFromSupergraph.js +68 -63
- package/dist/extractSubgraphsFromSupergraph.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/extractSubgraphsFromSupergraph.ts +73 -92
- 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
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
|
|
|
@@ -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
|
}
|
package/src/specs/coreSpec.ts
CHANGED
|
@@ -195,7 +195,8 @@ export type CoreDirectiveArgs = {
|
|
|
195
195
|
url: undefined,
|
|
196
196
|
feature: string,
|
|
197
197
|
as?: string,
|
|
198
|
-
for?: string
|
|
198
|
+
for?: string,
|
|
199
|
+
import: undefined,
|
|
199
200
|
}
|
|
200
201
|
|
|
201
202
|
export type LinkDirectiveArgs = {
|
|
@@ -203,7 +204,7 @@ export type LinkDirectiveArgs = {
|
|
|
203
204
|
feature: undefined,
|
|
204
205
|
as?: string,
|
|
205
206
|
for?: string,
|
|
206
|
-
import?: (string | CoreImport)[]
|
|
207
|
+
import?: (string | CoreImport)[],
|
|
207
208
|
}
|
|
208
209
|
|
|
209
210
|
export type CoreOrLinkDirectiveArgs = CoreDirectiveArgs | LinkDirectiveArgs;
|
|
@@ -539,36 +540,36 @@ export class CoreSpecDefinition extends FeatureDefinition {
|
|
|
539
540
|
return feature.url.version;
|
|
540
541
|
}
|
|
541
542
|
|
|
542
|
-
applyFeatureToSchema(
|
|
543
|
+
applyFeatureToSchema(
|
|
544
|
+
schema: Schema,
|
|
545
|
+
feature: FeatureDefinition,
|
|
546
|
+
as?: string,
|
|
547
|
+
purpose?: CorePurpose,
|
|
548
|
+
imports?: CoreImport[],
|
|
549
|
+
): GraphQLError[] {
|
|
543
550
|
const coreDirective = this.coreDirective(schema);
|
|
544
551
|
const args = {
|
|
545
552
|
[this.urlArgName()]: feature.toString(),
|
|
546
553
|
as,
|
|
547
|
-
} as
|
|
548
|
-
if (
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
const existing = schema.schemaDefinition.appliedDirectivesOf(linkDirectiveDefaultName).find((link) => link.arguments().url === feature.toString());
|
|
557
|
-
if (existing) {
|
|
558
|
-
existing.remove();
|
|
554
|
+
} as CoreOrLinkDirectiveArgs;
|
|
555
|
+
if (purpose) {
|
|
556
|
+
if (this.supportPurposes()) {
|
|
557
|
+
args.for = purpose;
|
|
558
|
+
} else {
|
|
559
|
+
return [new GraphQLError(
|
|
560
|
+
`Cannot apply feature ${feature} with purpose since the schema's @core/@link version does not support it.`
|
|
561
|
+
)];
|
|
562
|
+
}
|
|
559
563
|
}
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
if (this.supportPurposes() && purpose) {
|
|
569
|
-
args.for = purpose;
|
|
564
|
+
if (imports && imports.length > 0) {
|
|
565
|
+
if (this.supportImport()) {
|
|
566
|
+
args.import = imports.map(i => i.as ? i : i.name);
|
|
567
|
+
} else {
|
|
568
|
+
return [new GraphQLError(
|
|
569
|
+
`Cannot apply feature ${feature} with imports since the schema's @core/@link version does not support it.`
|
|
570
|
+
)];
|
|
571
|
+
}
|
|
570
572
|
}
|
|
571
|
-
|
|
572
573
|
schema.schemaDefinition.applyDirective(coreDirective, args);
|
|
573
574
|
return feature.addElementsToSchema(schema);
|
|
574
575
|
}
|
package/src/specs/costSpec.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DirectiveLocation } from 'graphql';
|
|
2
2
|
import { createDirectiveSpecification } from '../directiveAndTypeSpecification';
|
|
3
3
|
import { FeatureDefinition, FeatureDefinitions, FeatureUrl, FeatureVersion } from './coreSpec';
|
|
4
|
-
import { ListType, NonNullType } from '../definitions';
|
|
4
|
+
import { DirectiveDefinition, ListType, NonNullType, Schema } from '../definitions';
|
|
5
5
|
import { registerKnownFeature } from '../knownCoreFeatures';
|
|
6
6
|
import { ARGUMENT_COMPOSITION_STRATEGIES } from '../argumentCompositionStrategies';
|
|
7
7
|
|
|
@@ -41,6 +41,14 @@ export class CostSpecDefinition extends FeatureDefinition {
|
|
|
41
41
|
supergraphSpecification: (fedVersion) => COST_VERSIONS.getMinimumRequiredVersion(fedVersion)
|
|
42
42
|
}));
|
|
43
43
|
}
|
|
44
|
+
|
|
45
|
+
costDirective(schema: Schema): DirectiveDefinition<CostDirectiveArguments> | undefined {
|
|
46
|
+
return this.directive(schema, 'cost');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
listSizeDirective(schema: Schema): DirectiveDefinition<ListSizeDirectiveArguments> | undefined {
|
|
50
|
+
return this.directive(schema, 'listSize');
|
|
51
|
+
}
|
|
44
52
|
}
|
|
45
53
|
|
|
46
54
|
export const COST_VERSIONS = new FeatureDefinitions<CostSpecDefinition>(costIdentity)
|
package/src/supergraphs.ts
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { DocumentNode, GraphQLError } from "graphql";
|
|
2
|
-
import { ErrCoreCheckFailed, FeatureUrl, FeatureVersion } from "./specs/coreSpec";
|
|
3
2
|
import { CoreFeatures, Schema, sourceASTs } from "./definitions";
|
|
3
|
+
import { ErrCoreCheckFailed, FeatureUrl, FeatureVersion } from "./specs/coreSpec";
|
|
4
4
|
import { joinIdentity, JoinSpecDefinition, JOIN_VERSIONS } from "./specs/joinSpec";
|
|
5
|
+
import { CONTEXT_VERSIONS, ContextSpecDefinition } from "./specs/contextSpec";
|
|
6
|
+
import { COST_VERSIONS, costIdentity, CostSpecDefinition } from "./specs/costSpec";
|
|
5
7
|
import { buildSchema, buildSchemaFromAST } from "./buildSchema";
|
|
6
8
|
import { extractSubgraphsNamesAndUrlsFromSupergraph, extractSubgraphsFromSupergraph } from "./extractSubgraphsFromSupergraph";
|
|
7
9
|
import { ERRORS } from "./error";
|
|
@@ -81,11 +83,17 @@ function checkFeatureSupport(coreFeatures: CoreFeatures, supportedFeatures: Set<
|
|
|
81
83
|
}
|
|
82
84
|
}
|
|
83
85
|
|
|
84
|
-
export function validateSupergraph(supergraph: Schema): [
|
|
86
|
+
export function validateSupergraph(supergraph: Schema): [
|
|
87
|
+
CoreFeatures,
|
|
88
|
+
JoinSpecDefinition,
|
|
89
|
+
ContextSpecDefinition | undefined,
|
|
90
|
+
CostSpecDefinition | undefined,
|
|
91
|
+
] {
|
|
85
92
|
const coreFeatures = supergraph.coreFeatures;
|
|
86
93
|
if (!coreFeatures) {
|
|
87
94
|
throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err("Invalid supergraph: must be a core schema");
|
|
88
95
|
}
|
|
96
|
+
|
|
89
97
|
const joinFeature = coreFeatures.getByIdentity(joinIdentity);
|
|
90
98
|
if (!joinFeature) {
|
|
91
99
|
throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err("Invalid supergraph: must use the join spec");
|
|
@@ -95,7 +103,27 @@ export function validateSupergraph(supergraph: Schema): [CoreFeatures, JoinSpecD
|
|
|
95
103
|
throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err(
|
|
96
104
|
`Invalid supergraph: uses unsupported join spec version ${joinFeature.url.version} (supported versions: ${JOIN_VERSIONS.versions().join(', ')})`);
|
|
97
105
|
}
|
|
98
|
-
|
|
106
|
+
|
|
107
|
+
const contextFeature = coreFeatures.getByIdentity(ContextSpecDefinition.identity);
|
|
108
|
+
let contextSpec = undefined;
|
|
109
|
+
if (contextFeature) {
|
|
110
|
+
contextSpec = CONTEXT_VERSIONS.find(contextFeature.url.version);
|
|
111
|
+
if (!contextSpec) {
|
|
112
|
+
throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err(
|
|
113
|
+
`Invalid supergraph: uses unsupported context spec version ${contextFeature.url.version} (supported versions: ${CONTEXT_VERSIONS.versions().join(', ')})`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const costFeature = coreFeatures.getByIdentity(costIdentity);
|
|
118
|
+
let costSpec = undefined;
|
|
119
|
+
if (costFeature) {
|
|
120
|
+
costSpec = COST_VERSIONS.find(costFeature.url.version);
|
|
121
|
+
if (!costSpec) {
|
|
122
|
+
throw ERRORS.INVALID_FEDERATION_SUPERGRAPH.err(
|
|
123
|
+
`Invalid supergraph: uses unsupported cost spec version ${costFeature.url.version} (supported versions: ${COST_VERSIONS.versions().join(', ')})`);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
return [coreFeatures, joinSpec, contextSpec, costSpec];
|
|
99
127
|
}
|
|
100
128
|
|
|
101
129
|
export function isFed1Supergraph(supergraph: Schema): boolean {
|
package/src/values.ts
CHANGED
|
@@ -135,8 +135,10 @@ export function valueEquals(a: any, b: any): boolean {
|
|
|
135
135
|
if (Array.isArray(a)) {
|
|
136
136
|
return Array.isArray(b) && arrayValueEquals(a, b) ;
|
|
137
137
|
}
|
|
138
|
-
|
|
139
|
-
|
|
138
|
+
// Note that typeof null === 'object', so we have to manually rule that out
|
|
139
|
+
// here.
|
|
140
|
+
if (a !== null && typeof a === 'object') {
|
|
141
|
+
return b !== null && typeof b === 'object' && objectEquals(a, b);
|
|
140
142
|
}
|
|
141
143
|
return a === b;
|
|
142
144
|
}
|
|
@@ -224,8 +226,10 @@ function applyDefaultValues(value: any, type: InputType): any {
|
|
|
224
226
|
if (fieldValue === undefined) {
|
|
225
227
|
if (field.defaultValue !== undefined) {
|
|
226
228
|
updated[field.name] = applyDefaultValues(field.defaultValue, field.type);
|
|
227
|
-
} else if (isNonNullType(field.type)) {
|
|
228
|
-
|
|
229
|
+
} else if (!isNonNullType(field.type)) {
|
|
230
|
+
updated[field.name] = null;
|
|
231
|
+
} else {
|
|
232
|
+
throw ERRORS.INVALID_GRAPHQL.err(`Required field "${field.name}" of type ${type} was not provided.`);
|
|
229
233
|
}
|
|
230
234
|
} else {
|
|
231
235
|
updated[field.name] = applyDefaultValues(fieldValue, field.type);
|
|
@@ -249,8 +253,12 @@ export function withDefaultValues(value: any, argument: ArgumentDefinition<any>)
|
|
|
249
253
|
throw buildError(`Cannot compute default value for argument ${argument} as the type is undefined`);
|
|
250
254
|
}
|
|
251
255
|
if (value === undefined) {
|
|
252
|
-
if (argument.defaultValue) {
|
|
256
|
+
if (argument.defaultValue !== undefined) {
|
|
253
257
|
return applyDefaultValues(argument.defaultValue, argument.type);
|
|
258
|
+
} else if (!isNonNullType(argument.type)) {
|
|
259
|
+
return null;
|
|
260
|
+
} else {
|
|
261
|
+
throw ERRORS.INVALID_GRAPHQL.err(`Required argument "${argument.coordinate}" was not provided.`);
|
|
254
262
|
}
|
|
255
263
|
}
|
|
256
264
|
return applyDefaultValues(value, argument.type);
|