@apollo/federation-internals
Version:
Apollo Federation internal utilities
863 lines • 42.1 kB
JavaScript
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.extractSubgraphsFromSupergraph = exports.extractSubgraphsNamesAndUrlsFromSupergraph = void 0;
const definitions_1 = require("./definitions");
const federation_1 = require("./federation");
const coreSpec_1 = require("./specs/coreSpec");
const federation_2 = require("./federation");
const utils_1 = require("./utils");
const supergraphs_1 = require("./supergraphs");
const buildSchema_1 = require("./buildSchema");
const types_1 = require("./types");
const print_1 = require("./print");
const operations_1 = require("./operations");
const fs_1 = __importDefault(require("fs"));
const path_1 = __importDefault(require("path"));
const utils_2 = require("./utils");
const _1 = require(".");
function filteredTypes(supergraph, joinSpec, coreSpec) {
return supergraph.types().filter(t => !joinSpec.isSpecType(t) && !coreSpec.isSpecType(t));
}
function extractSubgraphsNamesAndUrlsFromSupergraph(supergraph) {
const [_, joinSpec] = (0, supergraphs_1.validateSupergraph)(supergraph);
const [subgraphs] = collectEmptySubgraphs(supergraph, joinSpec);
return subgraphs.values().map(subgraph => { return { name: subgraph.name, url: subgraph.url }; });
}
exports.extractSubgraphsNamesAndUrlsFromSupergraph = extractSubgraphsNamesAndUrlsFromSupergraph;
function collectEmptySubgraphs(supergraph, joinSpec) {
const subgraphs = new federation_2.Subgraphs();
const graphDirective = joinSpec.graphDirective(supergraph);
const graphEnum = joinSpec.graphEnum(supergraph);
const graphEnumNameToSubgraphName = new Map();
for (const value of graphEnum.values) {
const graphApplications = value.appliedDirectivesOf(graphDirective);
if (!graphApplications.length) {
throw new Error(`Value ${value} of join__Graph enum has no @join__graph directive`);
}
const info = graphApplications[0].arguments();
const subgraph = new federation_2.Subgraph(info.name, info.url, (0, federation_1.newEmptyFederation2Schema)());
subgraphs.add(subgraph);
graphEnumNameToSubgraphName.set(value.name, info.name);
}
return [subgraphs, graphEnumNameToSubgraphName];
}
class SubgraphExtractionError {
constructor(originalError, subgraph) {
this.originalError = originalError;
this.subgraph = subgraph;
}
}
function collectFieldReachableTypesForSubgraph(supergraph, subgraphName, addReachableType, fieldInfoInSubgraph, typeInfoInSubgraph) {
const seenTypes = new Set();
const stack = supergraph.schemaDefinition.roots().map((root) => root.type);
for (const type of supergraph.types()) {
const { isEntityWithKeyInSubgraph, typesInFederationDirectives } = typeInfoInSubgraph(type, subgraphName);
if (isEntityWithKeyInSubgraph) {
stack.push(type);
}
typesInFederationDirectives.forEach((t) => stack.push(t));
}
while (stack.length > 0) {
const type = stack.pop();
addReachableType(type);
if (seenTypes.has(type.name)) {
continue;
}
seenTypes.add(type.name);
switch (type.kind) {
case 'InterfaceType':
type.allImplementations().forEach((t) => stack.push(t));
case 'ObjectType':
type.interfaces().forEach((t) => stack.push(t));
for (const field of type.fields()) {
const { isInSubgraph, typesInFederationDirectives } = fieldInfoInSubgraph(field, subgraphName);
if (isInSubgraph) {
field.arguments().forEach((arg) => stack.push((0, definitions_1.baseType)(arg.type)));
stack.push((0, definitions_1.baseType)(field.type));
typesInFederationDirectives.forEach((t) => stack.push(t));
}
}
break;
case 'InputObjectType':
for (const field of type.fields()) {
const { isInSubgraph, typesInFederationDirectives } = fieldInfoInSubgraph(field, subgraphName);
if (isInSubgraph) {
stack.push((0, definitions_1.baseType)(field.type));
typesInFederationDirectives.forEach((t) => stack.push(t));
}
}
break;
case 'UnionType':
type.members().forEach((m) => stack.push(m.type));
break;
}
}
for (const directive of supergraph.directives()) {
if (!directive.hasExecutableLocations()) {
continue;
}
directive.arguments().forEach((arg) => stack.push((0, definitions_1.baseType)(arg.type)));
}
}
function collectFieldReachableTypesForAllSubgraphs(supergraph, allSubgraphs, fieldInfoInSubgraph, typeInfoInSubgraph) {
const reachableTypesBySubgraphs = new Map();
for (const subgraphName of allSubgraphs) {
const reachableTypes = new Set();
collectFieldReachableTypesForSubgraph(supergraph, subgraphName, (t) => reachableTypes.add(t.name), fieldInfoInSubgraph, typeInfoInSubgraph);
reachableTypesBySubgraphs.set(subgraphName, reachableTypes);
}
return reachableTypesBySubgraphs;
}
function typesUsedInFederationDirective(fieldSet, parentType) {
if (!fieldSet) {
return [];
}
const usedTypes = [];
(0, operations_1.parseSelectionSet)({
parentType,
source: fieldSet,
fieldAccessor: (type, fieldName) => {
const field = type.field(fieldName);
if (field) {
usedTypes.push((0, definitions_1.baseType)(field.type));
}
return field;
},
validate: false,
});
return usedTypes;
}
function extractSubgraphsFromSupergraph(supergraph, validateExtractedSubgraphs = true) {
const [coreFeatures, joinSpec, contextSpec, costSpec] = (0, supergraphs_1.validateSupergraph)(supergraph);
const isFed1 = joinSpec.version.equals(new coreSpec_1.FeatureVersion(0, 1));
try {
const [subgraphs, graphEnumNameToSubgraphName] = collectEmptySubgraphs(supergraph, joinSpec);
const getSubgraph = (application) => {
const graph = application.arguments().graph;
if (!graph) {
return undefined;
}
const subgraphName = graphEnumNameToSubgraphName.get(graph);
(0, utils_1.assert)(subgraphName, () => `Invalid graph name ${graph} found in ${application} on ${application.parent}: does not match a graph defined in the @join__Graph enum`);
const subgraph = subgraphs.get(subgraphName);
(0, utils_1.assert)(subgraph, 'All subgraphs should have been created by `collectEmptySubgraphs`');
return subgraph;
};
const subgraphNameToGraphEnumValue = new Map();
for (const [k, v] of graphEnumNameToSubgraphName.entries()) {
subgraphNameToGraphEnumValue.set(v, k);
}
const getSubgraphEnumValue = (subgraphName) => {
const enumValue = subgraphNameToGraphEnumValue.get(subgraphName);
(0, utils_1.assert)(enumValue, () => `Invalid subgraph name ${subgraphName} found: does not match a subgraph defined in the @join__Graph enum`);
return enumValue;
};
const types = filteredTypes(supergraph, joinSpec, coreFeatures.coreDefinition);
const args = {
supergraph,
subgraphs,
joinSpec,
contextSpec,
costSpec,
filteredTypes: types,
getSubgraph,
getSubgraphEnumValue,
};
if (isFed1) {
extractSubgraphsFromFed1Supergraph(args);
}
else {
extractSubgraphsFromFed2Supergraph(args);
}
for (const subgraph of subgraphs) {
if (validateExtractedSubgraphs) {
try {
subgraph.validate();
}
catch (e) {
throw new SubgraphExtractionError(e, subgraph);
}
}
else {
subgraph.assumeValid();
}
}
return [subgraphs, subgraphNameToGraphEnumValue];
}
catch (e) {
let error = e;
let subgraph = undefined;
if (e instanceof SubgraphExtractionError) {
error = e.originalError;
subgraph = e.subgraph;
}
const impacted = subgraph ? `subgraph "${subgraph.name}"` : 'subgraphs';
if (isFed1) {
const msg = `Error extracting ${impacted} from the supergraph: this might be due to errors in subgraphs that were mistakenly ignored by federation 0.x versions but are rejected by federation 2.\n`
+ 'Please try composing your subgraphs with federation 2: this should help precisely pinpoint the problems and, once fixed, generate a correct federation 2 supergraph';
throw new Error(`${msg}.\n\nDetails:\n${errorToString(error)}`);
}
else {
const msg = `Unexpected error extracting ${impacted} from the supergraph: this is either a bug, or the supergraph has been corrupted`;
const dumpMsg = subgraph ? '\n\n' + maybeDumpSubgraphSchema(subgraph) : '';
throw new Error(`${msg}.\n\nDetails:\n${errorToString(error)}${dumpMsg}`);
}
}
}
exports.extractSubgraphsFromSupergraph = extractSubgraphsFromSupergraph;
function addAllEmptySubgraphTypes(args) {
const { supergraph, joinSpec, filteredTypes, getSubgraph, } = args;
const typeDirective = joinSpec.typeDirective(supergraph);
const objOrItfTypes = [];
const inputObjTypes = [];
const enumTypes = [];
const unionTypes = [];
for (const type of filteredTypes) {
const typeApplications = type.appliedDirectivesOf(typeDirective);
switch (type.kind) {
case 'InterfaceType':
case 'ObjectType':
objOrItfTypes.push({ type, subgraphsInfo: addEmptyType(type, type.appliedDirectivesOf(typeDirective), args) });
break;
case 'InputObjectType':
inputObjTypes.push({ type, subgraphsInfo: addEmptyType(type, type.appliedDirectivesOf(typeDirective), args) });
break;
case 'EnumType':
enumTypes.push({ type, subgraphsInfo: addEmptyType(type, type.appliedDirectivesOf(typeDirective), args) });
break;
case 'UnionType':
unionTypes.push({ type, subgraphsInfo: addEmptyType(type, type.appliedDirectivesOf(typeDirective), args) });
break;
case 'ScalarType':
for (const application of typeApplications) {
const subgraph = getSubgraph(application);
(0, utils_1.assert)(subgraph, () => `Should have found the subgraph for ${application}`);
const subgraphType = subgraph.schema.addType((0, definitions_1.newNamedType)(type.kind, type.name));
if (args.costSpec) {
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec);
}
}
break;
}
}
return {
objOrItfTypes,
inputObjTypes,
enumTypes,
unionTypes,
};
}
function addEmptyType(type, typeApplications, args) {
var _a;
const { supergraph, getSubgraph, getSubgraphEnumValue } = args;
(0, utils_1.assert)(typeApplications.length > 0, `Missing @join__type on ${type}`);
const subgraphsInfo = new Map();
for (const application of typeApplications) {
const { graph, key, extension, resolvable, isInterfaceObject } = application.arguments();
let subgraphInfo = subgraphsInfo.get(graph);
if (!subgraphInfo) {
const subgraph = getSubgraph(application);
(0, utils_1.assert)(subgraph, () => `Should have found the subgraph for ${application}`);
const kind = isInterfaceObject ? 'ObjectType' : type.kind;
const subgraphType = subgraph.schema.addType((0, definitions_1.newNamedType)(kind, type.name));
if (isInterfaceObject) {
subgraphType.applyDirective('interfaceObject');
}
subgraphInfo = { type: subgraphType, subgraph };
subgraphsInfo.set(graph, subgraphInfo);
}
if (key) {
const directive = subgraphInfo.type.applyDirective('key', { 'fields': key, resolvable });
if (extension) {
directive.setOfExtension(subgraphInfo.type.newExtension());
}
}
}
const supergraphContextDirective = (_a = args.contextSpec) === null || _a === void 0 ? void 0 : _a.contextDirective(supergraph);
if (supergraphContextDirective) {
const contextApplications = type.appliedDirectivesOf(supergraphContextDirective);
for (const application of contextApplications) {
const { name } = application.arguments();
const match = name.match(/^(.*)__([A-Za-z]\w*)$/);
const graph = match ? match[1] : undefined;
const context = match ? match[2] : undefined;
(0, utils_1.assert)(graph, `Invalid context name ${name} found in ${application} on ${application.parent}: does not match the expected pattern`);
const subgraphInfo = subgraphsInfo.get(getSubgraphEnumValue(graph));
const contextDirective = subgraphInfo === null || subgraphInfo === void 0 ? void 0 : subgraphInfo.subgraph.metadata().contextDirective();
if (subgraphInfo && contextDirective && (0, _1.isFederationDirectiveDefinedInSchema)(contextDirective)) {
subgraphInfo.type.applyDirective(contextDirective, { name: context });
}
}
}
return subgraphsInfo;
}
function extractObjOrItfContent(args, info) {
const fieldDirective = args.joinSpec.fieldDirective(args.supergraph);
const implementsDirective = args.joinSpec.implementsDirective(args.supergraph);
(0, utils_1.assert)(implementsDirective, '@join__implements should existing for a fed2 supergraph');
for (const { type, subgraphsInfo } of info) {
const implementsApplications = type.appliedDirectivesOf(implementsDirective);
for (const application of implementsApplications) {
const args = application.arguments();
const subgraphInfo = subgraphsInfo.get(args.graph);
subgraphInfo.type.addImplementedInterface(args.interface);
}
if (args.costSpec) {
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) {
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec);
}
}
for (const field of type.fields()) {
const fieldApplications = field.appliedDirectivesOf(fieldDirective);
if (fieldApplications.length === 0) {
const isShareable = (0, definitions_1.isObjectType)(type) && subgraphsInfo.size > 1;
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) {
addSubgraphField({
field,
type: subgraphType,
subgraph,
isShareable,
costSpec: args.costSpec
});
}
}
else {
const isShareable = (0, definitions_1.isObjectType)(type)
&& fieldApplications.filter((application) => {
const args = application.arguments();
return !args.external && !args.usedOverridden;
}).length > 1;
for (const application of fieldApplications) {
const joinFieldArgs = application.arguments();
if (!joinFieldArgs.graph) {
continue;
}
const { type: subgraphType, subgraph } = subgraphsInfo.get(joinFieldArgs.graph);
addSubgraphField({
field,
type: subgraphType,
subgraph, isShareable,
joinFieldArgs,
costSpec: args.costSpec
});
}
}
}
}
}
function extractInputObjContent(args, info) {
const fieldDirective = args.joinSpec.fieldDirective(args.supergraph);
for (const { type, subgraphsInfo } of info) {
for (const field of type.fields()) {
const fieldApplications = field.appliedDirectivesOf(fieldDirective);
if (fieldApplications.length === 0) {
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) {
addSubgraphInputField({
field,
type: subgraphType,
subgraph,
costSpec: args.costSpec
});
}
}
else {
for (const application of fieldApplications) {
const joinFieldArgs = application.arguments();
if (!joinFieldArgs.graph) {
continue;
}
const { type: subgraphType, subgraph } = subgraphsInfo.get(joinFieldArgs.graph);
addSubgraphInputField({
field,
type: subgraphType,
subgraph,
joinFieldArgs,
costSpec: args.costSpec
});
}
}
}
}
}
function extractEnumTypeContent(args, info) {
const enumValueDirective = args.joinSpec.enumValueDirective(args.supergraph);
for (const { type, subgraphsInfo } of info) {
if (args.costSpec) {
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) {
propagateDemandControlDirectives(type, subgraphType, subgraph, args.costSpec);
}
}
for (const value of type.values) {
const enumValueApplications = enumValueDirective ? value.appliedDirectivesOf(enumValueDirective) : [];
if (enumValueApplications.length === 0) {
for (const { type: subgraphType } of subgraphsInfo.values()) {
subgraphType.addValue(value.name);
}
}
else {
for (const application of enumValueApplications) {
const args = application.arguments();
const { type: subgraphType } = subgraphsInfo.get(args.graph);
subgraphType.addValue(value.name);
}
}
}
}
}
function extractUnionTypeContent(args, info) {
const unionMemberDirective = args.joinSpec.unionMemberDirective(args.supergraph);
for (const { type, subgraphsInfo } of info) {
const unionMemberApplications = unionMemberDirective ? type.appliedDirectivesOf(unionMemberDirective) : [];
if (unionMemberApplications.length === 0) {
for (const { type: subgraphType, subgraph } of subgraphsInfo.values()) {
for (const member of type.types()) {
const subgraphMember = subgraph.schema.type(member.name);
if (subgraphMember) {
subgraphType.addType(subgraphMember);
}
}
}
}
else {
for (const application of unionMemberApplications) {
const args = application.arguments();
const { type: subgraphType, subgraph } = subgraphsInfo.get(args.graph);
subgraphType.addType(subgraph.schema.type(args.member));
}
}
}
}
function extractSubgraphsFromFed2Supergraph(args) {
const { objOrItfTypes, inputObjTypes, enumTypes, unionTypes, } = addAllEmptySubgraphTypes(args);
extractObjOrItfContent(args, objOrItfTypes);
extractInputObjContent(args, inputObjTypes);
extractEnumTypeContent(args, enumTypes);
extractUnionTypeContent(args, unionTypes);
const allExecutableDirectives = args.supergraph.directives().filter((def) => def.hasExecutableLocations());
for (const subgraph of args.subgraphs) {
(0, federation_1.removeInactiveProvidesAndRequires)(subgraph.schema);
removeUnusedTypesFromSubgraph(subgraph.schema);
for (const definition of allExecutableDirectives) {
(0, definitions_1.copyDirectiveDefinitionToSchema)({
definition,
schema: subgraph.schema,
copyDirectiveApplicationsInArguments: false,
locationFilter: (loc) => (0, definitions_1.isExecutableDirectiveLocation)(loc),
});
}
}
}
const DEBUG_SUBGRAPHS_ENV_VARIABLE_NAME = 'APOLLO_FEDERATION_DEBUG_SUBGRAPHS';
function maybeDumpSubgraphSchema(subgraph) {
const shouldDump = !!(0, utils_2.validateStringContainsBoolean)(process.env[DEBUG_SUBGRAPHS_ENV_VARIABLE_NAME]);
if (!shouldDump) {
return `Re-run with environment variable '${DEBUG_SUBGRAPHS_ENV_VARIABLE_NAME}' set to 'true' to extract the invalid subgraph`;
}
try {
const filename = `extracted-subgraph-${subgraph.name}-${Date.now()}.graphql`;
const file = path_1.default.resolve(filename);
if (fs_1.default.existsSync(file)) {
throw new Error(`candidate file ${filename} already existed`);
}
fs_1.default.writeFileSync(file, (0, print_1.printSchema)(subgraph.schema));
return `The (invalid) extracted subgraph has been written in: ${file}.`;
}
catch (e2) {
return `Was not able to print generated subgraph for "${subgraph.name}" because: ${errorToString(e2)}`;
}
}
function propagateDemandControlDirectives(source, dest, subgraph, costSpec) {
const costDirective = costSpec.costDirective(source.schema());
if (costDirective) {
const application = source.appliedDirectivesOf(costDirective)[0];
if (application) {
dest.applyDirective(subgraph.metadata().costDirective().name, application.arguments());
}
}
const listSizeDirective = costSpec.listSizeDirective(source.schema());
if (listSizeDirective) {
const application = source.appliedDirectivesOf(listSizeDirective)[0];
if (application) {
dest.applyDirective(subgraph.metadata().listSizeDirective().name, application.arguments());
}
}
}
function errorToString(e) {
const causes = (0, _1.errorCauses)(e);
return causes ? (0, _1.printErrors)(causes) : String(e);
}
function addSubgraphField({ field, type, subgraph, isShareable, joinFieldArgs, costSpec, }) {
const copiedFieldType = (joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.type)
? decodeType(joinFieldArgs.type, subgraph.schema, subgraph.name)
: copyType(field.type, subgraph.schema, subgraph.name);
const subgraphField = type.addField(field.name, copiedFieldType);
for (const arg of field.arguments()) {
const argDef = subgraphField.addArgument(arg.name, copyType(arg.type, subgraph.schema, subgraph.name), arg.defaultValue);
if (costSpec) {
propagateDemandControlDirectives(arg, argDef, subgraph, costSpec);
}
}
if (joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.requires) {
subgraphField.applyDirective(subgraph.metadata().requiresDirective(), { 'fields': joinFieldArgs.requires });
}
if (joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.provides) {
subgraphField.applyDirective(subgraph.metadata().providesDirective(), { 'fields': joinFieldArgs.provides });
}
if (joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.contextArguments) {
const fromContextDirective = subgraph.metadata().fromContextDirective();
if (!(0, _1.isFederationDirectiveDefinedInSchema)(fromContextDirective)) {
throw new Error(`@fromContext directive is not defined in the subgraph schema: ${subgraph.name}`);
}
else {
for (const arg of joinFieldArgs.contextArguments) {
const match = arg.context.match(/^.*__([A-Za-z]\w*)$/);
if (!match) {
throw new Error(`Invalid context argument: ${arg.context}`);
}
subgraphField.addArgument(arg.name, decodeType(arg.type, subgraph.schema, subgraph.name));
const argOnField = subgraphField.argument(arg.name);
argOnField === null || argOnField === void 0 ? void 0 : argOnField.applyDirective(fromContextDirective, {
field: `\$${match[1]} ${arg.selection}`,
});
}
}
}
const external = !!(joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.external);
if (external) {
subgraphField.applyDirective(subgraph.metadata().externalDirective());
}
const usedOverridden = !!(joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.usedOverridden);
if (usedOverridden && !(joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.overrideLabel)) {
subgraphField.applyDirective(subgraph.metadata().externalDirective(), { 'reason': '[overridden]' });
}
if (joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.override) {
subgraphField.applyDirective(subgraph.metadata().overrideDirective(), {
from: joinFieldArgs.override,
...(joinFieldArgs.overrideLabel ? { label: joinFieldArgs.overrideLabel } : {})
});
}
if (isShareable && !external && !usedOverridden) {
subgraphField.applyDirective(subgraph.metadata().shareableDirective());
}
if (costSpec) {
propagateDemandControlDirectives(field, subgraphField, subgraph, costSpec);
}
return subgraphField;
}
function addSubgraphInputField({ field, type, subgraph, joinFieldArgs, costSpec, }) {
const copiedType = (joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.type)
? decodeType(joinFieldArgs === null || joinFieldArgs === void 0 ? void 0 : joinFieldArgs.type, subgraph.schema, subgraph.name)
: copyType(field.type, subgraph.schema, subgraph.name);
const inputField = type.addField(field.name, copiedType);
inputField.defaultValue = field.defaultValue;
if (costSpec) {
propagateDemandControlDirectives(field, inputField, subgraph, costSpec);
}
return inputField;
}
function extractSubgraphsFromFed1Supergraph({ supergraph, subgraphs, joinSpec, filteredTypes, getSubgraph, }) {
const typeDirective = joinSpec.typeDirective(supergraph);
const ownerDirective = joinSpec.ownerDirective(supergraph);
const fieldDirective = joinSpec.fieldDirective(supergraph);
const reachableTypesBySubgraph = collectFieldReachableTypesForAllSubgraphs(supergraph, subgraphs.names(), (f, name) => {
var _a;
const fieldApplications = f.appliedDirectivesOf(fieldDirective);
if (fieldApplications.length) {
const application = fieldApplications.find((application) => { var _a; return ((_a = getSubgraph(application)) === null || _a === void 0 ? void 0 : _a.name) === name; });
if (application) {
const args = application.arguments();
const typesInFederationDirectives = typesUsedInFederationDirective(args.provides, (0, definitions_1.baseType)(f.type))
.concat(typesUsedInFederationDirective(args.requires, f.parent));
return { isInSubgraph: true, typesInFederationDirectives };
}
else {
return { isInSubgraph: false, typesInFederationDirectives: [] };
}
}
else {
const ownerApplications = ownerDirective ? f.parent.appliedDirectivesOf(ownerDirective) : [];
return { isInSubgraph: !ownerApplications.length || ((_a = getSubgraph(ownerApplications[0])) === null || _a === void 0 ? void 0 : _a.name) == name, typesInFederationDirectives: [] };
}
}, (t, name) => {
const typeApplications = t.appliedDirectivesOf(typeDirective);
const application = typeApplications.find((application) => { var _a; return (application.arguments().key && (((_a = getSubgraph(application)) === null || _a === void 0 ? void 0 : _a.name) === name)); });
if (application) {
const typesInFederationDirectives = typesUsedInFederationDirective(application.arguments().key, t);
return { isEntityWithKeyInSubgraph: true, typesInFederationDirectives };
}
else {
return { isEntityWithKeyInSubgraph: false, typesInFederationDirectives: [] };
}
});
const includeTypeInSubgraph = (t, name) => { var _a, _b; return (_b = (_a = reachableTypesBySubgraph.get(name)) === null || _a === void 0 ? void 0 : _a.has(t.name)) !== null && _b !== void 0 ? _b : false; };
for (const type of filteredTypes) {
const typeApplications = type.appliedDirectivesOf(typeDirective);
if (!typeApplications.length) {
for (const subgraph of subgraphs) {
if (includeTypeInSubgraph(type, subgraph.name)) {
subgraph.schema.addType((0, definitions_1.newNamedType)(type.kind, type.name));
}
}
}
else {
for (const application of typeApplications) {
const args = application.arguments();
const subgraph = getSubgraph(application);
(0, utils_1.assert)(subgraph, () => `Should have found the subgraph for ${application}`);
const schema = subgraph.schema;
let subgraphType = schema.type(type.name);
if (!subgraphType) {
const kind = args.isInterfaceObject ? 'ObjectType' : type.kind;
subgraphType = schema.addType((0, definitions_1.newNamedType)(kind, type.name));
if (args.isInterfaceObject) {
subgraphType.applyDirective('interfaceObject');
}
}
if (args.key) {
const { resolvable } = args;
const directive = subgraphType.applyDirective('key', { 'fields': args.key, resolvable });
if (args.extension) {
directive.setOfExtension(subgraphType.newExtension());
}
}
}
}
}
for (const type of filteredTypes) {
switch (type.kind) {
case 'ObjectType':
case 'InterfaceType':
for (const implementations of type.interfaceImplementations()) {
const name = implementations.interface.name;
for (const subgraph of subgraphs) {
const subgraphType = subgraph.schema.type(type.name);
const subgraphItf = subgraph.schema.type(name);
if (subgraphType && subgraphItf) {
subgraphType.addImplementedInterface(name);
}
}
}
case 'InputObjectType':
for (const field of type.fields()) {
const fieldApplications = field.appliedDirectivesOf(fieldDirective);
if (!fieldApplications.length) {
const ownerApplications = ownerDirective ? type.appliedDirectivesOf(ownerDirective) : [];
if (ownerApplications.length > 0) {
(0, utils_1.assert)(ownerApplications.length == 1, () => `Found multiple join__owner directives on type ${type}`);
const subgraph = getSubgraph(ownerApplications[0]);
(0, utils_1.assert)(subgraph, () => `Should have found the subgraph for ${ownerApplications[0]}`);
addSubgraphFieldForFed1(field, subgraph, false);
}
else {
const fieldBaseType = (0, definitions_1.baseType)(field.type);
const isShareable = (0, definitions_1.isObjectType)(type) && subgraphs.values().filter((s) => s.schema.type(type.name)).length > 1;
for (const subgraph of subgraphs) {
if (subgraph.schema.type(fieldBaseType.name)) {
addSubgraphFieldForFed1(field, subgraph, isShareable);
}
}
}
}
else {
const isShareable = (0, definitions_1.isObjectType)(type) && fieldApplications.length > 1;
for (const application of fieldApplications) {
const subgraph = getSubgraph(application);
if (!subgraph) {
continue;
}
const args = application.arguments();
addSubgraphFieldForFed1(field, subgraph, isShareable, args);
}
}
}
break;
case 'EnumType':
for (const subgraph of subgraphs) {
const subgraphEnum = subgraph.schema.type(type.name);
if (!subgraphEnum) {
continue;
}
(0, utils_1.assert)((0, definitions_1.isEnumType)(subgraphEnum), () => `${subgraphEnum} should be an enum but found a ${subgraphEnum.kind}`);
for (const value of type.values) {
subgraphEnum.addValue(value.name);
}
}
break;
case 'UnionType':
for (const subgraph of subgraphs) {
const subgraphUnion = subgraph.schema.type(type.name);
if (!subgraphUnion) {
continue;
}
(0, utils_1.assert)((0, definitions_1.isUnionType)(subgraphUnion), () => `${subgraphUnion} should be an enum but found a ${subgraphUnion.kind}`);
for (const memberTypeName of type.types().map((t) => t.name)) {
const subgraphType = subgraph.schema.type(memberTypeName);
if (subgraphType) {
subgraphUnion.addType(subgraphType);
}
}
}
break;
}
}
const allExecutableDirectives = supergraph.directives().filter((def) => def.hasExecutableLocations());
for (const subgraph of subgraphs) {
addExternalFields(subgraph, supergraph, true);
(0, federation_1.removeInactiveProvidesAndRequires)(subgraph.schema);
removeUnusedTypesFromSubgraph(subgraph.schema);
for (const definition of allExecutableDirectives) {
(0, definitions_1.copyDirectiveDefinitionToSchema)({
definition,
schema: subgraph.schema,
copyDirectiveApplicationsInArguments: false,
locationFilter: (loc) => (0, definitions_1.isExecutableDirectiveLocation)(loc),
});
}
}
return subgraphs;
}
function addSubgraphFieldForFed1(field, subgraph, isShareable, joinFieldArgs) {
const subgraphType = subgraph.schema.type(field.parent.name);
if (!subgraphType) {
return;
}
if (field instanceof definitions_1.FieldDefinition) {
addSubgraphField({
field,
subgraph,
type: subgraphType,
isShareable,
joinFieldArgs,
});
}
else {
addSubgraphInputField({
field,
subgraph,
type: subgraphType,
joinFieldArgs,
});
}
}
function decodeType(encodedType, subgraph, subgraphName) {
try {
return (0, buildSchema_1.builtTypeReference)(encodedType, subgraph);
}
catch (e) {
(0, utils_1.assert)(false, () => `Cannot parse type "${encodedType}" in subgraph ${subgraphName}: ${e}`);
}
}
function copyType(type, subgraph, subgraphName) {
switch (type.kind) {
case 'ListType':
return new definitions_1.ListType(copyType(type.ofType, subgraph, subgraphName));
case 'NonNullType':
return new definitions_1.NonNullType(copyType(type.ofType, subgraph, subgraphName));
default:
const subgraphType = subgraph.type(type.name);
(0, utils_1.assert)(subgraphType, () => `Cannot find type "${type.name}" in subgraph "${subgraphName}"`);
return subgraphType;
}
}
function addExternalFields(subgraph, supergraph, isFed1) {
const metadata = subgraph.metadata();
for (const type of subgraph.schema.types()) {
if (!(0, definitions_1.isObjectType)(type) && !(0, definitions_1.isInterfaceType)(type)) {
continue;
}
for (const keyApplication of type.appliedDirectivesOf(metadata.keyDirective())) {
const forceNonExternal = isFed1 || !!keyApplication.ofExtension();
addExternalFieldsFromDirectiveFieldSet(subgraph, type, keyApplication, supergraph, forceNonExternal);
}
for (const field of type.fields()) {
for (const requiresApplication of field.appliedDirectivesOf(metadata.requiresDirective())) {
addExternalFieldsFromDirectiveFieldSet(subgraph, type, requiresApplication, supergraph);
}
const fieldBaseType = (0, definitions_1.baseType)(field.type);
for (const providesApplication of field.appliedDirectivesOf(metadata.providesDirective())) {
(0, utils_1.assert)((0, definitions_1.isObjectType)(fieldBaseType) || (0, definitions_1.isInterfaceType)(fieldBaseType), () => `Found @provides on field ${field.coordinate} whose type ${field.type} (${fieldBaseType.kind}) is not an object or interface `);
addExternalFieldsFromDirectiveFieldSet(subgraph, fieldBaseType, providesApplication, supergraph);
}
}
addExternalFieldsFromInterface(metadata, type);
}
}
function addExternalFieldsFromDirectiveFieldSet(subgraph, parentType, directive, supergraph, forceNonExternal = false) {
const external = subgraph.metadata().externalDirective();
const fieldAccessor = function (type, fieldName) {
const field = type.field(fieldName);
if (field) {
if (forceNonExternal && field.hasAppliedDirective(external)) {
field.appliedDirectivesOf(external).forEach(d => d.remove());
}
return field;
}
(0, utils_1.assert)(!(0, definitions_1.isUnionType)(type), () => `Shouldn't select field ${fieldName} from union type ${type}`);
const supergraphType = supergraph.type(type.name);
const supergraphField = supergraphType.field(fieldName);
(0, utils_1.assert)(supergraphField, () => `No field named ${fieldName} found on type ${type.name} in the supergraph`);
const created = addSubgraphField({
field: supergraphField,
subgraph,
type,
isShareable: false,
});
if (!forceNonExternal) {
created.applyDirective(external);
}
return created;
};
try {
(0, federation_1.parseFieldSetArgument)({ parentType, directive, fieldAccessor, validate: false });
}
catch (e) {
}
}
function addExternalFieldsFromInterface(metadata, type) {
for (const itf of type.interfaces()) {
for (const field of itf.fields()) {
const typeField = type.field(field.name);
if (!typeField) {
copyFieldAsExternal(metadata, field, type);
}
else if (typeField.hasAppliedDirective(metadata.externalDirective())) {
maybeUpdateFieldForInterface(typeField, field);
}
}
}
}
function copyFieldAsExternal(metadata, field, type) {
const newField = type.addField(field.name, field.type);
for (const arg of field.arguments()) {
newField.addArgument(arg.name, arg.type, arg.defaultValue);
}
newField.applyDirective(metadata.externalDirective());
}
function maybeUpdateFieldForInterface(toModify, itfField) {
if (!(0, types_1.isSubtype)(itfField.type, toModify.type)) {
(0, utils_1.assert)((0, types_1.isSubtype)(toModify.type, itfField.type), () => `For ${toModify.coordinate}, expected ${itfField.type} and ${toModify.type} to be in a subtyping relationship`);
toModify.type = itfField.type;
}
}
function removeUnusedTypesFromSubgraph(schema) {
for (const type of schema.types()) {
switch (type.kind) {
case 'ObjectType':
case 'InterfaceType':
case 'InputObjectType':
if (!type.hasFields()) {
type.removeRecursive();
}
break;
case 'UnionType':
if (type.membersCount() === 0) {
type.removeRecursive();
}
break;
}
}
}
//# sourceMappingURL=extractSubgraphsFromSupergraph.js.map
;