UNPKG

@apollo/federation-internals

Version:
863 lines 42.1 kB
"use strict"; 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