graphql-compose
Version:
GraphQL schema builder from different data sources with middleware extensions.
556 lines • 22 kB
JavaScript
"use strict";
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
t[p[i]] = s[p[i]];
}
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.UnionTypeComposer = void 0;
const graphql_1 = require("./graphql");
const is_1 = require("./utils/is");
const misc_1 = require("./utils/misc");
const ObjectTypeComposer_1 = require("./ObjectTypeComposer");
const SchemaComposer_1 = require("./SchemaComposer");
const ListComposer_1 = require("./ListComposer");
const NonNullComposer_1 = require("./NonNullComposer");
const ThunkComposer_1 = require("./ThunkComposer");
const configToDefine_1 = require("./utils/configToDefine");
const typeHelpers_1 = require("./utils/typeHelpers");
const graphqlVersion_1 = require("./utils/graphqlVersion");
const schemaPrinter_1 = require("./utils/schemaPrinter");
const definitionNode_1 = require("./utils/definitionNode");
const schemaPrinterSortTypes_1 = require("./utils/schemaPrinterSortTypes");
class UnionTypeComposer {
constructor(graphqlType, schemaComposer) {
this._gqcFallbackResolveType = null;
if (!(schemaComposer instanceof SchemaComposer_1.SchemaComposer)) {
throw new Error('You must provide SchemaComposer instance as a second argument for `new UnionTypeComposer(GraphQLUnionType, SchemaComposer)`');
}
if (!(graphqlType instanceof graphql_1.GraphQLUnionType)) {
throw new Error('UnionTypeComposer accept only GraphQLUnionType in constructor. Try to use more flexible method `UnionTypeComposer.create()`.');
}
this.schemaComposer = schemaComposer;
this._gqType = graphqlType;
this.schemaComposer.set(graphqlType, this);
this.schemaComposer.set(graphqlType.name, this);
let types = [];
if (graphqlVersion_1.graphqlVersion >= 14) {
types = this._gqType._types;
}
else {
types = this._gqType._types || this._gqType._typeConfig.types;
}
types = (0, configToDefine_1.convertObjectTypeArrayAsThunk)(types, this.schemaComposer);
this._gqcTypes = new Set();
types.forEach((type) => {
this._gqcTypes.add(type);
});
this._gqcTypeResolvers = new Map();
if (!this._gqType.astNode) {
this._gqType.astNode = (0, definitionNode_1.getUnionTypeDefinitionNode)(this);
}
this._gqcIsModified = false;
}
static create(typeDef, schemaComposer) {
if (!(schemaComposer instanceof SchemaComposer_1.SchemaComposer)) {
throw new Error('You must provide SchemaComposer instance as a second argument for `UnionTypeComposer.create(typeDef, schemaComposer)`');
}
if (schemaComposer.hasInstance(typeDef, UnionTypeComposer)) {
return schemaComposer.getUTC(typeDef);
}
const utc = this.createTemp(typeDef, schemaComposer);
schemaComposer.add(utc);
return utc;
}
static createTemp(typeDef, schemaComposer) {
const sc = schemaComposer || new SchemaComposer_1.SchemaComposer();
let UTC;
if ((0, is_1.isString)(typeDef)) {
const typeName = typeDef;
if ((0, typeHelpers_1.isTypeNameString)(typeName)) {
UTC = new UnionTypeComposer(new graphql_1.GraphQLUnionType({
name: typeName,
types: () => [],
}), sc);
}
else {
UTC = sc.typeMapper.convertSDLTypeDefinition(typeName);
if (!(UTC instanceof UnionTypeComposer)) {
throw new Error('You should provide correct GraphQLUnionType type definition. ' +
'Eg. `union MyType = Photo | Person`');
}
}
}
else if (typeDef instanceof graphql_1.GraphQLUnionType) {
UTC = new UnionTypeComposer(typeDef, sc);
}
else if ((0, is_1.isObject)(typeDef)) {
const type = new graphql_1.GraphQLUnionType(Object.assign(Object.assign({}, typeDef), { types: () => [] }));
UTC = new UnionTypeComposer(type, sc);
const types = typeDef.types;
if (Array.isArray(types))
UTC.setTypes(types);
else if ((0, is_1.isFunction)(types)) {
UTC.setTypes((0, configToDefine_1.convertObjectTypeArrayAsThunk)(types, sc));
}
UTC.setExtensions(typeDef.extensions);
if (Array.isArray(typeDef === null || typeDef === void 0 ? void 0 : typeDef.directives)) {
UTC.setDirectives(typeDef.directives);
}
}
else {
throw new Error(`You should provide GraphQLUnionTypeConfig or string with union name or SDL definition. Provided:\n${(0, misc_1.inspect)(typeDef)}`);
}
return UTC;
}
hasType(name) {
const typeName = (0, typeHelpers_1.getComposeTypeName)(name, this.schemaComposer);
for (const type of this._gqcTypes) {
if (type.getTypeName() === typeName) {
return true;
}
}
return false;
}
getTypes() {
return Array.from(this._gqcTypes.values());
}
getTypeComposers() {
return this.getTypes().map((t) => (0, typeHelpers_1.unwrapOutputTC)(t));
}
getTypeNames() {
return this.getTypes().map((t) => t.getTypeName());
}
clearTypes() {
this._gqcTypes.clear();
this._gqcIsModified = true;
return this;
}
setTypes(types) {
const tcs = (0, configToDefine_1.convertObjectTypeArrayAsThunk)(types, this.schemaComposer);
this._gqcTypes = new Set(tcs);
this._gqcIsModified = true;
return this;
}
addType(type) {
const tc = this._convertObjectType(type);
this.removeType(tc.getTypeName());
this._gqcTypes.add(tc);
this._gqcIsModified = true;
return this;
}
addTypes(types) {
if (!Array.isArray(types)) {
throw new Error(`UnionTypeComposer[${this.getTypeName()}].addType() accepts only array`);
}
types.forEach((type) => this.addType(type));
return this;
}
removeType(nameOrArray) {
const typeNames = Array.isArray(nameOrArray) ? nameOrArray : [nameOrArray];
typeNames.forEach((typeName) => {
for (const type of this._gqcTypes) {
if (type.getTypeName() === typeName) {
this._gqcTypes.delete(type);
this._gqcIsModified = true;
}
}
});
return this;
}
removeOtherTypes(nameOrArray) {
const keepTypeNames = Array.isArray(nameOrArray) ? nameOrArray : [nameOrArray];
for (const type of this._gqcTypes) {
if (keepTypeNames.indexOf(type.getTypeName()) === -1) {
this._gqcTypes.delete(type);
this._gqcIsModified = true;
}
}
return this;
}
getType() {
if (this._gqcIsModified) {
this._gqcIsModified = false;
this._gqType.astNode = (0, definitionNode_1.getUnionTypeDefinitionNode)(this);
const prepareTypes = () => {
try {
return this.getTypes().map((tc) => tc.getType());
}
catch (e) {
e.message = `UnionError[${this.getTypeName()}]: ${e.message}`;
throw e;
}
};
if (graphqlVersion_1.graphqlVersion >= 14) {
this._gqType._types = prepareTypes;
}
else {
this._gqType._types = null;
this._gqType._typeConfig.types = prepareTypes;
}
}
return this._gqType;
}
getTypePlural() {
return new ListComposer_1.ListComposer(this);
}
getTypeNonNull() {
return new NonNullComposer_1.NonNullComposer(this);
}
get List() {
return new ListComposer_1.ListComposer(this);
}
get NonNull() {
return new NonNullComposer_1.NonNullComposer(this);
}
getTypeName() {
return this._gqType.name;
}
setTypeName(name) {
this._gqType.name = name;
this._gqcIsModified = true;
this.schemaComposer.add(this);
return this;
}
getDescription() {
return this._gqType.description || '';
}
setDescription(description) {
this._gqType.description = description;
this._gqcIsModified = true;
return this;
}
clone(newTypeNameOrTC) {
if (!newTypeNameOrTC) {
throw new Error('You should provide newTypeName:string for UnionTypeComposer.clone()');
}
const cloned = newTypeNameOrTC instanceof UnionTypeComposer
? newTypeNameOrTC
: UnionTypeComposer.create(newTypeNameOrTC, this.schemaComposer);
cloned._gqcExtensions = Object.assign({}, this._gqcExtensions);
cloned._gqcTypes = new Set(this._gqcTypes);
cloned._gqcTypeResolvers = new Map(this._gqcTypeResolvers);
cloned._gqcFallbackResolveType = this._gqcFallbackResolveType;
cloned.setDescription(this.getDescription());
cloned.setDirectives(this.getDirectives());
return cloned;
}
cloneTo(anotherSchemaComposer, cloneMap = new Map()) {
if (!anotherSchemaComposer) {
throw new Error('You should provide SchemaComposer for ObjectTypeComposer.cloneTo()');
}
if (cloneMap.has(this))
return this;
const cloned = UnionTypeComposer.create(this.getTypeName(), anotherSchemaComposer);
cloneMap.set(this, cloned);
cloned._gqcExtensions = Object.assign({}, this._gqcExtensions);
cloned.setDescription(this.getDescription());
const typeResolversMap = this.getTypeResolvers();
if (typeResolversMap.size > 0) {
const clonedTypeResolvers = new Map();
typeResolversMap.forEach((fn, tc) => {
const clonedTC = (0, typeHelpers_1.cloneTypeTo)(tc, anotherSchemaComposer, cloneMap);
clonedTypeResolvers.set(clonedTC, fn);
});
cloned.setTypeResolvers(clonedTypeResolvers);
}
if (this._gqcFallbackResolveType) {
cloned._gqcFallbackResolveType = (0, typeHelpers_1.cloneTypeTo)(this._gqcFallbackResolveType, anotherSchemaComposer, cloneMap);
}
const types = this.getTypes();
if (types.length > 0) {
cloned.setTypes(types.map((tc) => (0, typeHelpers_1.cloneTypeTo)(tc, anotherSchemaComposer, cloneMap)));
}
return cloned;
}
merge(type) {
let tc;
if (type instanceof graphql_1.GraphQLUnionType) {
tc = UnionTypeComposer.createTemp(type, this.schemaComposer);
}
else if (type instanceof UnionTypeComposer) {
tc = type;
}
else {
throw new Error(`Cannot merge ${(0, misc_1.inspect)(type)} with UnionType(${this.getTypeName()}). Provided type should be GraphQLUnionType or UnionTypeComposer.`);
}
this.addTypes(tc.getTypes().map((t) => t.getTypeName()));
return this;
}
getResolveType() {
return this._gqType.resolveType;
}
setResolveType(fn) {
this._gqType.resolveType = fn;
this._gqcIsModified = true;
return this;
}
hasTypeResolver(type) {
const typeResolversMap = this.getTypeResolvers();
const tc = this._convertObjectType(type);
return typeResolversMap.has(tc);
}
getTypeResolvers() {
return this._gqcTypeResolvers;
}
getTypeResolverCheckFn(type) {
const typeResolversMap = this.getTypeResolvers();
const tc = this._convertObjectType(type);
if (!typeResolversMap.has(tc)) {
throw new Error(`Type resolve function in union '${this.getTypeName()}' is not defined for type ${(0, misc_1.inspect)(type)}.`);
}
return typeResolversMap.get(tc);
}
getTypeResolverNames() {
const typeResolversMap = this.getTypeResolvers();
const names = [];
typeResolversMap.forEach((_, tc) => {
names.push(tc.getTypeName());
});
return names;
}
getTypeResolverTypes() {
const typeResolversMap = this.getTypeResolvers();
return Array.from(typeResolversMap.keys());
}
setTypeResolvers(typeResolversMap) {
this._gqcTypeResolvers = this._convertTypeResolvers(typeResolversMap);
this._gqcIsModified = true;
this._initResolveTypeFn();
return this;
}
_initResolveTypeFn() {
const fallbackType = this._gqcFallbackResolveType
? (0, typeHelpers_1.getGraphQLType)(this._gqcFallbackResolveType)
: undefined;
const fastEntries = [];
if (graphqlVersion_1.graphqlVersion >= 16) {
for (const [composeType, checkFn] of this._gqcTypeResolvers.entries()) {
fastEntries.push([(0, typeHelpers_1.getComposeTypeName)(composeType, this.schemaComposer), checkFn]);
this.addType(composeType);
}
}
else {
for (const [composeType, checkFn] of this._gqcTypeResolvers.entries()) {
fastEntries.push([(0, typeHelpers_1.getGraphQLType)(composeType), checkFn]);
this.addType(composeType);
}
}
let resolveType;
const isAsyncRuntime = this._isTypeResolversAsync(this._gqcTypeResolvers);
if (isAsyncRuntime) {
resolveType = (value, context, info) => __awaiter(this, void 0, void 0, function* () {
for (const [_gqType, checkFn] of fastEntries) {
if (yield checkFn(value, context, info))
return _gqType;
}
return fallbackType;
});
}
else {
resolveType = (value, context, info) => {
for (const [_gqType, checkFn] of fastEntries) {
if (checkFn(value, context, info))
return _gqType;
}
return fallbackType;
};
}
this.setResolveType(resolveType);
return this;
}
_convertObjectType(type) {
const tc = this.schemaComposer.typeMapper.convertOutputTypeDefinition(type);
if (tc instanceof ObjectTypeComposer_1.ObjectTypeComposer || tc instanceof ThunkComposer_1.ThunkComposer) {
return tc;
}
throw new Error(`Should be provided ObjectType but received ${(0, misc_1.inspect)(type)}`);
}
_convertTypeResolvers(typeResolversMap) {
if (!(typeResolversMap instanceof Map)) {
throw new Error(`For union ${this.getTypeName()} you should provide Map object for type resolvers.`);
}
const result = new Map();
for (const [composeType, checkFn] of typeResolversMap.entries()) {
try {
result.set(this._convertObjectType(composeType), checkFn);
}
catch (e) {
throw new Error(`For union type resolver ${this.getTypeName()} you must provide GraphQLObjectType or ObjectTypeComposer, but provided ${(0, misc_1.inspect)(composeType)}`);
}
if (!(0, is_1.isFunction)(checkFn)) {
throw new Error(`Union ${this.getTypeName()} has invalid check function for type ${(0, misc_1.inspect)(composeType)}`);
}
}
return result;
}
_isTypeResolversAsync(typeResolversMap) {
let res = false;
for (const [, checkFn] of typeResolversMap.entries()) {
try {
const r = checkFn({}, {}, {});
if (r instanceof Promise) {
r.catch(() => { });
res = true;
}
}
catch (e) {
}
}
return res;
}
addTypeResolver(type, checkFn) {
const typeResolversMap = this.getTypeResolvers();
const tc = this._convertObjectType(type);
typeResolversMap.set(tc, checkFn);
this.schemaComposer.addSchemaMustHaveType(tc);
this.setTypeResolvers(typeResolversMap);
return this;
}
removeTypeResolver(type) {
const typeResolversMap = this.getTypeResolvers();
const tc = this._convertObjectType(type);
typeResolversMap.delete(tc);
this.setTypeResolvers(typeResolversMap);
return this;
}
setTypeResolverFallback(type) {
if (type) {
this.addType(type);
this.schemaComposer.addSchemaMustHaveType(type);
}
this._gqcFallbackResolveType = type;
this._gqcIsModified = true;
this._initResolveTypeFn();
return this;
}
getExtensions() {
if (!this._gqcExtensions) {
return {};
}
else {
return this._gqcExtensions;
}
}
setExtensions(extensions) {
this._gqcExtensions = extensions;
this._gqcIsModified = true;
return this;
}
extendExtensions(extensions) {
const current = this.getExtensions();
this.setExtensions(Object.assign(Object.assign({}, current), extensions));
return this;
}
clearExtensions() {
this.setExtensions({});
return this;
}
getExtension(extensionName) {
const extensions = this.getExtensions();
return extensions[extensionName];
}
hasExtension(extensionName) {
const extensions = this.getExtensions();
return extensionName in extensions;
}
setExtension(extensionName, value) {
this.extendExtensions({
[extensionName]: value,
});
return this;
}
removeExtension(extensionName) {
const extensions = Object.assign({}, this.getExtensions());
delete extensions[extensionName];
this.setExtensions(extensions);
return this;
}
getDirectives() {
return this._gqcDirectives || [];
}
setDirectives(directives) {
this._gqcDirectives = directives;
this._gqcIsModified = true;
return this;
}
getDirectiveNames() {
return this.getDirectives().map((d) => d.name);
}
getDirectiveByName(directiveName) {
const directive = this.getDirectives().find((d) => d.name === directiveName);
if (!directive)
return undefined;
return directive.args;
}
setDirectiveByName(directiveName, args) {
const directives = this.getDirectives();
const idx = directives.findIndex((d) => d.name === directiveName);
if (idx >= 0) {
directives[idx].args = args;
}
else {
directives.push({ name: directiveName, args });
}
this.setDirectives(directives);
return this;
}
getDirectiveById(idx) {
const directive = this.getDirectives()[idx];
if (!directive)
return undefined;
return directive.args;
}
getNestedTCs(opts = {}, passedTypes = new Set()) {
const exclude = Array.isArray(opts.exclude) ? opts.exclude : [];
this.getTypeComposers().forEach((tc) => {
if (!passedTypes.has(tc) && !exclude.includes(tc.getTypeName())) {
passedTypes.add(tc);
if (tc instanceof ObjectTypeComposer_1.ObjectTypeComposer) {
tc.getNestedTCs(opts, passedTypes);
}
}
});
return passedTypes;
}
toSDL(opts) {
const _a = opts || {}, { deep } = _a, innerOpts = __rest(_a, ["deep"]);
innerOpts.sortTypes = innerOpts.sortTypes || false;
const exclude = Array.isArray(innerOpts.exclude) ? innerOpts.exclude : [];
if (deep) {
let r = '';
r += (0, schemaPrinter_1.printUnion)(this.getType(), innerOpts);
const nestedTypes = Array.from(this.getNestedTCs({ exclude }));
const sortMethod = (0, schemaPrinterSortTypes_1.getSortMethodFromOption)(innerOpts.sortAll || innerOpts.sortTypes);
if (sortMethod) {
nestedTypes.sort(sortMethod);
}
nestedTypes.forEach((t) => {
if (t !== this && !exclude.includes(t.getTypeName())) {
const sdl = t.toSDL(innerOpts);
if (sdl)
r += `\n\n${sdl}`;
}
});
return r;
}
return (0, schemaPrinter_1.printUnion)(this.getType(), innerOpts);
}
}
exports.UnionTypeComposer = UnionTypeComposer;
//# sourceMappingURL=UnionTypeComposer.js.map