@nestjs/graphql
Version:
Nest - modern, fast, powerful node.js web framework (@graphql)
161 lines (160 loc) • 8.03 kB
JavaScript
import { __decorate, __metadata } from "tslib";
import { Injectable } from '@nestjs/common';
import { isUndefined } from '@nestjs/common/utils/shared.utils.js';
import { GraphQLInterfaceType, } from 'graphql';
import { InterfaceNotRegisteredError } from '../errors/interface-not-registered.error.js';
import { ReturnTypeCannotBeResolvedError } from '../errors/return-type-cannot-be-resolved.error.js';
import { OrphanedReferenceRegistry } from '../services/orphaned-reference.registry.js';
import { TypeFieldsAccessor } from '../services/type-fields.accessor.js';
import { TypeDefinitionsStorage } from '../storages/type-definitions.storage.js';
import { TypeMetadataStorage } from '../storages/type-metadata.storage.js';
import { getInterfacesArray } from '../utils/get-interfaces-array.util.js';
import { ArgsFactory } from './args.factory.js';
import { AstDefinitionNodeFactory } from './ast-definition-node.factory.js';
import { OutputTypeFactory } from './output-type.factory.js';
import { ResolveTypeFactory } from './resolve-type.factory.js';
let InterfaceDefinitionFactory = class InterfaceDefinitionFactory {
constructor(resolveTypeFactory, typeDefinitionsStorage, outputTypeFactory, orphanedReferenceRegistry, typeFieldsAccessor, argsFactory, astDefinitionNodeFactory) {
this.resolveTypeFactory = resolveTypeFactory;
this.typeDefinitionsStorage = typeDefinitionsStorage;
this.outputTypeFactory = outputTypeFactory;
this.orphanedReferenceRegistry = orphanedReferenceRegistry;
this.typeFieldsAccessor = typeFieldsAccessor;
this.argsFactory = argsFactory;
this.astDefinitionNodeFactory = astDefinitionNodeFactory;
}
create(metadata, options) {
const prototype = Object.getPrototypeOf(metadata.target);
const getParentType = () => {
const parentTypeDefinition = this.typeDefinitionsStorage.getObjectTypeByTarget(prototype) ||
this.typeDefinitionsStorage.getInterfaceByTarget(prototype);
return parentTypeDefinition ? parentTypeDefinition.type : undefined;
};
const resolveType = this.createResolveTypeFn(metadata);
return {
target: metadata.target,
isAbstract: metadata.isAbstract || false,
interfaces: getInterfacesArray(metadata.interfaces),
type: new GraphQLInterfaceType({
name: metadata.name,
description: metadata.description,
fields: this.generateFields(metadata, options),
interfaces: this.generateInterfaces(metadata, getParentType),
resolveType,
/**
* AST node has to be manually created in order to define directives
* (more on this topic here: https://github.com/graphql/graphql-js/issues/1343)
*/
astNode: this.astDefinitionNodeFactory.createInterfaceTypeNode(metadata.name, metadata.directives),
}),
};
}
createResolveTypeFn(metadata) {
const objectTypesMetadata = TypeMetadataStorage.getObjectTypesMetadata();
const implementedTypes = objectTypesMetadata
.filter((objectType) => {
const interfaces = getInterfacesArray(objectType.interfaces);
return interfaces.includes(metadata.target);
})
.map((objectType) => objectType.target);
return metadata.resolveType
? this.resolveTypeFactory.getResolveTypeFunction(metadata.resolveType)
: (instance) => {
const target = implementedTypes.find((Type) => instance instanceof Type);
if (!target) {
if (Reflect.has(instance, '__typename')) {
return instance.__typename;
}
throw new ReturnTypeCannotBeResolvedError(metadata.name);
}
return this.typeDefinitionsStorage.getObjectTypeByTarget(target).type
?.name;
};
}
generateFields(metadata, options) {
const prototype = Object.getPrototypeOf(metadata.target);
metadata.properties.forEach(({ typeFn }) => this.orphanedReferenceRegistry.addToRegistryIfOrphaned(typeFn()));
const getParentType = () => {
const parentTypeDefinition = this.typeDefinitionsStorage.getInterfaceByTarget(prototype);
return parentTypeDefinition ? parentTypeDefinition.type : undefined;
};
return () => {
let fields = {};
let properties = [];
if (metadata.interfaces) {
const implementedInterfaces = TypeMetadataStorage.getInterfacesMetadata()
.filter((it) => getInterfacesArray(metadata.interfaces).includes(it.target))
.map((it) => it.properties);
implementedInterfaces.forEach((fields) => properties.push(...(fields || [])));
}
properties = properties.concat(metadata.properties);
properties.forEach((field) => {
const type = this.outputTypeFactory.create(field.name, field.typeFn(), options, field.options);
fields[field.schemaName] = {
description: field.description,
type,
args: this.argsFactory.create(field.methodArgs, options),
resolve: (root) => {
const value = root[field.name];
return typeof value === 'undefined'
? field.options.defaultValue
: value;
},
deprecationReason: field.deprecationReason,
/**
* AST node has to be manually created in order to define directives
* (more on this topic here: https://github.com/graphql/graphql-js/issues/1343)
*/
astNode: this.astDefinitionNodeFactory.createFieldNode(field.name, type, field.directives),
extensions: {
complexity: field.complexity,
...field.extensions,
},
};
});
if (!isUndefined(prototype.prototype)) {
const parentClassRef = getParentType();
if (parentClassRef) {
const parentFields = this.typeFieldsAccessor.extractFromInterfaceOrObjectType(parentClassRef);
fields = {
...parentFields,
...fields,
};
}
}
return fields;
};
}
generateInterfaces(metadata, getParentType) {
const prototype = Object.getPrototypeOf(metadata.target);
return () => {
const interfaces = getInterfacesArray(metadata.interfaces).map((item) => {
const definition = this.typeDefinitionsStorage.getInterfaceByTarget(item);
if (!definition) {
throw new InterfaceNotRegisteredError(metadata.name, item);
}
return definition.type;
});
if (!isUndefined(prototype)) {
const parentClass = getParentType();
if (!parentClass) {
return interfaces;
}
const parentInterfaces = parentClass.getInterfaces?.() ?? [];
return Array.from(new Set([...interfaces, ...parentInterfaces]));
}
return interfaces;
};
}
};
InterfaceDefinitionFactory = __decorate([
Injectable(),
__metadata("design:paramtypes", [ResolveTypeFactory,
TypeDefinitionsStorage,
OutputTypeFactory,
OrphanedReferenceRegistry,
TypeFieldsAccessor,
ArgsFactory,
AstDefinitionNodeFactory])
], InterfaceDefinitionFactory);
export { InterfaceDefinitionFactory };