@@ -10,7 +10,7 @@ import {
1010 PutObjectCommand ,
1111 S3Client ,
1212} from '@aws-sdk/client-s3' ;
13- import { createSupergraphManager } from '@graphql-hive/apollo' ;
13+ import { createServicesFetcher , createSupergraphManager } from '@graphql-hive/apollo' ;
1414import { graphql } from '../../testkit/gql' ;
1515import { execute } from '../../testkit/graphql' ;
1616import { initSeed } from '../../testkit/seed' ;
@@ -460,6 +460,103 @@ function runArtifactsCDNTests(
460460 }
461461 } ) ;
462462
463+ test . concurrent (
464+ 'createSupergraphManager with schemaVersionId pins to specific version' ,
465+ async ( { expect } ) => {
466+ const endpointBaseUrl = await getBaseEndpoint ( ) ;
467+ const { createOrg } = await initSeed ( ) . createOwner ( ) ;
468+ const { createProject } = await createOrg ( ) ;
469+ const { createTargetAccessToken, createCdnAccess, target } = await createProject (
470+ ProjectType . Federation ,
471+ ) ;
472+ const writeToken = await createTargetAccessToken ( { } ) ;
473+
474+ // Publish V1 Schema
475+ await writeToken
476+ . publishSchema ( {
477+ author : 'Kamil' ,
478+ commit : 'v1' ,
479+ sdl : `type Query { ping: String }` ,
480+ service : 'ping' ,
481+ url : 'http://ping.com' ,
482+ } )
483+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
484+
485+ const cdnAccessResult = await createCdnAccess ( ) ;
486+
487+ // Create manager without pinning to get initial version
488+ const manager = createSupergraphManager ( {
489+ endpoint : endpointBaseUrl + target . id ,
490+ key : cdnAccessResult . secretAccessToken ,
491+ } ) ;
492+
493+ const gateway = new ApolloGateway ( { supergraphSdl : manager } ) ;
494+ const server = new ApolloServer ( { gateway } ) ;
495+
496+ try {
497+ await startStandaloneServer ( server ) ;
498+
499+ // Capture the version ID
500+ const v1VersionId = manager . getSchemaVersionId ( ) ;
501+ expect ( v1VersionId ) . toBeDefined ( ) ;
502+
503+ await server . stop ( ) ;
504+
505+ // Publish V2 Schema with different content
506+ await writeToken
507+ . publishSchema ( {
508+ author : 'Kamil' ,
509+ commit : 'v2' ,
510+ sdl : `type Query { ping: String, pong: String }` ,
511+ service : 'ping' ,
512+ url : 'http://ping.com' ,
513+ } )
514+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
515+
516+ // Create a new manager pinned to V1
517+ const pinnedManager = createSupergraphManager ( {
518+ endpoint : endpointBaseUrl + target . id ,
519+ key : cdnAccessResult . secretAccessToken ,
520+ schemaVersionId : v1VersionId ! ,
521+ } ) ;
522+
523+ const pinnedGateway = new ApolloGateway ( { supergraphSdl : pinnedManager } ) ;
524+ const pinnedServer = new ApolloServer ( { gateway : pinnedGateway } ) ;
525+
526+ try {
527+ const { url } = await startStandaloneServer ( pinnedServer ) ;
528+
529+ // Query the schema - should only have 'ping', not 'pong'
530+ const response = await fetch ( url , {
531+ method : 'POST' ,
532+ headers : {
533+ accept : 'application/json' ,
534+ 'content-type' : 'application/json' ,
535+ } ,
536+ body : JSON . stringify ( {
537+ query : `{ __schema { types { name fields { name } } } }` ,
538+ } ) ,
539+ } ) ;
540+
541+ expect ( response . status ) . toBe ( 200 ) ;
542+ const result = await response . json ( ) ;
543+ const queryType = result . data . __schema . types . find (
544+ ( t : { name : string } ) => t . name === 'Query' ,
545+ ) ;
546+ expect ( queryType . fields ) . toContainEqual ( { name : 'ping' } ) ;
547+ expect ( queryType . fields ) . not . toContainEqual ( { name : 'pong' } ) ;
548+
549+ // Verify the pinned manager returns V1 version ID
550+ expect ( pinnedManager . getSchemaVersionId ( ) ) . toBe ( v1VersionId ) ;
551+ } finally {
552+ await pinnedServer . stop ( ) ;
553+ }
554+ } finally {
555+ // Server already stopped in try block
556+ }
557+ } ,
558+ ) ;
559+
463560 test . concurrent ( 'access versioned SDL artifact with valid credentials' , async ( { expect } ) => {
464561 const { createOrg } = await initSeed ( ) . createOwner ( ) ;
465562 const { createProject } = await createOrg ( ) ;
@@ -1230,6 +1327,78 @@ function runArtifactsCDNTests(
12301327 expect ( contractSupergraphResponse . headers . get ( 'x-hive-schema-version-id' ) ) . toBe ( versionId ) ;
12311328 } ,
12321329 ) ;
1330+
1331+ test . concurrent (
1332+ 'createServicesFetcher with schemaVersionId fetches pinned version' ,
1333+ async ( { expect } ) => {
1334+ const endpointBaseUrl = await getBaseEndpoint ( ) ;
1335+ const { createOrg } = await initSeed ( ) . createOwner ( ) ;
1336+ const { createProject } = await createOrg ( ) ;
1337+ const { createTargetAccessToken, createCdnAccess, target } = await createProject (
1338+ ProjectType . Federation ,
1339+ ) ;
1340+ const writeToken = await createTargetAccessToken ( { } ) ;
1341+
1342+ // Publish V1 Schema
1343+ await writeToken
1344+ . publishSchema ( {
1345+ author : 'Kamil' ,
1346+ commit : 'v1' ,
1347+ sdl : `type Query { ping: String }` ,
1348+ service : 'ping' ,
1349+ url : 'http://ping.com' ,
1350+ } )
1351+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1352+
1353+ // Get V1 version ID
1354+ const v1Version = await writeToken . fetchLatestValidSchema ( ) ;
1355+ const v1VersionId = v1Version . latestValidVersion ?. id ;
1356+ expect ( v1VersionId ) . toBeDefined ( ) ;
1357+
1358+ const cdnAccessResult = await createCdnAccess ( ) ;
1359+
1360+ // Fetch V1 and capture content
1361+ const v1Fetcher = createServicesFetcher ( {
1362+ endpoint : endpointBaseUrl + target . id ,
1363+ key : cdnAccessResult . secretAccessToken ,
1364+ } ) ;
1365+ const v1Result = await v1Fetcher ( ) ;
1366+ expect ( v1Result [ 0 ] . schemaVersionId ) . toBe ( v1VersionId ) ;
1367+
1368+ // Publish V2 Schema with different content
1369+ await writeToken
1370+ . publishSchema ( {
1371+ author : 'Kamil' ,
1372+ commit : 'v2' ,
1373+ sdl : `type Query { ping: String, pong: String }` ,
1374+ service : 'ping' ,
1375+ url : 'http://ping.com' ,
1376+ } )
1377+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1378+
1379+ // Create a pinned fetcher for V1
1380+ const pinnedFetcher = createServicesFetcher ( {
1381+ endpoint : endpointBaseUrl + target . id ,
1382+ key : cdnAccessResult . secretAccessToken ,
1383+ schemaVersionId : v1VersionId ! ,
1384+ } ) ;
1385+
1386+ const pinnedResult = await pinnedFetcher ( ) ;
1387+
1388+ // Should still return V1 content even after V2 was published
1389+ expect ( pinnedResult [ 0 ] . sdl ) . toBe ( v1Result [ 0 ] . sdl ) ;
1390+ expect ( pinnedResult [ 0 ] . sdl ) . not . toContain ( 'pong' ) ;
1391+
1392+ // Latest fetcher should return V2
1393+ const latestFetcher = createServicesFetcher ( {
1394+ endpoint : endpointBaseUrl + target . id ,
1395+ key : cdnAccessResult . secretAccessToken ,
1396+ } ) ;
1397+ const latestResult = await latestFetcher ( ) ;
1398+ expect ( latestResult [ 0 ] . sdl ) . toContain ( 'pong' ) ;
1399+ expect ( latestResult [ 0 ] . schemaVersionId ) . not . toBe ( v1VersionId ) ;
1400+ } ,
1401+ ) ;
12331402 } ) ;
12341403}
12351404
0 commit comments