@nestjs/graphql
Version:
Nest - modern, fast, powerful node.js web framework (@graphql)
178 lines (177 loc) • 8.42 kB
JavaScript
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 };