@theguild/federation-composition
Version:
Open Source Composition library for Apollo Federation
163 lines (161 loc) • 6.22 kB
JavaScript
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;
},
};
}