UNPKG

@theguild/federation-composition

Version:
163 lines (161 loc) • 6.22 kB
import { Kind, parse } from "graphql"; import { print } from "./graphql/printer.js"; import { transformSupergraphToPublicSchema } from "./graphql/transform-supergraph-to-public-schema.js"; import { sdl as authenticatedSDL } from "./specifications/authenticated.js"; import { sdl as costSDL } from "./specifications/cost.js"; import { sdl as inaccessibleSDL } from "./specifications/inaccessible.js"; import { sdl as joinSDL } from "./specifications/join.js"; import { sdl as linkSDL, printLink } from "./specifications/link.js"; import { sdl as policySDL } from "./specifications/policy.js"; import { sdl as requiresScopesSDL } from "./specifications/requires-scopes.js"; import { sdl as tagSDL } from "./specifications/tag.js"; import { validate } from "./validate.js"; export function composeServices(services, __internal) { const validationResult = validate(services, __internal); if (!validationResult.success) { return { errors: validationResult.errors, }; } const rootTypes = { query: false, mutation: false, subscription: false, }; for (const def of validationResult.supergraph) { if (def.name.value === "Query") { rootTypes.query = true; } else if (def.name.value === "Mutation") { rootTypes.mutation = true; } else if (def.name.value === "Subscription") { rootTypes.subscription = true; } if (rootTypes.query === true && rootTypes.mutation === true && rootTypes.subscription === true) { break; } } const usedTagSpec = validationResult.specs.tag; const usedCostSpec = validationResult.specs.cost; const usedInaccessibleSpec = validationResult.specs.inaccessible; const usedPolicySpec = validationResult.specs.policy; const usedRequiresScopesSpec = validationResult.specs.requiresScopes; const usedAuthenticatedSpec = validationResult.specs.authenticated; let costLinkImports = ""; if (usedCostSpec.used) { const imports = []; if (usedCostSpec.names.cost && usedCostSpec.names.cost !== "cost") { imports.push(createLinkImportValue("@cost", `@${usedCostSpec.names.cost}`)); } if (usedCostSpec.names.listSize) { imports.push(createLinkImportValue("@listSize", usedCostSpec.names.listSize !== "listSize" ? `@${usedCostSpec.names.listSize}` : null)); } if (imports.length) { costLinkImports = `, import: [${imports.join(", ")}]`; } } const federationVersionToJoinVersion = { "v1.0": "v0.3", "v2.0": "v0.3", "v2.1": "v0.3", "v2.2": "v0.3", "v2.3": "v0.3", "v2.4": "v0.3", "v2.5": "v0.3", "v2.6": "v0.3", "v2.7": "v0.4", "v2.8": "v0.5", "v2.9": "v0.5", }; const core = parse(` schema @link(url: "https://specs.apollo.dev/link/v1.0") @link(url: "https://specs.apollo.dev/join/${federationVersionToJoinVersion[validationResult.federationVersion]}", for: EXECUTION) ${usedTagSpec ? '@link(url: "https://specs.apollo.dev/tag/v0.3")' : ""} ${usedCostSpec.used ? `@link(url: "https://specs.apollo.dev/cost/v0.1"${costLinkImports})` : ""} ${usedInaccessibleSpec ? '@link(url: "https://specs.apollo.dev/inaccessible/v0.2", for: SECURITY)' : ""} ${usedPolicySpec ? '@link(url: "https://specs.apollo.dev/policy/v0.1", for: SECURITY)' : ""} ${usedRequiresScopesSpec ? '@link(url: "https://specs.apollo.dev/requiresScopes/v0.1", for: SECURITY)' : ""} ${usedAuthenticatedSpec ? '@link(url: "https://specs.apollo.dev/authenticated/v0.1", for: SECURITY)' : ""} ${validationResult.links.map(printLink).join("\n ")} { ${rootTypes.query ? "query: Query" : ""} ${rootTypes.mutation ? "mutation: Mutation" : ""} ${rootTypes.subscription ? "subscription: Subscription" : ""} } ${joinSDL(validationResult.federationVersion)} ${linkSDL} ${usedTagSpec ? tagSDL : ""} ${usedCostSpec.used ? costSDL({ cost: usedCostSpec.names.cost ?? "cost", listSize: usedCostSpec.names.listSize ?? "listSize", }) : ""} ${usedInaccessibleSpec ? inaccessibleSDL : ""} ${usedPolicySpec ? policySDL : ""} ${usedRequiresScopesSpec ? requiresScopesSDL : ""} ${usedAuthenticatedSpec ? authenticatedSDL : ""} `); const supergraphDocument = { kind: Kind.DOCUMENT, definitions: [...core.definitions, ...validationResult.supergraph], }; return createCompositionSuccessReadCacheContainer(supergraphDocument); } function createLinkImportValue(name, alias) { if (alias) { return `{ name: "${name}", as: "${alias}" }`; } return `"${name}"`; } export function assertCompositionSuccess(compositionResult, message) { if (compositionHasErrors(compositionResult)) { throw new Error(message || "Unexpected test failure"); } } export function assertCompositionFailure(compositionResult, message) { if (!compositionHasErrors(compositionResult)) { throw new Error(message || "Unexpected test failure"); } } export function compositionHasErrors(compositionResult) { return "errors" in compositionResult && !!compositionResult.errors; } export function createCompositionSuccessReadCacheContainer(supergraphDocumentNode) { let supergraphSdl; let publicDocumentNode; let publicSdl; return { supergraphDocumentNode, get supergraphSdl() { if (supergraphSdl == null) { supergraphSdl = print(supergraphDocumentNode); } return supergraphSdl; }, get publicDocumentNode() { if (publicDocumentNode == null) { publicDocumentNode = transformSupergraphToPublicSchema(supergraphDocumentNode); } return publicDocumentNode; }, get publicSdl() { if (publicSdl == null) { publicSdl = print(this.publicDocumentNode); } return publicSdl; }, }; }