@theguild/federation-composition
Version:
Open Source Composition library for Apollo Federation
68 lines (67 loc) • 2.71 kB
JavaScript
import { GraphQLError } from "graphql";
import { TypeKind } from "../../../subgraph/state.js";
const mapIRKindToString = {
[TypeKind.OBJECT]: "Object",
[TypeKind.INTERFACE]: "Interface",
[TypeKind.UNION]: "Union",
[TypeKind.ENUM]: "Enum",
[TypeKind.INPUT_OBJECT]: "InputObject",
[TypeKind.SCALAR]: "Scalar",
[TypeKind.DIRECTIVE]: "Directive",
};
export function TypesOfTheSameKindRule(context) {
const typeToKindWithGraphs = new Map();
const typesWithConflict = new Set();
for (const [graph, state] of context.subgraphStates) {
state.types.forEach((type) => {
const kindToGraphs = typeToKindWithGraphs.get(type.name);
const isInterfaceObject = type.kind === TypeKind.INTERFACE ? type.isInterfaceObject : false;
const graphsValue = {
graphName: context.graphIdToName(graph),
isInterfaceObject,
};
if (kindToGraphs) {
const graphs = kindToGraphs.get(type.kind);
if (graphs) {
graphs.add(graphsValue);
}
else {
kindToGraphs.set(type.kind, new Set([graphsValue]));
}
if (kindToGraphs.size > 1) {
typesWithConflict.add(type.name);
}
}
else {
typeToKindWithGraphs.set(type.name, new Map([[type.kind, new Set([graphsValue])]]));
}
});
}
for (const typeName of typesWithConflict) {
const kindToGraphs = typeToKindWithGraphs.get(typeName);
if (interfaceObjectConditions(kindToGraphs)) {
continue;
}
const groups = Array.from(kindToGraphs.entries()).map(([kind, graphs]) => {
const plural = graphs.size > 1 ? "s" : "";
return `${mapIRKindToString[kind]} Type in subgraph${plural} "${Array.from(graphs)
.map((typeValidationContext) => typeValidationContext.graphName)
.join('", "')}"`;
});
const [first, second, ...rest] = groups;
context.reportError(new GraphQLError(`Type "${typeName}" has mismatched kind: it is defined as ${first} but ${second}${rest.length ? ` and ${rest.join(" and ")}` : ""}`, {
extensions: {
code: "TYPE_KIND_MISMATCH",
},
}));
}
}
function interfaceObjectConditions(kindToGraphs) {
const interfaceTypes = kindToGraphs.get(TypeKind.INTERFACE) || [];
for (const graphTypeValidationContext of interfaceTypes) {
if (graphTypeValidationContext.isInterfaceObject) {
return true;
}
}
return false;
}