UNPKG

@nestjs/graphql

Version:

Nest - modern, fast, powerful node.js web framework (@graphql)

138 lines (137 loc) 5.53 kB
// This file is copied from `apollographql/federation`. The only difference is // that it has a hack to not remove federation specific properties. // https://github.com/apollographql/federation/blob/main/subgraph-js/src/schema-helper/transformSchema.ts import { GraphQLDirective, GraphQLInputObjectType, GraphQLInterfaceType, GraphQLList, GraphQLNonNull, GraphQLObjectType, GraphQLSchema, GraphQLUnionType, isInputObjectType, isInterfaceType, isIntrospectionType, isListType, isNonNullType, isObjectType, isUnionType, } from 'graphql'; export function transformSchema(schema, transformType) { const typeMap = Object.create(null); for (const oldType of Object.values(schema.getTypeMap())) { if (isIntrospectionType(oldType)) continue; const result = transformType(oldType); // Returning `null` removes the type. if (result === null) continue; // Returning `undefined` keeps the old type. const newType = result || oldType; typeMap[newType.name] = recreateNamedType(newType); } const schemaConfig = schema.toConfig(); return new GraphQLSchema({ ...schemaConfig, types: Object.values(typeMap), query: replaceMaybeType(schemaConfig.query), mutation: replaceMaybeType(schemaConfig.mutation), subscription: replaceMaybeType(schemaConfig.subscription), directives: replaceDirectives([...schemaConfig.directives]), }); function recreateNamedType(type) { if (isObjectType(type)) { const config = type.toConfig(); const objectType = new GraphQLObjectType({ ...config, interfaces: () => config.interfaces.map(replaceNamedType), fields: () => replaceFields(config.fields), }); const typeApolloExtensions = type.extensions?.apollo; const objectApolloExtensions = objectType.extensions?.apollo; if (typeApolloExtensions?.subgraph?.resolveReference) { objectType.extensions = { ...objectType.extensions, apollo: { ...objectApolloExtensions, subgraph: { ...objectApolloExtensions?.subgraph, resolveReference: typeApolloExtensions.subgraph.resolveReference, }, }, }; /** * Backcompat for old versions of @apollo/subgraph which didn't use * `extensions` This can be removed when support for @apollo/subgraph < * 0.4.2 is dropped Reference: * https://github.com/apollographql/federation/pull/1747 */ // @ts-expect-error (explanation above) } else if (type.resolveReference) { // @ts-expect-error (explanation above) objectType.resolveReference = type.resolveReference; } return objectType; } else if (isInterfaceType(type)) { const config = type.toConfig(); return new GraphQLInterfaceType({ ...config, interfaces: () => config.interfaces.map(replaceNamedType), fields: () => replaceFields(config.fields), }); } else if (isUnionType(type)) { const config = type.toConfig(); return new GraphQLUnionType({ ...config, types: () => config.types.map(replaceNamedType), }); } else if (isInputObjectType(type)) { const config = type.toConfig(); return new GraphQLInputObjectType({ ...config, fields: () => replaceInputFields(config.fields), }); } return type; } function replaceType(type) { if (isListType(type)) { return new GraphQLList(replaceType(type.ofType)); } else if (isNonNullType(type)) { return new GraphQLNonNull(replaceType(type.ofType)); } return replaceNamedType(type); } function replaceNamedType(type) { const newType = typeMap[type.name]; return newType ? newType : type; } function replaceMaybeType(type) { return type ? replaceNamedType(type) : undefined; } function replaceFields(fieldsMap) { return mapValues(fieldsMap, (field) => ({ ...field, type: replaceType(field.type), args: field.args ? replaceArgs(field.args) : undefined, })); } function replaceInputFields(fieldsMap) { return mapValues(fieldsMap, (field) => ({ ...field, type: replaceType(field.type), })); } function replaceArgs(args) { return mapValues(args, (arg) => ({ ...arg, type: replaceType(arg.type), })); } function replaceDirectives(directives) { return directives.map((directive) => { const config = directive.toConfig(); return new GraphQLDirective({ ...config, args: replaceArgs(config.args), }); }); } } function mapValues(object, callback) { const result = Object.create(null); for (const [key, value] of Object.entries(object)) { result[key] = callback(value); } return result; }