UNPKG

@graphql-hive/federation-gateway-audit

Version:
274 lines (268 loc) 7.35 kB
'use strict'; var composition = require('@apollo/composition'); var graphql = require('graphql'); var fets = require('fets'); var subgraph = require('@apollo/subgraph'); var graphqlYoga = require('graphql-yoga'); function getSupergraph(subgraphs) { const result = composition.composeServices( subgraphs.map((subgraph) => ({ name: subgraph.name, typeDefs: graphql.parse(subgraph.typeDefs), url: subgraph.url })) ); if (!result.supergraphSdl) { result.errors?.forEach((error) => console.error(error.message)); throw new Error("Failed to compose supergraph"); } return result.supergraphSdl; } function serve(id, subgraphs, tests) { return { id, createRoutes(router) { let subgraphNames = /* @__PURE__ */ new Set(); for (const subgraph of subgraphs) { if (subgraphNames.has(subgraph.name)) { throw new Error(`Duplicate subgraph name ${subgraph.name}`); } subgraphNames.add(subgraph.name); subgraph.createRoutes(id, router); } function serveSupergraph(request) { const supergraph = getSupergraph( subgraphs.map((subgraph) => ({ name: subgraph.name, typeDefs: subgraph.typeDefs, url: `${request.parsedUrl.origin}/${id}/${subgraph.name}` })) ); const response = new fets.Response(supergraph, { status: 200, headers: { "Content-Type": "text/plain", "Cache-Control": "public, max-age=604800, stale-while-revalidate=432000" } }); return response; } router.route({ method: "GET", path: `/${id}/supergraph`, tags: [id], description: "Supergraph SDL endpoint", operationId: "supergraph", schemas: { responses: { 200: { type: "string" } } }, handler(req) { return serveSupergraph(req); } }); router.route({ method: "GET", path: `/${id}/supergraph.graphql`, tags: [id], description: "Supergraph SDL endpoint", operationId: "supergraph.graphql", schemas: { responses: { 200: { type: "string" } } }, handler(req) { return serveSupergraph(req); } }); router.route({ method: "GET", path: `/${id}/subgraphs`, tags: [id], description: "A list of subgraphs with their SDLs, URLs and names", operationId: "subgraphs", schemas: { responses: { 200: { type: "array", items: { type: "object", properties: { name: { type: "string" }, url: { type: "string" }, sdl: { type: "string" } }, required: ["name", "url", "sdl"] } } } }, handler(req) { return fets.Response.json( subgraphs.map((subgraph) => ({ name: subgraph.name, sdl: subgraph.typeDefs, url: `${req.parsedUrl.origin}/${id}/${subgraph.name}` })) ); } }); router.route({ method: "GET", path: `/${id}/tests`, tags: [id], description: "Endpoint with a list of tests", operationId: "tests", schemas: { responses: { 200: { type: "array", items: { type: "object", properties: { query: { type: "string" }, expected: { type: "object", properties: { data: { anyOf: [ { type: "object" }, { type: "null" } ] }, errors: { type: "boolean", description: "Indicates that errors are expected", default: false } }, additionalProperties: false } }, additionalProperties: false, required: ["query", "expected"] } } } }, handler() { const testsArray = Array.isArray(tests) ? tests : tests(); return fets.Response.json(testsArray); } }); return id; } }; } function createSubgraph(name, schemaParameters) { let schema; function lazySchema() { if (!schema) { schema = subgraph.buildSubgraphSchema({ typeDefs: graphql.parse(schemaParameters.typeDefs), resolvers: schemaParameters.resolvers }); } return schema; } function lazyYoga() { if (!yoga) { yoga = graphqlYoga.createYoga({ schema: lazySchema(), graphqlEndpoint: "*", logging: false, maskedErrors: false }); } return yoga; } let yoga; return { createRoutes(testCaseId, router) { router.route({ method: "GET", path: `/${testCaseId}/${name}`, tags: [testCaseId], operationId: "GraphiQL", schemas: { request: { query: { type: "object", properties: { query: { type: "string" } }, additionalProperties: true, required: ["query"] } } }, handler(req) { return lazyYoga()(req); } }); router.route({ method: "POST", path: `/${testCaseId}/${name}`, tags: [testCaseId], operationId: `"${name}" subgraph`, description: "GraphQL endpoint for the subgraph", schemas: { request: { json: { type: "object", properties: { query: { type: "string" }, variables: { type: "object" } }, additionalProperties: true, required: ["query"] } }, responses: { 200: { type: "object", properties: { data: { type: "object" }, errors: { type: "array", items: { type: "object", properties: { message: { type: "string" } }, required: ["message"], additionalProperties: false } } }, required: ["data"], additionalProperties: false } } }, handler(req) { return lazyYoga()(req); } }); }, name, typeDefs: schemaParameters.typeDefs }; } function createTest(query, expected) { return { query, expected }; } exports.createSubgraph = createSubgraph; exports.createTest = createTest; exports.serve = serve;