@@ -40,7 +40,7 @@ import { parseSelectionSet } from "./operations";
4040import fs from 'fs' ;
4141import path from 'path' ;
4242import { validateStringContainsBoolean } from "./utils" ;
43- import { CONTEXT_VERSIONS , ContextSpecDefinition , DirectiveDefinition , FeatureUrl , FederationDirectiveName , SchemaElement , errorCauses , isFederationDirectiveDefinedInSchema , printErrors } from "." ;
43+ import { ContextSpecDefinition , CostSpecDefinition , SchemaElement , errorCauses , isFederationDirectiveDefinedInSchema , printErrors } from "." ;
4444
4545function filteredTypes (
4646 supergraph : Schema ,
@@ -194,7 +194,7 @@ function typesUsedInFederationDirective(fieldSet: string | undefined, parentType
194194}
195195
196196export 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 ) ;
198198 const isFed1 = joinSpec . version . equals ( new FeatureVersion ( 0 , 1 ) ) ;
199199 try {
200200 // 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
224224 }
225225
226226 const types = filteredTypes ( supergraph , joinSpec , coreFeatures . coreDefinition ) ;
227- const originalDirectiveNames = getApolloDirectiveNames ( supergraph ) ;
228227 const args : ExtractArguments = {
229228 supergraph,
230229 subgraphs,
231230 joinSpec,
231+ contextSpec,
232+ costSpec,
232233 filteredTypes : types ,
233- originalDirectiveNames,
234234 getSubgraph,
235235 getSubgraphEnumValue,
236236 } ;
@@ -293,8 +293,9 @@ type ExtractArguments = {
293293 supergraph : Schema ,
294294 subgraphs : Subgraphs ,
295295 joinSpec : JoinSpecDefinition ,
296+ contextSpec : ContextSpecDefinition | undefined ,
297+ costSpec : CostSpecDefinition | undefined ,
296298 filteredTypes : NamedType [ ] ,
297- originalDirectiveNames : Record < string , string > ,
298299 getSubgraph : ( application : Directive < any , { graph ?: string } > ) => Subgraph | undefined ,
299300 getSubgraphEnumValue : ( subgraphName : string ) => string
300301}
@@ -352,7 +353,9 @@ function addAllEmptySubgraphTypes(args: ExtractArguments): TypesInfo {
352353 const subgraph = getSubgraph ( application ) ;
353354 assert ( subgraph , ( ) => `Should have found the subgraph for ${ application } ` ) ;
354355 const subgraphType = subgraph . schema . addType ( newNamedType ( type . kind , type . name ) ) ;
355- propagateDemandControlDirectives ( type , subgraphType , subgraph , args . originalDirectiveNames ) ;
356+ if ( args . costSpec ) {
357+ propagateDemandControlDirectives ( type , subgraphType , subgraph , args . costSpec ) ;
358+ }
356359 }
357360 break ;
358361 }
@@ -401,17 +404,8 @@ function addEmptyType<T extends NamedType>(
401404 }
402405 }
403406 }
404-
405- const coreFeatures = supergraph . coreFeatures ;
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 ) ;
415409 if ( supergraphContextDirective ) {
416410 const contextApplications = type . appliedDirectivesOf ( supergraphContextDirective ) ;
417411 // for every application, apply the context directive to the correct subgraph
@@ -438,8 +432,6 @@ function extractObjOrItfContent(args: ExtractArguments, info: TypeInfo<ObjectTyp
438432 const implementsDirective = args . joinSpec . implementsDirective ( args . supergraph ) ;
439433 assert ( implementsDirective , '@join__implements should existing for a fed2 supergraph' ) ;
440434
441- const originalDirectiveNames = args . originalDirectiveNames ;
442-
443435 for ( const { type, subgraphsInfo } of info ) {
444436 const implementsApplications = type . appliedDirectivesOf ( implementsDirective ) ;
445437 for ( const application of implementsApplications ) {
@@ -450,8 +442,10 @@ function extractObjOrItfContent(args: ExtractArguments, info: TypeInfo<ObjectTyp
450442 subgraphInfo . type . addImplementedInterface ( args . interface ) ;
451443 }
452444
453- for ( const { type : subgraphType , subgraph } of subgraphsInfo . values ( ) ) {
454- propagateDemandControlDirectives ( type , subgraphType , subgraph , args . originalDirectiveNames ) ;
445+ if ( args . costSpec ) {
446+ for ( const { type : subgraphType , subgraph } of subgraphsInfo . values ( ) ) {
447+ propagateDemandControlDirectives ( type , subgraphType , subgraph , args . costSpec ) ;
448+ }
455449 }
456450
457451 for ( const field of type . fields ( ) ) {
@@ -460,7 +454,13 @@ function extractObjOrItfContent(args: ExtractArguments, info: TypeInfo<ObjectTyp
460454 // In fed2 subgraph, no @join__field means that the field is in all the subgraphs in which the type is.
461455 const isShareable = isObjectType ( type ) && subgraphsInfo . size > 1 ;
462456 for ( const { type : subgraphType , subgraph } of subgraphsInfo . values ( ) ) {
463- addSubgraphField ( { field, type : subgraphType , subgraph, isShareable, originalDirectiveNames } ) ;
457+ addSubgraphField ( {
458+ field,
459+ type : subgraphType ,
460+ subgraph,
461+ isShareable,
462+ costSpec : args . costSpec
463+ } ) ;
464464 }
465465 } else {
466466 const isShareable = isObjectType ( type )
@@ -478,78 +478,52 @@ function extractObjOrItfContent(args: ExtractArguments, info: TypeInfo<ObjectTyp
478478 }
479479
480480 const { type : subgraphType , subgraph } = subgraphsInfo . get ( joinFieldArgs . graph ) ! ;
481- addSubgraphField ( { field, type : subgraphType , subgraph, isShareable, joinFieldArgs, originalDirectiveNames } ) ;
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+ } ) ;
522488 }
523489 }
524490 }
525491 }
526-
527- return originalDirectiveNames ;
528492}
529493
530494function extractInputObjContent ( args : ExtractArguments , info : TypeInfo < InputObjectType > [ ] ) {
531495 const fieldDirective = args . joinSpec . fieldDirective ( args . supergraph ) ;
532- const originalDirectiveNames = args . originalDirectiveNames ;
533496
534497 for ( const { type, subgraphsInfo } of info ) {
535498 for ( const field of type . fields ( ) ) {
536499 const fieldApplications = field . appliedDirectivesOf ( fieldDirective ) ;
537500 if ( fieldApplications . length === 0 ) {
538501 // In fed2 subgraph, no @join__field means that the field is in all the subgraphs in which the type is.
539502 for ( const { type : subgraphType , subgraph } of subgraphsInfo . values ( ) ) {
540- addSubgraphInputField ( { field, type : subgraphType , subgraph, originalDirectiveNames } ) ;
503+ addSubgraphInputField ( {
504+ field,
505+ type : subgraphType ,
506+ subgraph,
507+ costSpec : args . costSpec
508+ } ) ;
541509 }
542510 } else {
543511 for ( const application of fieldApplications ) {
544- const args = application . arguments ( ) ;
512+ const joinFieldArgs = application . arguments ( ) ;
545513 // We use a @join__field with no graph to indicates when a field in the supergraph does not come
546514 // directly from any subgraph and there is thus nothing to do to "extract" it.
547- if ( ! args . graph ) {
515+ if ( ! joinFieldArgs . graph ) {
548516 continue ;
549517 }
550518
551- const { type : subgraphType , subgraph } = subgraphsInfo . get ( args . graph ) ! ;
552- addSubgraphInputField ( { field, type : subgraphType , subgraph, joinFieldArgs : args , originalDirectiveNames } ) ;
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+ } ) ;
553527 }
554528 }
555529 }
@@ -559,11 +533,12 @@ function extractInputObjContent(args: ExtractArguments, info: TypeInfo<InputObje
559533function extractEnumTypeContent ( args : ExtractArguments , info : TypeInfo < EnumType > [ ] ) {
560534 // This was added in join 0.3, so it can genuinely be undefined.
561535 const enumValueDirective = args . joinSpec . enumValueDirective ( args . supergraph ) ;
562- const originalDirectiveNames = args . originalDirectiveNames ;
563536
564537 for ( const { type, subgraphsInfo } of info ) {
565- for ( const { type : subgraphType , subgraph } of subgraphsInfo . values ( ) ) {
566- propagateDemandControlDirectives ( type , subgraphType , subgraph , originalDirectiveNames ) ;
538+ if ( args . costSpec ) {
539+ for ( const { type : subgraphType , subgraph } of subgraphsInfo . values ( ) ) {
540+ propagateDemandControlDirectives ( type , subgraphType , subgraph , args . costSpec ) ;
541+ }
567542 }
568543
569544 for ( const value of type . values ) {
@@ -678,20 +653,20 @@ function maybeDumpSubgraphSchema(subgraph: Subgraph): string {
678653 }
679654}
680655
681- function propagateDemandControlDirectives ( source : SchemaElement < any , any > , dest : SchemaElement < any , any > , subgraph : Subgraph , originalDirectiveNames ?: Record < string , string > ) {
682- const costDirectiveName = originalDirectiveNames ?. [ FederationDirectiveName . COST ] ;
683- if ( costDirectiveName ) {
684- const costDirective = source . appliedDirectivesOf ( costDirectiveName ) . pop ( ) ;
685- if ( costDirective ) {
686- dest . applyDirective ( subgraph . metadata ( ) . costDirective ( ) . name , costDirective . arguments ( ) ) ;
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 ( ) ) ;
687662 }
688663 }
689664
690- const listSizeDirectiveName = originalDirectiveNames ?. [ FederationDirectiveName . LIST_SIZE ] ;
691- if ( listSizeDirectiveName ) {
692- const listSizeDirective = source . appliedDirectivesOf ( listSizeDirectiveName ) . pop ( ) ;
693- if ( listSizeDirective ) {
694- dest . applyDirective ( subgraph . metadata ( ) . listSizeDirective ( ) . name , listSizeDirective . arguments ( ) ) ;
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 ( ) ) ;
695670 }
696671 }
697672}
@@ -707,14 +682,14 @@ function addSubgraphField({
707682 subgraph,
708683 isShareable,
709684 joinFieldArgs,
710- originalDirectiveNames ,
685+ costSpec ,
711686} : {
712687 field : FieldDefinition < ObjectType | InterfaceType > ,
713688 type : ObjectType | InterfaceType ,
714689 subgraph : Subgraph ,
715690 isShareable : boolean ,
716691 joinFieldArgs ?: JoinFieldDirectiveArguments ,
717- originalDirectiveNames ?: Record < string , string > ,
692+ costSpec ?: CostSpecDefinition ,
718693} ) : FieldDefinition < ObjectType | InterfaceType > {
719694 const copiedFieldType = joinFieldArgs ?. type
720695 ? decodeType ( joinFieldArgs . type , subgraph . schema , subgraph . name )
@@ -723,7 +698,9 @@ function addSubgraphField({
723698 const subgraphField = type . addField ( field . name , copiedFieldType ) ;
724699 for ( const arg of field . arguments ( ) ) {
725700 const argDef = subgraphField . addArgument ( arg . name , copyType ( arg . type ! , subgraph . schema , subgraph . name ) , arg . defaultValue ) ;
726- propagateDemandControlDirectives ( arg , argDef , subgraph , originalDirectiveNames )
701+ if ( costSpec ) {
702+ propagateDemandControlDirectives ( arg , argDef , subgraph , costSpec ) ;
703+ }
727704 }
728705 if ( joinFieldArgs ?. requires ) {
729706 subgraphField . applyDirective ( subgraph . metadata ( ) . requiresDirective ( ) , { 'fields' : joinFieldArgs . requires } ) ;
@@ -769,7 +746,9 @@ function addSubgraphField({
769746 subgraphField . applyDirective ( subgraph . metadata ( ) . shareableDirective ( ) ) ;
770747 }
771748
772- propagateDemandControlDirectives ( field , subgraphField , subgraph , originalDirectiveNames ) ;
749+ if ( costSpec ) {
750+ propagateDemandControlDirectives ( field , subgraphField , subgraph , costSpec ) ;
751+ }
773752
774753 return subgraphField ;
775754}
@@ -779,13 +758,13 @@ function addSubgraphInputField({
779758 type,
780759 subgraph,
781760 joinFieldArgs,
782- originalDirectiveNames ,
761+ costSpec ,
783762} : {
784763 field : InputFieldDefinition ,
785764 type : InputObjectType ,
786765 subgraph : Subgraph ,
787766 joinFieldArgs ?: JoinFieldDirectiveArguments ,
788- originalDirectiveNames ?: Record < string , string >
767+ costSpec ?: CostSpecDefinition ,
789768} ) : InputFieldDefinition {
790769 const copiedType = joinFieldArgs ?. type
791770 ? decodeType ( joinFieldArgs ?. type , subgraph . schema , subgraph . name )
@@ -794,7 +773,9 @@ function addSubgraphInputField({
794773 const inputField = type . addField ( field . name , copiedType ) ;
795774 inputField . defaultValue = field . defaultValue
796775
797- propagateDemandControlDirectives ( field , inputField , subgraph , originalDirectiveNames ) ;
776+ if ( costSpec ) {
777+ propagateDemandControlDirectives ( field , inputField , subgraph , costSpec ) ;
778+ }
798779
799780 return inputField ;
800781}
0 commit comments