UNPKG

@nestjs/graphql

Version:

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

178 lines (177 loc) 8.42 kB
import { __decorate, __metadata } from "tslib"; /* eslint-disable @typescript-eslint/no-unused-expressions */ import { makeExecutableSchema, mergeSchemas } from '@graphql-tools/schema'; import { Injectable } from '@nestjs/common'; import { existsSync, lstatSync, readFileSync } from 'fs'; import { GraphQLObjectType, GraphQLSchema, lexicographicSortSchema, printSchema, } from 'graphql'; import { gql } from 'graphql-tag'; import { GraphQLAstExplorer, } from './graphql-ast.explorer.js'; import { GraphQLSchemaBuilder } from './graphql-schema.builder.js'; import { ResolversExplorerService, ScalarsExplorerService, } from './services/index.js'; import { extend, removeTempField } from './utils/index.js'; let GraphQLFactory = class GraphQLFactory { constructor(resolversExplorerService, scalarsExplorerService, graphqlAstExplorer, gqlSchemaBuilder) { this.resolversExplorerService = resolversExplorerService; this.scalarsExplorerService = scalarsExplorerService; this.graphqlAstExplorer = graphqlAstExplorer; this.gqlSchemaBuilder = gqlSchemaBuilder; } async generateSchema(options = { typeDefs: [] }) { const resolvers = this.resolversExplorerService.explore(); const rawResolvers = extend(this.scalarsExplorerService.explore(), resolvers); const typesResolvers = options.transformResolvers ? await options.transformResolvers(rawResolvers) : rawResolvers; const transformSchema = async (schema) => options.transformSchema ? await options.transformSchema(schema) : schema; if (options.autoSchemaFile) { const autoGeneratedSchema = await this.gqlSchemaBuilder.build(options.autoSchemaFile, options, this.resolversExplorerService.getAllCtors()); const executableSchema = makeExecutableSchema({ resolvers: extend(typesResolvers, options.resolvers), typeDefs: gql ` ${printSchema(autoGeneratedSchema)} `, resolverValidationOptions: { ...(options.resolverValidationOptions || {}), requireResolversForResolveType: 'ignore', }, inheritResolversFromInterfaces: options.inheritResolversFromInterfaces, }); let schema = options.schema ? mergeSchemas({ schemas: [options.schema, executableSchema], }) : executableSchema; const autoGeneratedSchemaConfig = autoGeneratedSchema.toConfig(); const schemaConfig = this.overrideOrExtendResolvers(schema.toConfig(), autoGeneratedSchemaConfig); schema = new GraphQLSchema(schemaConfig); schema = await transformSchema(schema); schema = options.sortSchema ? lexicographicSortSchema(schema) : schema; return schema; } if (this.isEmptyValue(options.typeDefs)) { const schema = await transformSchema(options.schema); return schema; } const executableSchema = makeExecutableSchema({ resolvers: extend(typesResolvers, options.resolvers), typeDefs: gql ` ${options.typeDefs} `, resolverValidationOptions: options.resolverValidationOptions, inheritResolversFromInterfaces: options.inheritResolversFromInterfaces, }); let schema = options.schema ? mergeSchemas({ schemas: [options.schema, executableSchema], }) : executableSchema; removeTempField(schema); schema = await transformSchema(schema); schema = options.sortSchema ? lexicographicSortSchema(schema) : schema; return schema; } overrideOrExtendResolvers(executableSchemaConfig, autoGeneratedSchemaConfig) { const schemaConfig = autoGeneratedSchemaConfig; const rootResolverKeys = [ 'mutation', 'query', 'subscription', ]; rootResolverKeys .filter((key) => executableSchemaConfig[key] && schemaConfig[key]) .forEach((key) => { const executableSchemaFields = executableSchemaConfig[key].getFields(); const schemaFields = schemaConfig[key].getFields(); Object.entries(executableSchemaFields).forEach(([resolverName, value]) => { if (schemaFields[resolverName]) { schemaFields[resolverName].resolve = executableSchemaFields[resolverName].resolve; schemaFields[resolverName].subscribe = executableSchemaFields[resolverName].subscribe; } else { schemaFields[resolverName] = value; } }); }); const getAutoGeneratedTypeByName = (name) => schemaConfig.types.find((type) => type.name === name); executableSchemaConfig.types .filter((type) => type instanceof GraphQLObjectType) .forEach((type) => { const fields = type.getFields(); const autoGeneratedType = getAutoGeneratedTypeByName(type.name); if (!autoGeneratedType) { return; } /** * Inherit "resolve()" functions from auto-generated interfaces */ const implementedInterfaces = autoGeneratedType.getInterfaces() || []; if (implementedInterfaces.length > 0) { implementedInterfaces.forEach((interfaceRef) => { const interfaceInExecutableSchema = executableSchemaConfig.types.find((type) => type.name === interfaceRef.name); Object.entries(interfaceRef.getFields()).forEach(([key]) => { const fieldInExecutableSchema = interfaceInExecutableSchema.getFields()[key]; if (!fieldInExecutableSchema) { return; } if (!fieldInExecutableSchema.resolve) { return; } const baseClassField = autoGeneratedType.getFields()[key]; baseClassField && (baseClassField.resolve = fieldInExecutableSchema.resolve); }); }); } Object.entries(fields).forEach(([key, value]) => { if (!value.resolve) { return; } const field = autoGeneratedType.getFields()[key]; field && (field.resolve = value.resolve); }); }); return schemaConfig; } async generateDefinitions(typeDefs, options) { if (this.isEmptyValue(typeDefs) || !options.definitions) { return; } const definitionsGeneratorOptions = { emitTypenameField: options.definitions.emitTypenameField, skipResolverArgs: options.definitions.skipResolverArgs, defaultScalarType: options.definitions.defaultScalarType, customScalarTypeMapping: options.definitions.customScalarTypeMapping, additionalHeader: options.definitions.additionalHeader, defaultTypeMapping: options.definitions.defaultTypeMapping, enumsAsTypes: options.definitions.enumsAsTypes, typeName: options.definitions.typeName, }; const tsFile = await this.graphqlAstExplorer.explore(gql ` ${typeDefs} `, options.definitions.path, options.definitions.outputAs, definitionsGeneratorOptions); if (!existsSync(options.definitions.path) || !lstatSync(options.definitions.path).isFile() || readFileSync(options.definitions.path, 'utf8') !== tsFile.getFullText()) { await tsFile.save(); } } isEmptyValue(value) { if (value == null) { return true; } if (Array.isArray(value) || typeof value === 'string') { return value.length === 0; } return false; } }; GraphQLFactory = __decorate([ Injectable(), __metadata("design:paramtypes", [ResolversExplorerService, ScalarsExplorerService, GraphQLAstExplorer, GraphQLSchemaBuilder]) ], GraphQLFactory); export { GraphQLFactory };