@@ -79,6 +79,7 @@ import {
7979 FederationTypeName ,
8080 FEDERATION1_TYPES ,
8181 FEDERATION1_DIRECTIVES ,
82+ FederationSpecDefinition ,
8283} from "./specs/federationSpec" ;
8384import { defaultPrintOptions , PrintOptions as PrintOptions , printSchema } from "./print" ;
8485import { createObjectTypeSpecification , createScalarTypeSpecification , createUnionTypeSpecification } from "./directiveAndTypeSpecification" ;
@@ -93,10 +94,18 @@ import {
9394
9495const linkSpec = LINK_VERSIONS . latest ( ) ;
9596const tagSpec = TAG_VERSIONS . latest ( ) ;
96- const federationSpec = FEDERATION_VERSIONS . latest ( ) ;
97+ const federationSpec = ( version ?: FeatureVersion ) : FederationSpecDefinition => {
98+ if ( ! version ) return FEDERATION_VERSIONS . latest ( ) ;
99+ const spec = FEDERATION_VERSIONS . find ( version ) ;
100+ assert ( spec , `Federation spec version ${ version } is not known` ) ;
101+ return spec ;
102+ } ;
103+
97104// Some users rely on auto-expanding fed v1 graphs with fed v2 directives. While technically we should only expand @tag
98105// directive from v2 definitions, we will continue expanding other directives (up to v2.4) to ensure backwards compatibility.
99- const autoExpandedFederationSpec = FEDERATION_VERSIONS . find ( new FeatureVersion ( 2 , 4 ) ) ! ;
106+ const autoExpandedFederationSpec = federationSpec ( new FeatureVersion ( 2 , 4 ) ) ;
107+
108+ const latestFederationSpec = federationSpec ( ) ;
100109
101110// We don't let user use this as a subgraph name. That allows us to use it in `query graphs` to name the source of roots
102111// in the "federated query graph" without worrying about conflict (see `FEDERATED_GRAPH_ROOT_SOURCE` in `querygraph.ts`).
@@ -601,7 +610,7 @@ export class FederationMetadata {
601610 }
602611
603612 federationFeature ( ) : CoreFeature | undefined {
604- return this . schema . coreFeatures ?. getByIdentity ( federationSpec . identity ) ;
613+ return this . schema . coreFeatures ?. getByIdentity ( latestFederationSpec . identity ) ;
605614 }
606615
607616 private externalTester ( ) : ExternalTester {
@@ -663,7 +672,7 @@ export class FederationMetadata {
663672 if ( this . isFed2Schema ( ) ) {
664673 const coreFeatures = this . schema . coreFeatures ;
665674 assert ( coreFeatures , 'Schema should be a core schema' ) ;
666- const federationFeature = coreFeatures . getByIdentity ( federationSpec . identity ) ;
675+ const federationFeature = coreFeatures . getByIdentity ( latestFederationSpec . identity ) ;
667676 assert ( federationFeature , 'Schema should have the federation feature' ) ;
668677 return federationFeature . directiveNameInSchema ( name ) ;
669678 } else {
@@ -685,7 +694,7 @@ export class FederationMetadata {
685694 if ( this . isFed2Schema ( ) ) {
686695 const coreFeatures = this . schema . coreFeatures ;
687696 assert ( coreFeatures , 'Schema should be a core schema' ) ;
688- const federationFeature = coreFeatures . getByIdentity ( federationSpec . identity ) ;
697+ const federationFeature = coreFeatures . getByIdentity ( latestFederationSpec . identity ) ;
689698 assert ( federationFeature , 'Schema should have the federation feature' ) ;
690699 return federationFeature . typeNameInSchema ( name ) ;
691700 } else {
@@ -1190,7 +1199,7 @@ function findUnusedNamedForLinkDirective(schema: Schema): string | undefined {
11901199 }
11911200}
11921201
1193- export function setSchemaAsFed2Subgraph ( schema : Schema ) {
1202+ export function setSchemaAsFed2Subgraph ( schema : Schema , useLatest : boolean = false ) {
11941203 let core = schema . coreFeatures ;
11951204 let spec : CoreSpecDefinition ;
11961205 if ( core ) {
@@ -1209,11 +1218,16 @@ export function setSchemaAsFed2Subgraph(schema: Schema) {
12091218 assert ( core , 'Schema should now be a core schema' ) ;
12101219 }
12111220
1212- assert ( ! core . getByIdentity ( federationSpec . identity ) , 'Schema already set as a federation subgraph' ) ;
1221+ const fedSpec = useLatest ? latestFederationSpec : autoExpandedFederationSpec ;
1222+
1223+ assert ( ! core . getByIdentity ( fedSpec . identity ) , 'Schema already set as a federation subgraph' ) ;
12131224 schema . schemaDefinition . applyDirective (
12141225 core . coreItself . nameInSchema ,
12151226 {
1216- url : federationSpec . url . toString ( ) ,
1227+ // note that there is a mismatch between url and directives that are imported. This is because
1228+ // we want to maintain backward compatibility for those who have already upgraded and we had been upgrading the url to
1229+ // latest, but we never automatically import directives that exist past 2.4
1230+ url : fedSpec . url . toString ( ) ,
12171231 import : autoExpandedFederationSpec . directiveSpecs ( ) . map ( ( spec ) => `@${ spec . name } ` ) ,
12181232 }
12191233 ) ;
@@ -1226,29 +1240,33 @@ export function setSchemaAsFed2Subgraph(schema: Schema) {
12261240// This is the full @link declaration as added by `asFed2SubgraphDocument`. It's here primarily for uses by tests that print and match
12271241// subgraph schema to avoid having to update 20+ tests every time we use a new directive or the order of import changes ...
12281242export const FEDERATION2_LINK_WITH_FULL_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject", "@authenticated", "@requiresScopes", "@policy", "@sourceAPI", "@sourceType", "@sourceField"])' ;
1229- // This is the full @link declaration that is added when upgrading fed v1 subgraphs to v2 version. It should only be used by tests.
1243+
1244+ // This is the federation @link for tests that go through the asFed2SubgraphDocument function.
12301245export const FEDERATION2_LINK_WITH_AUTO_EXPANDED_IMPORTS = '@link(url: "https://specs.apollo.dev/federation/v2.7", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"])' ;
12311246
1247+ // This is the federation @link for tests that go through the SchemaUpgrader.
1248+ export const FEDERATION2_LINK_WITH_AUTO_EXPANDED_IMPORTS_UPGRADED = '@link(url: "https://specs.apollo.dev/federation/v2.4", import: ["@key", "@requires", "@provides", "@external", "@tag", "@extends", "@shareable", "@inaccessible", "@override", "@composeDirective", "@interfaceObject"])' ;
1249+
12321250/**
12331251 * Given a document that is assumed to _not_ be a fed2 schema (it does not have a `@link` to the federation spec),
12341252 * returns an equivalent document that `@link` to the last known federation spec.
12351253 *
12361254 * @param document - the document to "augment".
1237- * @param options.addAsSchemaExtension - defines whethere the added `@link` is added as a schema extension (`extend schema`) or
1255+ * @param options.addAsSchemaExtension - defines whether the added `@link` is added as a schema extension (`extend schema`) or
12381256 * added to the schema definition. Defaults to `true` (added as an extension), as this mimics what we tends to write manually.
12391257 * @param options.includeAllImports - defines whether we should auto import ALL latest federation v2 directive definitions or include
12401258 * only limited set of directives (i.e. federation v2.4 definitions)
12411259 */
12421260export function asFed2SubgraphDocument ( document : DocumentNode , options ?: { addAsSchemaExtension ?: boolean , includeAllImports ?: boolean } ) : DocumentNode {
1243- const importedDirectives = options ?. includeAllImports ? federationSpec . directiveSpecs ( ) : autoExpandedFederationSpec . directiveSpecs ( ) ;
1261+ const importedDirectives = options ?. includeAllImports ? latestFederationSpec . directiveSpecs ( ) : autoExpandedFederationSpec . directiveSpecs ( ) ;
12441262 const directiveToAdd : ConstDirectiveNode = ( {
12451263 kind : Kind . DIRECTIVE ,
12461264 name : { kind : Kind . NAME , value : linkDirectiveDefaultName } ,
12471265 arguments : [
12481266 {
12491267 kind : Kind . ARGUMENT ,
12501268 name : { kind : Kind . NAME , value : 'url' } ,
1251- value : { kind : Kind . STRING , value : federationSpec . url . toString ( ) }
1269+ value : { kind : Kind . STRING , value : latestFederationSpec . url . toString ( ) }
12521270 } ,
12531271 {
12541272 kind : Kind . ARGUMENT ,
@@ -1374,7 +1392,7 @@ export function buildSubgraph(
13741392
13751393export function newEmptyFederation2Schema ( config ?: SchemaConfig ) : Schema {
13761394 const schema = new Schema ( new FederationBlueprint ( true ) , config ) ;
1377- setSchemaAsFed2Subgraph ( schema ) ;
1395+ setSchemaAsFed2Subgraph ( schema , true ) ;
13781396 return schema ;
13791397}
13801398
0 commit comments