UNPKG

@graphql-hive/federation-gateway-audit

Version:
270 lines (265 loc) 7.25 kB
import { composeServices } from '@apollo/composition'; import { parse } from 'graphql'; import { Response } from 'fets'; import { buildSubgraphSchema } from '@apollo/subgraph'; import { createYoga } from 'graphql-yoga'; function getSupergraph(subgraphs) { const result = composeServices( subgraphs.map((subgraph) => ({ name: subgraph.name, typeDefs: 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 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 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 Response.json(testsArray); } }); return id; } }; } function createSubgraph(name, schemaParameters) { let schema; function lazySchema() { if (!schema) { schema = buildSubgraphSchema({ typeDefs: parse(schemaParameters.typeDefs), resolvers: schemaParameters.resolvers }); } return schema; } function lazyYoga() { if (!yoga) { yoga = 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 }; } export { createTest as a, createSubgraph as c, serve as s };