@@ -10,7 +10,12 @@ import {
1010 PutObjectCommand ,
1111 S3Client ,
1212} from '@aws-sdk/client-s3' ;
13- import { createSupergraphManager } from '@graphql-hive/apollo' ;
13+ import {
14+ createSchemaFetcher ,
15+ createServicesFetcher ,
16+ createSupergraphManager ,
17+ } from '@graphql-hive/apollo' ;
18+ import { createSupergraphSDLFetcher } from '@graphql-hive/core' ;
1419import { graphql } from '../../testkit/gql' ;
1520import { execute } from '../../testkit/graphql' ;
1621import { initSeed } from '../../testkit/seed' ;
@@ -1230,6 +1235,335 @@ function runArtifactsCDNTests(
12301235 expect ( contractSupergraphResponse . headers . get ( 'x-hive-schema-version-id' ) ) . toBe ( versionId ) ;
12311236 } ,
12321237 ) ;
1238+
1239+ test . concurrent (
1240+ 'createSupergraphSDLFetcher extracts schemaVersionId from response' ,
1241+ async ( { expect } ) => {
1242+ const endpointBaseUrl = await getBaseEndpoint ( ) ;
1243+ const { createOrg } = await initSeed ( ) . createOwner ( ) ;
1244+ const { createProject } = await createOrg ( ) ;
1245+ const { createTargetAccessToken, createCdnAccess, target } = await createProject (
1246+ ProjectType . Federation ,
1247+ ) ;
1248+ const writeToken = await createTargetAccessToken ( { } ) ;
1249+
1250+ // Publish Schema
1251+ await writeToken
1252+ . publishSchema ( {
1253+ author : 'Kamil' ,
1254+ commit : 'abc123' ,
1255+ sdl : `type Query { ping: String }` ,
1256+ service : 'ping' ,
1257+ url : 'http://ping.com' ,
1258+ } )
1259+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1260+
1261+ const cdnAccessResult = await createCdnAccess ( ) ;
1262+
1263+ // Use SDK fetcher without version pinning
1264+ const fetcher = createSupergraphSDLFetcher ( {
1265+ endpoint : endpointBaseUrl + target . id ,
1266+ key : cdnAccessResult . secretAccessToken ,
1267+ } ) ;
1268+
1269+ const result = await fetcher ( ) ;
1270+
1271+ // Should extract schemaVersionId from header
1272+ expect ( result . schemaVersionId ) . toBeDefined ( ) ;
1273+ expect ( result . supergraphSdl ) . toContain ( 'Query' ) ;
1274+ expect ( result . id ) . toBeDefined ( ) ;
1275+ } ,
1276+ ) ;
1277+
1278+ test . concurrent (
1279+ 'createSupergraphSDLFetcher with schemaVersionId fetches pinned version' ,
1280+ async ( { expect } ) => {
1281+ const endpointBaseUrl = await getBaseEndpoint ( ) ;
1282+ const { createOrg } = await initSeed ( ) . createOwner ( ) ;
1283+ const { createProject } = await createOrg ( ) ;
1284+ const { createTargetAccessToken, createCdnAccess, target } = await createProject (
1285+ ProjectType . Federation ,
1286+ ) ;
1287+ const writeToken = await createTargetAccessToken ( { } ) ;
1288+
1289+ // Publish V1 Schema
1290+ await writeToken
1291+ . publishSchema ( {
1292+ author : 'Kamil' ,
1293+ commit : 'v1' ,
1294+ sdl : `type Query { ping: String }` ,
1295+ service : 'ping' ,
1296+ url : 'http://ping.com' ,
1297+ } )
1298+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1299+
1300+ // Get V1 version ID
1301+ const v1Version = await writeToken . fetchLatestValidSchema ( ) ;
1302+ const v1VersionId = v1Version . latestValidVersion ?. id ;
1303+ expect ( v1VersionId ) . toBeDefined ( ) ;
1304+
1305+ const cdnAccessResult = await createCdnAccess ( ) ;
1306+
1307+ // Fetch V1 and capture content
1308+ const v1Fetcher = createSupergraphSDLFetcher ( {
1309+ endpoint : endpointBaseUrl + target . id ,
1310+ key : cdnAccessResult . secretAccessToken ,
1311+ } ) ;
1312+ const v1Result = await v1Fetcher ( ) ;
1313+ expect ( v1Result . schemaVersionId ) . toBe ( v1VersionId ) ;
1314+
1315+ // Publish V2 Schema with different content
1316+ await writeToken
1317+ . publishSchema ( {
1318+ author : 'Kamil' ,
1319+ commit : 'v2' ,
1320+ sdl : `type Query { ping: String, pong: String }` ,
1321+ service : 'ping' ,
1322+ url : 'http://ping.com' ,
1323+ } )
1324+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1325+
1326+ // Create a pinned fetcher for V1
1327+ const pinnedFetcher = createSupergraphSDLFetcher ( {
1328+ endpoint : endpointBaseUrl + target . id ,
1329+ key : cdnAccessResult . secretAccessToken ,
1330+ schemaVersionId : v1VersionId ! ,
1331+ } ) ;
1332+
1333+ const pinnedResult = await pinnedFetcher ( ) ;
1334+
1335+ // Should still return V1 content even after V2 was published
1336+ expect ( pinnedResult . supergraphSdl ) . toBe ( v1Result . supergraphSdl ) ;
1337+ expect ( pinnedResult . supergraphSdl ) . not . toContain ( 'pong' ) ;
1338+
1339+ // Latest fetcher should return V2
1340+ const latestFetcher = createSupergraphSDLFetcher ( {
1341+ endpoint : endpointBaseUrl + target . id ,
1342+ key : cdnAccessResult . secretAccessToken ,
1343+ } ) ;
1344+ const latestResult = await latestFetcher ( ) ;
1345+ expect ( latestResult . supergraphSdl ) . toContain ( 'pong' ) ;
1346+ expect ( latestResult . schemaVersionId ) . not . toBe ( v1VersionId ) ;
1347+ } ,
1348+ ) ;
1349+
1350+ test . concurrent (
1351+ 'createSchemaFetcher extracts schemaVersionId from response' ,
1352+ async ( { expect } ) => {
1353+ const endpointBaseUrl = await getBaseEndpoint ( ) ;
1354+ const { createOrg } = await initSeed ( ) . createOwner ( ) ;
1355+ const { createProject } = await createOrg ( ) ;
1356+ const { createTargetAccessToken, createCdnAccess, target } = await createProject (
1357+ ProjectType . Single ,
1358+ ) ;
1359+ const writeToken = await createTargetAccessToken ( { } ) ;
1360+
1361+ // Publish Schema
1362+ await writeToken
1363+ . publishSchema ( {
1364+ author : 'Kamil' ,
1365+ commit : 'abc123' ,
1366+ sdl : `type Query { ping: String }` ,
1367+ } )
1368+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1369+
1370+ const cdnAccessResult = await createCdnAccess ( ) ;
1371+
1372+ // Use SDK fetcher
1373+ const fetcher = createSchemaFetcher ( {
1374+ endpoint : endpointBaseUrl + target . id ,
1375+ key : cdnAccessResult . secretAccessToken ,
1376+ } ) ;
1377+
1378+ const result = await fetcher ( ) ;
1379+
1380+ // Should extract schemaVersionId from header
1381+ expect ( result . schemaVersionId ) . toBeDefined ( ) ;
1382+ expect ( result . sdl ) . toContain ( 'Query' ) ;
1383+ expect ( result . id ) . toBeDefined ( ) ;
1384+ } ,
1385+ ) ;
1386+
1387+ test . concurrent (
1388+ 'createSchemaFetcher with schemaVersionId fetches pinned version' ,
1389+ async ( { expect } ) => {
1390+ const endpointBaseUrl = await getBaseEndpoint ( ) ;
1391+ const { createOrg } = await initSeed ( ) . createOwner ( ) ;
1392+ const { createProject } = await createOrg ( ) ;
1393+ const { createTargetAccessToken, createCdnAccess, target } = await createProject (
1394+ ProjectType . Single ,
1395+ ) ;
1396+ const writeToken = await createTargetAccessToken ( { } ) ;
1397+
1398+ // Publish V1 Schema
1399+ await writeToken
1400+ . publishSchema ( {
1401+ author : 'Kamil' ,
1402+ commit : 'v1' ,
1403+ sdl : `type Query { ping: String }` ,
1404+ } )
1405+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1406+
1407+ // Get V1 version ID
1408+ const v1Version = await writeToken . fetchLatestValidSchema ( ) ;
1409+ const v1VersionId = v1Version . latestValidVersion ?. id ;
1410+ expect ( v1VersionId ) . toBeDefined ( ) ;
1411+
1412+ const cdnAccessResult = await createCdnAccess ( ) ;
1413+
1414+ // Fetch V1 and capture content
1415+ const v1Fetcher = createSchemaFetcher ( {
1416+ endpoint : endpointBaseUrl + target . id ,
1417+ key : cdnAccessResult . secretAccessToken ,
1418+ } ) ;
1419+ const v1Result = await v1Fetcher ( ) ;
1420+ expect ( v1Result . schemaVersionId ) . toBe ( v1VersionId ) ;
1421+
1422+ // Publish V2 Schema with different content
1423+ await writeToken
1424+ . publishSchema ( {
1425+ author : 'Kamil' ,
1426+ commit : 'v2' ,
1427+ sdl : `type Query { ping: String, pong: String }` ,
1428+ } )
1429+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1430+
1431+ // Create a pinned fetcher for V1
1432+ const pinnedFetcher = createSchemaFetcher ( {
1433+ endpoint : endpointBaseUrl + target . id ,
1434+ key : cdnAccessResult . secretAccessToken ,
1435+ schemaVersionId : v1VersionId ! ,
1436+ } ) ;
1437+
1438+ const pinnedResult = await pinnedFetcher ( ) ;
1439+
1440+ // Should still return V1 content even after V2 was published
1441+ expect ( pinnedResult . sdl ) . toBe ( v1Result . sdl ) ;
1442+ expect ( pinnedResult . sdl ) . not . toContain ( 'pong' ) ;
1443+
1444+ // Latest fetcher should return V2
1445+ const latestFetcher = createSchemaFetcher ( {
1446+ endpoint : endpointBaseUrl + target . id ,
1447+ key : cdnAccessResult . secretAccessToken ,
1448+ } ) ;
1449+ const latestResult = await latestFetcher ( ) ;
1450+ expect ( latestResult . sdl ) . toContain ( 'pong' ) ;
1451+ expect ( latestResult . schemaVersionId ) . not . toBe ( v1VersionId ) ;
1452+ } ,
1453+ ) ;
1454+
1455+ test . concurrent (
1456+ 'createServicesFetcher extracts schemaVersionId into each item' ,
1457+ async ( { expect } ) => {
1458+ const endpointBaseUrl = await getBaseEndpoint ( ) ;
1459+ const { createOrg } = await initSeed ( ) . createOwner ( ) ;
1460+ const { createProject } = await createOrg ( ) ;
1461+ const { createTargetAccessToken, createCdnAccess, target } = await createProject (
1462+ ProjectType . Federation ,
1463+ ) ;
1464+ const writeToken = await createTargetAccessToken ( { } ) ;
1465+
1466+ // Publish Schema
1467+ await writeToken
1468+ . publishSchema ( {
1469+ author : 'Kamil' ,
1470+ commit : 'abc123' ,
1471+ sdl : `type Query { ping: String }` ,
1472+ service : 'ping' ,
1473+ url : 'http://ping.com' ,
1474+ } )
1475+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1476+
1477+ const cdnAccessResult = await createCdnAccess ( ) ;
1478+
1479+ // Use SDK fetcher
1480+ const fetcher = createServicesFetcher ( {
1481+ endpoint : endpointBaseUrl + target . id ,
1482+ key : cdnAccessResult . secretAccessToken ,
1483+ } ) ;
1484+
1485+ const result = await fetcher ( ) ;
1486+
1487+ // Should return array with schemaVersionId on each item
1488+ expect ( result ) . toHaveLength ( 1 ) ;
1489+ expect ( result [ 0 ] . schemaVersionId ) . toBeDefined ( ) ;
1490+ expect ( result [ 0 ] . name ) . toBe ( 'ping' ) ;
1491+ expect ( result [ 0 ] . sdl ) . toContain ( 'Query' ) ;
1492+ expect ( result [ 0 ] . id ) . toBeDefined ( ) ;
1493+ } ,
1494+ ) ;
1495+
1496+ test . concurrent (
1497+ 'createServicesFetcher with schemaVersionId fetches pinned version' ,
1498+ async ( { expect } ) => {
1499+ const endpointBaseUrl = await getBaseEndpoint ( ) ;
1500+ const { createOrg } = await initSeed ( ) . createOwner ( ) ;
1501+ const { createProject } = await createOrg ( ) ;
1502+ const { createTargetAccessToken, createCdnAccess, target } = await createProject (
1503+ ProjectType . Federation ,
1504+ ) ;
1505+ const writeToken = await createTargetAccessToken ( { } ) ;
1506+
1507+ // Publish V1 Schema
1508+ await writeToken
1509+ . publishSchema ( {
1510+ author : 'Kamil' ,
1511+ commit : 'v1' ,
1512+ sdl : `type Query { ping: String }` ,
1513+ service : 'ping' ,
1514+ url : 'http://ping.com' ,
1515+ } )
1516+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1517+
1518+ // Get V1 version ID
1519+ const v1Version = await writeToken . fetchLatestValidSchema ( ) ;
1520+ const v1VersionId = v1Version . latestValidVersion ?. id ;
1521+ expect ( v1VersionId ) . toBeDefined ( ) ;
1522+
1523+ const cdnAccessResult = await createCdnAccess ( ) ;
1524+
1525+ // Fetch V1 and capture content
1526+ const v1Fetcher = createServicesFetcher ( {
1527+ endpoint : endpointBaseUrl + target . id ,
1528+ key : cdnAccessResult . secretAccessToken ,
1529+ } ) ;
1530+ const v1Result = await v1Fetcher ( ) ;
1531+ expect ( v1Result [ 0 ] . schemaVersionId ) . toBe ( v1VersionId ) ;
1532+
1533+ // Publish V2 Schema with different content
1534+ await writeToken
1535+ . publishSchema ( {
1536+ author : 'Kamil' ,
1537+ commit : 'v2' ,
1538+ sdl : `type Query { ping: String, pong: String }` ,
1539+ service : 'ping' ,
1540+ url : 'http://ping.com' ,
1541+ } )
1542+ . then ( r => r . expectNoGraphQLErrors ( ) ) ;
1543+
1544+ // Create a pinned fetcher for V1
1545+ const pinnedFetcher = createServicesFetcher ( {
1546+ endpoint : endpointBaseUrl + target . id ,
1547+ key : cdnAccessResult . secretAccessToken ,
1548+ schemaVersionId : v1VersionId ! ,
1549+ } ) ;
1550+
1551+ const pinnedResult = await pinnedFetcher ( ) ;
1552+
1553+ // Should still return V1 content even after V2 was published
1554+ expect ( pinnedResult [ 0 ] . sdl ) . toBe ( v1Result [ 0 ] . sdl ) ;
1555+ expect ( pinnedResult [ 0 ] . sdl ) . not . toContain ( 'pong' ) ;
1556+
1557+ // Latest fetcher should return V2
1558+ const latestFetcher = createServicesFetcher ( {
1559+ endpoint : endpointBaseUrl + target . id ,
1560+ key : cdnAccessResult . secretAccessToken ,
1561+ } ) ;
1562+ const latestResult = await latestFetcher ( ) ;
1563+ expect ( latestResult [ 0 ] . sdl ) . toContain ( 'pong' ) ;
1564+ expect ( latestResult [ 0 ] . schemaVersionId ) . not . toBe ( v1VersionId ) ;
1565+ } ,
1566+ ) ;
12331567 } ) ;
12341568}
12351569
0 commit comments