UNPKG

@graphql-mesh/fusion-runtime

Version:

Runtime for GraphQL Mesh Fusion Supergraph

219 lines (218 loc) • 11.6 kB
import { isSpecifiedScalarType, } from 'graphql'; import { getDefDirectives } from '@graphql-mesh/utils'; import { stitchingDirectives } from '@graphql-tools/stitching-directives'; import { getRootTypeNames, MapperKind, mapSchema } from '@graphql-tools/utils'; import { RenameInputObjectFields, RenameInterfaceFields, RenameObjectFields, RenameTypes, TransformEnumValues, } from '@graphql-tools/wrap'; export function extractSubgraphsFromFusiongraph(fusiongraph) { const subgraphNames = new Set(); const subschemaMap = new Map(); const transportEntryMap = {}; const schemaDirectives = getDefDirectives(fusiongraph, fusiongraph); const transportDirectives = schemaDirectives.filter(directive => directive.name === 'transport'); const { stitchingDirectivesTransformer } = stitchingDirectives(); for (const transportDirective of transportDirectives) { const subgraph = transportDirective.args.subgraph; if (typeof subgraph === 'string') { subgraphNames.add(subgraph); transportEntryMap[subgraph] = transportDirective.args; } } const rootTypeNames = getRootTypeNames(fusiongraph); const additionalResolversFromTypeDefs = []; const additionalTypeDefs = new Set(); for (const subgraph of subgraphNames) { const renameTypeNames = {}; const renameTypeNamesReversed = {}; const renameFieldByObjectTypeNames = {}; const renameFieldByInputTypeNames = {}; const renameFieldByInterfaceTypeNames = {}; const renameEnumValueByEnumTypeNames = {}; const subgraphSchema = mapSchema(fusiongraph, { [MapperKind.TYPE]: type => { const typeDirectives = getDefDirectives(fusiongraph, type, subgraph); const sourceDirectives = typeDirectives.filter(directive => directive.name === 'source'); const sourceDirective = sourceDirectives.find(directive => directive.args.subgraph === subgraph); if (sourceDirective != null) { const realName = sourceDirective.args.name ?? type.name; if (type.name !== realName) { renameTypeNames[realName] = type.name; renameTypeNamesReversed[type.name] = realName; return new (Object.getPrototypeOf(type).constructor)({ ...type.toConfig(), name: realName, }); } return type; } if (rootTypeNames.has(type.name) || isSpecifiedScalarType(type)) { return type; } return null; }, [MapperKind.OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => { const fieldDirectives = getDefDirectives(fusiongraph, fieldConfig); const resolveToDirectives = fieldDirectives.filter(directive => directive.name === 'resolveTo'); if (resolveToDirectives.length > 0) { for (const resolveToDirective of resolveToDirectives) { additionalResolversFromTypeDefs.push({ targetTypeName: typeName, targetFieldName: fieldName, ...resolveToDirective.args, }); } } const sourceDirectives = fieldDirectives.filter(directive => directive.name === 'source'); if (!sourceDirectives.length) { const argEntries = Object.entries(fieldConfig.args ?? {}); additionalTypeDefs.add(`extend type ${typeName} { ${fieldName}${argEntries.length > 0 ? ` (${argEntries.map(([argName, argConfig]) => `${argName}: ${argConfig.type}`).join('\n,')}) ` : ''}: ${fieldConfig.type} } `); } const sourceDirective = sourceDirectives.find(directive => directive.args.subgraph === subgraph); if (sourceDirective != null) { const realTypeName = renameTypeNamesReversed[typeName] ?? typeName; const realName = sourceDirective.args.name ?? fieldName; if (fieldName !== realName) { if (!renameFieldByObjectTypeNames[realTypeName]) { renameFieldByObjectTypeNames[realTypeName] = {}; } renameFieldByObjectTypeNames[realTypeName][realName] = fieldName; } const directivesObj = {}; for (const fieldDirective of fieldDirectives) { if (fieldDirective?.args?.subgraph && fieldDirective.args.subgraph !== subgraph) { continue; } directivesObj[fieldDirective.name] ||= []; directivesObj[fieldDirective.name].push(fieldDirective.args); } return [ realName, { ...fieldConfig, astNode: undefined, extensions: { ...fieldConfig.extensions, directives: directivesObj, }, }, ]; } return null; }, [MapperKind.INPUT_OBJECT_FIELD]: (fieldConfig, fieldName, typeName) => { const fieldDirectives = getDefDirectives(fusiongraph, fieldConfig, subgraph); const [sourceDirective] = fieldDirectives.filter(directive => directive.name === 'source' && directive.args.subgraph === subgraph); if (sourceDirective != null) { const realTypeName = renameTypeNamesReversed[typeName] ?? typeName; const realName = sourceDirective.args.name ?? fieldName; if (fieldName !== realName) { if (!renameFieldByInputTypeNames[realTypeName]) { renameFieldByInputTypeNames[realTypeName] = {}; } renameFieldByInputTypeNames[realTypeName][realName] = fieldName; } return [realName, fieldConfig]; } return null; }, [MapperKind.INTERFACE_FIELD]: (fieldConfig, fieldName, typeName) => { const fieldDirectives = getDefDirectives(fusiongraph, fieldConfig, subgraph); const [sourceDirective] = fieldDirectives.filter(directive => directive.name === 'source' && directive.args.subgraph === subgraph); if (sourceDirective != null) { const realName = sourceDirective.args.name ?? fieldName; if (fieldName !== realName) { const realTypeName = renameTypeNamesReversed[typeName] ?? typeName; if (!renameFieldByInterfaceTypeNames[realTypeName]) { renameFieldByInterfaceTypeNames[realTypeName] = {}; } renameFieldByInterfaceTypeNames[realTypeName][realName] = fieldName; } return [realName, fieldConfig]; } return null; }, [MapperKind.ENUM_VALUE]: (enumValueConfig, typeName, _schema, externalValue) => { const enumValueDirectives = getDefDirectives(fusiongraph, enumValueConfig, subgraph); const [sourceDirective] = enumValueDirectives.filter(directive => directive.name === 'source' && directive.args.subgraph === subgraph); if (sourceDirective != null) { const realValue = sourceDirective.args.name ?? externalValue; const realTypeName = renameTypeNamesReversed[typeName] ?? typeName; if (externalValue !== realValue) { if (!renameEnumValueByEnumTypeNames[realTypeName]) { renameEnumValueByEnumTypeNames[realTypeName] = {}; } renameEnumValueByEnumTypeNames[realTypeName][realValue] = externalValue; } return [realValue, enumValueConfig]; } return null; }, }); const transforms = []; if (Object.keys(renameTypeNames).length > 0) { transforms.push(new RenameTypes(typeName => renameTypeNames[typeName] || typeName)); } if (Object.keys(renameFieldByObjectTypeNames).length > 0) { transforms.push(new RenameObjectFields((typeName, fieldName, _fieldConfig) => { return renameFieldByObjectTypeNames[typeName]?.[fieldName] ?? fieldName; })); } if (Object.keys(renameFieldByInputTypeNames).length > 0) { transforms.push(new RenameInputObjectFields((typeName, fieldName, _fieldConfig) => { return renameFieldByInputTypeNames[typeName]?.[fieldName] ?? fieldName; })); } if (Object.keys(renameFieldByInterfaceTypeNames).length > 0) { transforms.push(new RenameInterfaceFields((typeName, fieldName, _fieldConfig) => { return renameFieldByInterfaceTypeNames[typeName]?.[fieldName] ?? fieldName; })); } if (Object.keys(renameEnumValueByEnumTypeNames).length > 0) { transforms.push(new TransformEnumValues((typeName, externalValue, enumValueConfig) => { return [ renameEnumValueByEnumTypeNames[typeName]?.[externalValue] ?? externalValue, enumValueConfig, ]; })); } let subschema = { schema: subgraphSchema, transforms, }; subschema = stitchingDirectivesTransformer(subschema); const queryType = subgraphSchema.getQueryType(); // Transformer doesn't respect transforms if (transforms.length && subschema.merge) { const mergeConfig = {}; for (const realTypeName in subschema.merge) { const renamedTypeName = renameTypeNames[realTypeName] ?? realTypeName; mergeConfig[renamedTypeName] = subschema.merge[realTypeName]; const realQueryFieldName = mergeConfig[renamedTypeName].fieldName; if (realQueryFieldName) { mergeConfig[renamedTypeName].fieldName = renameFieldByObjectTypeNames[queryType.name]?.[realQueryFieldName] ?? realQueryFieldName; } mergeConfig[renamedTypeName].entryPoints = subschema.merge[realTypeName].entryPoints?.map(entryPoint => ({ ...entryPoint, fieldName: renameFieldByObjectTypeNames[queryType.name]?.[entryPoint.fieldName] ?? entryPoint.fieldName, })); } subschema.merge = mergeConfig; } subschemaMap.set(subgraph, subschema); } return { subschemaMap, transportEntryMap, additionalTypeDefs, additionalResolversFromTypeDefs, }; }