UNPKG

graphql-compose

Version:

GraphQL schema builder from different data sources with middleware extensions.

1,194 lines • 50.3 kB
"use strict";
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.ObjectTypeComposer = void 0;
const graphql_1 = require("./graphql");
const InputTypeComposer_1 = require("./InputTypeComposer");
const UnionTypeComposer_1 = require("./UnionTypeComposer");
const InterfaceTypeComposer_1 = require("./InterfaceTypeComposer");
const Resolver_1 = require("./Resolver");
const SchemaComposer_1 = require("./SchemaComposer");
const ListComposer_1 = require("./ListComposer");
const NonNullComposer_1 = require("./NonNullComposer");
const ThunkComposer_1 = require("./ThunkComposer");
const EnumTypeComposer_1 = require("./EnumTypeComposer");
const misc_1 = require("./utils/misc");
const is_1 = require("./utils/is");
const configToDefine_1 = require("./utils/configToDefine");
const toInputType_1 = require("./utils/toInputType");
const typeByPath_1 = require("./utils/typeByPath");
const typeHelpers_1 = require("./utils/typeHelpers");
const graphqlVersion_1 = require("./utils/graphqlVersion");
const createThunkedObjectProxy_1 = require("./utils/createThunkedObjectProxy");
const schemaPrinter_1 = require("./utils/schemaPrinter");
const definitionNode_1 = require("./utils/definitionNode");
const schemaPrinterSortTypes_1 = require("./utils/schemaPrinterSortTypes");
class ObjectTypeComposer {
    static create(typeDef, schemaComposer) {
        if (!(schemaComposer instanceof SchemaComposer_1.SchemaComposer)) {
            throw new Error('You must provide SchemaComposer instance as a second argument for `ObjectTypeComposer.create(typeDef, schemaComposer)`');
        }
        if (schemaComposer.hasInstance(typeDef, ObjectTypeComposer)) {
            return schemaComposer.getOTC(typeDef);
        }
        const tc = this.createTemp(typeDef, schemaComposer);
        const typeName = tc.getTypeName();
        if (typeName !== 'Query' && typeName !== 'Mutation' && typeName !== 'Subscription') {
            schemaComposer.add(tc);
        }
        return tc;
    }
    static createTemp(typeDef, schemaComposer) {
        const sc = schemaComposer || new SchemaComposer_1.SchemaComposer();
        let TC;
        if ((0, is_1.isString)(typeDef)) {
            const typeName = typeDef;
            if ((0, typeHelpers_1.isTypeNameString)(typeName)) {
                TC = new ObjectTypeComposer(new graphql_1.GraphQLObjectType({
                    name: typeName,
                    fields: () => ({}),
                }), sc);
            }
            else {
                TC = sc.typeMapper.convertSDLTypeDefinition(typeName);
                if (!(TC instanceof ObjectTypeComposer)) {
                    throw new Error('You should provide correct GraphQLObjectType type definition. ' +
                        'Eg. `type MyType { name: String }`');
                }
            }
        }
        else if (typeDef instanceof graphql_1.GraphQLObjectType) {
            TC = new ObjectTypeComposer(typeDef, sc);
        }
        else if (typeDef instanceof ObjectTypeComposer) {
            return typeDef;
        }
        else if ((0, is_1.isObject)(typeDef)) {
            const type = new graphql_1.GraphQLObjectType(Object.assign(Object.assign({}, typeDef), { fields: () => ({}) }));
            TC = new ObjectTypeComposer(type, sc);
            const fields = typeDef.fields;
            if ((0, is_1.isFunction)(fields)) {
                TC.addFields((0, configToDefine_1.convertObjectFieldMapToConfig)(fields, sc));
            }
            else if ((0, is_1.isObject)(fields)) {
                TC.addFields(fields);
            }
            const interfaces = typeDef.interfaces;
            if (Array.isArray(interfaces))
                TC.setInterfaces(interfaces);
            else if ((0, is_1.isFunction)(interfaces)) {
                TC.setInterfaces((0, configToDefine_1.convertInterfaceArrayAsThunk)(interfaces, sc));
            }
            TC.setExtensions(typeDef.extensions);
            if (Array.isArray(typeDef === null || typeDef === void 0 ? void 0 : typeDef.directives)) {
                TC.setDirectives(typeDef.directives);
            }
        }
        else {
            throw new Error(`You should provide GraphQLObjectTypeConfig or string with type name to ObjectTypeComposer.create(opts). Provided:\n${(0, misc_1.inspect)(typeDef)}`);
        }
        return TC;
    }
    constructor(graphqlType, schemaComposer) {
        if (!(schemaComposer instanceof SchemaComposer_1.SchemaComposer)) {
            throw new Error('You must provide SchemaComposer instance as a second argument for `new ObjectTypeComposer(GraphQLObjectType, SchemaComposer)`');
        }
        if (!(graphqlType instanceof graphql_1.GraphQLObjectType)) {
            throw new Error('ObjectTypeComposer accept only GraphQLObjectType in constructor');
        }
        this.schemaComposer = schemaComposer;
        this._gqType = graphqlType;
        this.schemaComposer.set(graphqlType, this);
        const typename = graphqlType.name;
        if (typename !== 'Query' && typename !== 'Mutation' && typename !== 'Subscription') {
            this.schemaComposer.set(typename, this);
        }
        if (graphqlVersion_1.graphqlVersion >= 14) {
            this._gqcFields = (0, configToDefine_1.convertObjectFieldMapToConfig)(this._gqType._fields, this.schemaComposer);
            this._gqcInterfaces = (0, configToDefine_1.convertInterfaceArrayAsThunk)(this._gqType._interfaces, this.schemaComposer);
        }
        else {
            const fields = this._gqType._typeConfig
                .fields;
            this._gqcFields = this.schemaComposer.typeMapper.convertOutputFieldConfigMap(((0, misc_1.resolveMaybeThunk)(fields) || {}), this.getTypeName());
            this._gqcInterfaces = (0, configToDefine_1.convertInterfaceArrayAsThunk)(this._gqType._interfaces || this._gqType._typeConfig.interfaces, this.schemaComposer);
        }
        if (!this._gqType.astNode) {
            this._gqType.astNode = (0, definitionNode_1.getObjectTypeDefinitionNode)(this);
        }
        this._gqcIsModified = false;
    }
    getFields() {
        return this._gqcFields;
    }
    getFieldNames() {
        return Object.keys(this._gqcFields);
    }
    getField(fieldName) {
        if ((0, is_1.isFunction)(this._gqcFields[fieldName])) {
            const unwrappedFieldConfig = this._gqcFields[fieldName](this.schemaComposer);
            this.setField(fieldName, unwrappedFieldConfig);
        }
        const field = this._gqcFields[fieldName];
        if (!field) {
            throw new Error(`Cannot get field '${fieldName}' from type '${this.getTypeName()}'. Field does not exist.`);
        }
        return field;
    }
    hasField(fieldName) {
        return !!this._gqcFields[fieldName];
    }
    setFields(fields) {
        this._gqcFields = {};
        Object.keys(fields).forEach((name) => {
            this.setField(name, fields[name]);
        });
        return this;
    }
    setField(fieldName, fieldConfig) {
        this._gqcFields[fieldName] = (0, is_1.isFunction)(fieldConfig)
            ? fieldConfig
            : this.schemaComposer.typeMapper.convertOutputFieldConfig(fieldConfig, fieldName, this.getTypeName());
        this._gqcIsModified = true;
        return this;
    }
    addFields(newFields) {
        Object.keys(newFields).forEach((name) => {
            this.setField(name, newFields[name]);
        });
        return this;
    }
    addNestedFields(newFields) {
        Object.keys(newFields).forEach((fieldName) => {
            const fc = newFields[fieldName];
            const names = fieldName.split('.');
            const name = names.shift();
            if (!name) {
                throw new Error(`Type ${this.getTypeName()} has invalid field name: ${fieldName}`);
            }
            if (names.length === 0) {
                this.setField(name, fc);
            }
            else {
                let childTC;
                if (!this.hasField(name)) {
                    childTC = ObjectTypeComposer.create(`${this.getTypeName()}${(0, misc_1.upperFirst)(name)}`, this.schemaComposer);
                    this.setField(name, {
                        type: childTC,
                        resolve: () => ({}),
                    });
                }
                else {
                    childTC = this.getFieldTC(name);
                }
                if (childTC instanceof ObjectTypeComposer) {
                    childTC.addNestedFields({ [names.join('.')]: fc });
                }
            }
        });
        return this;
    }
    removeField(fieldNameOrArray) {
        const fieldNames = Array.isArray(fieldNameOrArray) ? fieldNameOrArray : [fieldNameOrArray];
        fieldNames.forEach((fieldName) => {
            const names = fieldName.split('.');
            const name = names.shift();
            if (!name)
                return;
            if (names.length === 0) {
                delete this._gqcFields[name];
                this._gqcIsModified = true;
            }
            else {
                if (this.hasField(name)) {
                    const subTC = this.getFieldTC(name);
                    if (subTC instanceof ObjectTypeComposer || subTC instanceof EnumTypeComposer_1.EnumTypeComposer) {
                        subTC.removeField(names.join('.'));
                    }
                }
            }
        });
        return this;
    }
    removeOtherFields(fieldNameOrArray) {
        const keepFieldNames = Array.isArray(fieldNameOrArray) ? fieldNameOrArray : [fieldNameOrArray];
        Object.keys(this._gqcFields).forEach((fieldName) => {
            if (keepFieldNames.indexOf(fieldName) === -1) {
                delete this._gqcFields[fieldName];
                this._gqcIsModified = true;
            }
        });
        return this;
    }
    reorderFields(names) {
        const orderedFields = {};
        const fields = this._gqcFields;
        names.forEach((name) => {
            if (fields[name]) {
                orderedFields[name] = fields[name];
                delete fields[name];
            }
        });
        this._gqcFields = Object.assign(Object.assign({}, orderedFields), fields);
        this._gqcIsModified = true;
        return this;
    }
    extendField(fieldName, partialFieldConfig) {
        let prevFieldConfig;
        try {
            prevFieldConfig = this.getField(fieldName);
        }
        catch (e) {
            throw new Error(`Cannot extend field '${fieldName}' from type '${this.getTypeName()}'. Field does not exist.`);
        }
        this.setField(fieldName, Object.assign(Object.assign(Object.assign({}, prevFieldConfig), partialFieldConfig), { extensions: Object.assign(Object.assign({}, (prevFieldConfig.extensions || {})), (partialFieldConfig.extensions || {})), directives: [...(prevFieldConfig.directives || []), ...(partialFieldConfig.directives || [])] }));
        return this;
    }
    getFieldConfig(fieldName) {
        const _a = this.getField(fieldName), { type, args } = _a, rest = __rest(_a, ["type", "args"]);
        return Object.assign({ type: type.getType(), args: args &&
                (0, misc_1.mapEachKey)(args, (ac) => (Object.assign(Object.assign({}, ac), { type: ac.type.getType() }))) }, rest);
    }
    getFieldType(fieldName) {
        return this.getField(fieldName).type.getType();
    }
    getFieldTypeName(fieldName) {
        return this.getField(fieldName).type.getTypeName();
    }
    getFieldTC(fieldName) {
        const anyTC = this.getField(fieldName).type;
        return (0, typeHelpers_1.unwrapOutputTC)(anyTC);
    }
    getFieldOTC(fieldName) {
        const tc = this.getFieldTC(fieldName);
        if (!(tc instanceof ObjectTypeComposer)) {
            throw new Error(`${this.getTypeName()}.getFieldOTC('${fieldName}') must be ObjectTypeComposer, but received ${tc.constructor.name}. Maybe you need to use 'getFieldTC()' method which returns any type composer?`);
        }
        return tc;
    }
    isFieldNonNull(fieldName) {
        return this.getField(fieldName).type instanceof NonNullComposer_1.NonNullComposer;
    }
    makeFieldNonNull(fieldNameOrArray) {
        const fieldNames = Array.isArray(fieldNameOrArray) ? fieldNameOrArray : [fieldNameOrArray];
        fieldNames.forEach((fieldName) => {
            const fc = this._gqcFields[fieldName];
            if (fc && !(fc.type instanceof NonNullComposer_1.NonNullComposer)) {
                fc.type = new NonNullComposer_1.NonNullComposer(fc.type);
                this._gqcIsModified = true;
            }
        });
        return this;
    }
    makeFieldNullable(fieldNameOrArray) {
        const fieldNames = Array.isArray(fieldNameOrArray) ? fieldNameOrArray : [fieldNameOrArray];
        fieldNames.forEach((fieldName) => {
            const fc = this._gqcFields[fieldName];
            if (fc && fc.type instanceof NonNullComposer_1.NonNullComposer) {
                fc.type = fc.type.ofType;
                this._gqcIsModified = true;
            }
        });
        return this;
    }
    isFieldPlural(fieldName) {
        const type = this.getField(fieldName).type;
        return (type instanceof ListComposer_1.ListComposer ||
            (type instanceof NonNullComposer_1.NonNullComposer && type.ofType instanceof ListComposer_1.ListComposer));
    }
    makeFieldPlural(fieldNameOrArray) {
        const fieldNames = Array.isArray(fieldNameOrArray) ? fieldNameOrArray : [fieldNameOrArray];
        fieldNames.forEach((fieldName) => {
            const fc = this._gqcFields[fieldName];
            if (fc && !(fc.type instanceof ListComposer_1.ListComposer)) {
                fc.type = new ListComposer_1.ListComposer(fc.type);
                this._gqcIsModified = true;
            }
        });
        return this;
    }
    makeFieldNonPlural(fieldNameOrArray) {
        const fieldNames = Array.isArray(fieldNameOrArray) ? fieldNameOrArray : [fieldNameOrArray];
        fieldNames.forEach((fieldName) => {
            const fc = this._gqcFields[fieldName];
            if (fc) {
                if (fc.type instanceof ListComposer_1.ListComposer) {
                    fc.type = fc.type.ofType;
                    this._gqcIsModified = true;
                }
                else if (fc.type instanceof NonNullComposer_1.NonNullComposer && fc.type.ofType instanceof ListComposer_1.ListComposer) {
                    fc.type =
                        fc.type.ofType.ofType instanceof NonNullComposer_1.NonNullComposer
                            ? fc.type.ofType.ofType
                            : new NonNullComposer_1.NonNullComposer(fc.type.ofType.ofType);
                    this._gqcIsModified = true;
                }
            }
        });
        return this;
    }
    deprecateFields(fields) {
        const existedFieldNames = this.getFieldNames();
        if (typeof fields === 'string') {
            if (existedFieldNames.indexOf(fields) === -1) {
                throw new Error(`Cannot deprecate non-existent field '${fields}' from type '${this.getTypeName()}'`);
            }
            this.extendField(fields, { deprecationReason: 'deprecated' });
        }
        else if (Array.isArray(fields)) {
            fields.forEach((field) => {
                if (existedFieldNames.indexOf(field) === -1) {
                    throw new Error(`Cannot deprecate non-existent field '${field}' from type '${this.getTypeName()}'`);
                }
                this.extendField(field, { deprecationReason: 'deprecated' });
            });
        }
        else {
            const fieldMap = fields;
            Object.keys(fieldMap).forEach((field) => {
                if (existedFieldNames.indexOf(field) === -1) {
                    throw new Error(`Cannot deprecate non-existent field '${field}' from type '${this.getTypeName()}'`);
                }
                const deprecationReason = fieldMap[field];
                this.extendField(field, { deprecationReason });
            });
        }
        return this;
    }
    getFieldArgs(fieldName) {
        try {
            const fc = this.getField(fieldName);
            return fc.args || {};
        }
        catch (e) {
            throw new Error(`Cannot get args from '${this.getTypeName()}.${fieldName}'. Field does not exist.`);
        }
    }
    getFieldArgNames(fieldName) {
        return Object.keys(this.getFieldArgs(fieldName));
    }
    hasFieldArg(fieldName, argName) {
        try {
            const fieldArgs = this.getFieldArgs(fieldName);
            return !!fieldArgs[argName];
        }
        catch (e) {
            return false;
        }
    }
    getFieldArg(fieldName, argName) {
        const fieldArgs = this.getFieldArgs(fieldName);
        const arg = fieldArgs[argName];
        if (!arg) {
            throw new Error(`Cannot get '${this.getTypeName()}.${fieldName}@${argName}'. Argument does not exist.`);
        }
        return arg;
    }
    getFieldArgType(fieldName, argName) {
        const ac = this.getFieldArg(fieldName, argName);
        return ac.type.getType();
    }
    getFieldArgTypeName(fieldName, argName) {
        const ac = this.getFieldArg(fieldName, argName);
        return ac.type.getTypeName();
    }
    getFieldArgTC(fieldName, argName) {
        const anyTC = this.getFieldArg(fieldName, argName).type;
        return (0, typeHelpers_1.unwrapInputTC)(anyTC);
    }
    getFieldArgITC(fieldName, argName) {
        const tc = this.getFieldArgTC(fieldName, argName);
        if (!(tc instanceof InputTypeComposer_1.InputTypeComposer)) {
            throw new Error(`${this.getTypeName()}.getFieldArgITC('${fieldName}', '${argName}') must be InputTypeComposer, but received ${tc.constructor.name}. Maybe you need to use 'getFieldArgTC()' method which returns any type composer?`);
        }
        return tc;
    }
    setFieldArgs(fieldName, args) {
        const fc = this.getField(fieldName);
        fc.args = this.schemaComposer.typeMapper.convertArgConfigMap(args, fieldName, this.getTypeName());
        this._gqcIsModified = true;
        return this;
    }
    addFieldArgs(fieldName, newArgs) {
        const fc = this.getField(fieldName);
        fc.args = Object.assign(Object.assign({}, fc.args), this.schemaComposer.typeMapper.convertArgConfigMap(newArgs, fieldName, this.getTypeName()));
        this._gqcIsModified = true;
        return this;
    }
    setFieldArg(fieldName, argName, argConfig) {
        const fc = this.getField(fieldName);
        fc.args = fc.args || {};
        fc.args[argName] = this.schemaComposer.typeMapper.convertArgConfig(argConfig, argName, fieldName, this.getTypeName());
        this._gqcIsModified = true;
        return this;
    }
    removeFieldArg(fieldName, argNameOrArray) {
        const argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray];
        const args = this._gqcFields[fieldName] && this._gqcFields[fieldName].args;
        if (args) {
            argNames.forEach((argName) => delete args[argName]);
            this._gqcIsModified = true;
        }
        return this;
    }
    removeFieldOtherArgs(fieldName, argNameOrArray) {
        const keepArgNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray];
        const args = this._gqcFields[fieldName] && this._gqcFields[fieldName].args;
        if (args) {
            Object.keys(args).forEach((argName) => {
                if (keepArgNames.indexOf(argName) === -1) {
                    delete args[argName];
                    this._gqcIsModified = true;
                }
            });
        }
        return this;
    }
    isFieldArgPlural(fieldName, argName) {
        const type = this.getFieldArg(fieldName, argName).type;
        return (type instanceof ListComposer_1.ListComposer ||
            (type instanceof NonNullComposer_1.NonNullComposer && type.ofType instanceof ListComposer_1.ListComposer));
    }
    makeFieldArgPlural(fieldName, argNameOrArray) {
        const args = this.getField(fieldName).args;
        if (!args)
            return this;
        const argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray];
        argNames.forEach((argName) => {
            const ac = args[argName];
            if (ac && !(ac.type instanceof ListComposer_1.ListComposer)) {
                ac.type = new ListComposer_1.ListComposer(ac.type);
                this._gqcIsModified = true;
            }
        });
        return this;
    }
    makeFieldArgNonPlural(fieldName, argNameOrArray) {
        const args = this.getField(fieldName).args;
        if (!args)
            return this;
        const argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray];
        argNames.forEach((argName) => {
            const ac = args[argName];
            if (ac) {
                if (ac.type instanceof ListComposer_1.ListComposer) {
                    ac.type = ac.type.ofType;
                    this._gqcIsModified = true;
                }
                else if (ac.type instanceof NonNullComposer_1.NonNullComposer && ac.type.ofType instanceof ListComposer_1.ListComposer) {
                    ac.type =
                        ac.type.ofType.ofType instanceof NonNullComposer_1.NonNullComposer
                            ? ac.type.ofType.ofType
                            : new NonNullComposer_1.NonNullComposer(ac.type.ofType.ofType);
                    this._gqcIsModified = true;
                }
            }
        });
        return this;
    }
    isFieldArgNonNull(fieldName, argName) {
        const type = this.getFieldArg(fieldName, argName).type;
        return type instanceof NonNullComposer_1.NonNullComposer;
    }
    makeFieldArgNonNull(fieldName, argNameOrArray) {
        const args = this.getField(fieldName).args;
        if (!args)
            return this;
        const argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray];
        argNames.forEach((argName) => {
            const ac = args[argName];
            if (ac && !(ac.type instanceof NonNullComposer_1.NonNullComposer)) {
                ac.type = new NonNullComposer_1.NonNullComposer(ac.type);
                this._gqcIsModified = true;
            }
        });
        return this;
    }
    makeFieldArgNullable(fieldName, argNameOrArray) {
        const args = this.getField(fieldName).args;
        if (!args)
            return this;
        const argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray];
        argNames.forEach((argName) => {
            const ac = args[argName];
            if (ac && ac.type instanceof NonNullComposer_1.NonNullComposer) {
                ac.type = ac.type.ofType;
                this._gqcIsModified = true;
            }
        });
        return this;
    }
    getType() {
        if (this._gqcIsModified) {
            this._gqcIsModified = false;
            this._gqType.astNode = (0, definitionNode_1.getObjectTypeDefinitionNode)(this);
            if (graphqlVersion_1.graphqlVersion >= 14) {
                this._gqType._fields = () => (0, configToDefine_1.defineFieldMap)(this._gqType, (0, misc_1.mapEachKey)(this._gqcFields, (_, name) => this.getFieldConfig(name)), this._gqType.astNode);
                this._gqType._interfaces = () => this.getInterfacesTypes();
            }
            else {
                this._gqType._typeConfig.fields = () => {
                    return (0, misc_1.mapEachKey)(this._gqcFields, (_, name) => this.getFieldConfig(name));
                };
                this._gqType._typeConfig.interfaces = () => this.getInterfacesTypes();
                delete this._gqType._fields;
                delete this._gqType._interfaces;
            }
        }
        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 ObjectTypeComposer.clone()');
        }
        const cloned = newTypeNameOrTC instanceof ObjectTypeComposer
            ? newTypeNameOrTC
            : ObjectTypeComposer.create(newTypeNameOrTC, this.schemaComposer);
        cloned._gqcFields = (0, misc_1.mapEachKey)(this._gqcFields, (fieldConfig) => (Object.assign(Object.assign({}, fieldConfig), { args: (0, misc_1.mapEachKey)(fieldConfig.args || {}, (argConfig) => (Object.assign(Object.assign({}, argConfig), { extensions: Object.assign({}, argConfig.extensions), directives: [...(argConfig.directives || [])] }))), extensions: Object.assign({}, fieldConfig.extensions), directives: [...(fieldConfig.directives || [])] })));
        cloned._gqcInterfaces = [...this._gqcInterfaces];
        cloned._gqcExtensions = Object.assign({}, this._gqcExtensions);
        cloned._gqcGetRecordIdFn = this._gqcGetRecordIdFn;
        cloned.setDescription(this.getDescription());
        cloned.setDirectives(this.getDirectives());
        this.getResolvers().forEach((resolver) => {
            const newResolver = resolver.clone();
            newResolver.type = (0, typeHelpers_1.replaceTC)(newResolver.type, (tc) => {
                return tc === this ? cloned : tc;
            });
            cloned.addResolver(newResolver);
        });
        return cloned;
    }
    cloneTo(anotherSchemaComposer, cloneMap = new Map()) {
        if (!anotherSchemaComposer) {
            throw new Error('You should provide SchemaComposer for ObjectTypeComposer.cloneTo()');
        }
        if (cloneMap.has(this))
            return cloneMap.get(this);
        const cloned = ObjectTypeComposer.create(this.getTypeName(), anotherSchemaComposer);
        cloneMap.set(this, cloned);
        cloned._gqcFields = (0, misc_1.mapEachKey)(this._gqcFields, (fieldConfig) => (Object.assign(Object.assign({}, fieldConfig), { type: (0, typeHelpers_1.cloneTypeTo)(fieldConfig.type, anotherSchemaComposer, cloneMap), args: (0, misc_1.mapEachKey)(fieldConfig.args, (argConfig) => (Object.assign(Object.assign({}, argConfig), { type: (0, typeHelpers_1.cloneTypeTo)(argConfig.type, anotherSchemaComposer, cloneMap), extensions: Object.assign({}, argConfig.extensions), directives: [...(argConfig.directives || [])] }))), extensions: Object.assign({}, fieldConfig.extensions), directives: [...(fieldConfig.directives || [])] })));
        cloned._gqcInterfaces = this._gqcInterfaces.map((i) => i.cloneTo(anotherSchemaComposer, cloneMap));
        cloned._gqcExtensions = Object.assign({}, this._gqcExtensions);
        cloned._gqcGetRecordIdFn = this._gqcGetRecordIdFn;
        cloned.setDescription(this.getDescription());
        cloned.setDirectives(this.getDirectives());
        this.getResolvers().forEach((resolver) => {
            const clonedResolver = resolver.cloneTo(anotherSchemaComposer, cloneMap);
            cloned.addResolver(clonedResolver);
        });
        return cloned;
    }
    getIsTypeOf() {
        return this._gqType.isTypeOf;
    }
    setIsTypeOf(fn) {
        this._gqType.isTypeOf = fn;
        this._gqcIsModified = true;
        return this;
    }
    merge(type) {
        let tc;
        if (type instanceof ObjectTypeComposer || type instanceof InterfaceTypeComposer_1.InterfaceTypeComposer) {
            tc = type;
        }
        else if (type instanceof graphql_1.GraphQLObjectType) {
            tc = ObjectTypeComposer.createTemp(type, this.schemaComposer);
        }
        else if (type instanceof graphql_1.GraphQLInterfaceType) {
            tc = InterfaceTypeComposer_1.InterfaceTypeComposer.createTemp(type, this.schemaComposer);
        }
        else {
            throw new Error(`Cannot merge ${(0, misc_1.inspect)(type)} with ObjectType(${this.getTypeName()}). Provided type should be GraphQLInterfaceType, GraphQLObjectType, InterfaceTypeComposer or ObjectTypeComposer.`);
        }
        const fields = Object.assign({}, tc.getFields());
        Object.keys(fields).forEach((fieldName) => {
            fields[fieldName] = Object.assign(Object.assign({}, fields[fieldName]), { args: Object.assign({}, fields[fieldName].args), type: tc.getFieldTypeName(fieldName) });
            tc.getFieldArgNames(fieldName).forEach((argName) => {
                fields[fieldName].args[argName] = Object.assign(Object.assign({}, fields[fieldName].args[argName]), { type: tc.getFieldArgTypeName(fieldName, argName) });
            });
        });
        this.addFields(fields);
        this.addInterfaces(tc.getInterfaces().map((i) => i.getTypeName()));
        return this;
    }
    getInputType() {
        return this.getInputTypeComposer().getType();
    }
    hasInputTypeComposer() {
        return !!this._gqcInputTypeComposer;
    }
    setInputTypeComposer(itc) {
        this._gqcInputTypeComposer = itc;
        return this;
    }
    getInputTypeComposer(opts) {
        if (!this._gqcInputTypeComposer) {
            this._gqcInputTypeComposer = (0, toInputType_1.toInputObjectType)(this, opts);
        }
        return this._gqcInputTypeComposer;
    }
    getITC(opts) {
        return this.getInputTypeComposer(opts);
    }
    removeInputTypeComposer() {
        this._gqcInputTypeComposer = undefined;
        return this;
    }
    getResolvers() {
        if (!this._gqcResolvers) {
            this._gqcResolvers = new Map();
        }
        return this._gqcResolvers;
    }
    hasResolver(name) {
        if (!this._gqcResolvers) {
            return false;
        }
        return this._gqcResolvers.has(name);
    }
    getResolver(name, middlewares) {
        if (!this.hasResolver(name)) {
            throw new Error(`Type ${this.getTypeName()} does not have resolver with name '${name}'`);
        }
        const resolverMap = this._gqcResolvers;
        const resolver = resolverMap.get(name);
        if (Array.isArray(middlewares)) {
            return resolver.withMiddlewares(middlewares);
        }
        return resolver;
    }
    setResolver(name, resolver) {
        if (!this._gqcResolvers) {
            this._gqcResolvers = new Map();
        }
        if (!(resolver instanceof Resolver_1.Resolver)) {
            throw new Error('setResolver() accept only Resolver instance');
        }
        this._gqcResolvers.set(name, resolver);
        resolver.setDisplayName(`${this.getTypeName()}.${resolver.name}`);
        return this;
    }
    addResolver(opts) {
        if (!opts) {
            throw new Error('addResolver called with empty Resolver');
        }
        let resolver;
        if (!(opts instanceof Resolver_1.Resolver)) {
            const resolverOpts = Object.assign({}, opts);
            if (!resolverOpts.hasOwnProperty('resolve')) {
                resolverOpts.resolve = () => ({});
            }
            resolver = new Resolver_1.Resolver(resolverOpts, this.schemaComposer);
        }
        else {
            resolver = opts;
        }
        if (!resolver.name) {
            throw new Error('resolver should have non-empty `name` property');
        }
        this.setResolver(resolver.name, resolver);
        return this;
    }
    removeResolver(resolverName) {
        if (resolverName) {
            this.getResolvers().delete(resolverName);
        }
        return this;
    }
    wrapResolver(resolverName, cbResolver) {
        const resolver = this.getResolver(resolverName);
        const newResolver = resolver.wrap(cbResolver);
        this.setResolver(resolverName, newResolver);
        return this;
    }
    wrapResolverAs(resolverName, fromResolverName, cbResolver) {
        const resolver = this.getResolver(fromResolverName);
        const newResolver = resolver.wrap(cbResolver);
        this.setResolver(resolverName, newResolver);
        return this;
    }
    wrapResolverResolve(resolverName, cbNextRp) {
        const resolver = this.getResolver(resolverName);
        this.setResolver(resolverName, resolver.wrapResolve(cbNextRp));
        return this;
    }
    getInterfaces() {
        return this._gqcInterfaces;
    }
    getInterfacesTypes() {
        return this._gqcInterfaces.map((i) => i.getType());
    }
    setInterfaces(interfaces) {
        this._gqcInterfaces = (0, configToDefine_1.convertInterfaceArrayAsThunk)(interfaces, this.schemaComposer);
        this._gqcIsModified = true;
        return this;
    }
    hasInterface(iface) {
        const typeName = (0, typeHelpers_1.getComposeTypeName)(iface, this.schemaComposer);
        return !!this._gqcInterfaces.find((i) => i.getTypeName() === typeName);
    }
    addInterface(iface) {
        if (!this.hasInterface(iface)) {
            this._gqcInterfaces.push(this.schemaComposer.typeMapper.convertInterfaceTypeDefinition(iface));
            this._gqcIsModified = true;
        }
        return this;
    }
    addInterfaces(ifaces) {
        if (!Array.isArray(ifaces)) {
            throw new Error(`ObjectTypeComposer[${this.getTypeName()}].addInterfaces() accepts only array`);
        }
        ifaces.forEach((iface) => this.addInterface(iface));
        return this;
    }
    removeInterface(iface) {
        const typeName = (0, typeHelpers_1.getComposeTypeName)(iface, this.schemaComposer);
        this._gqcInterfaces = this._gqcInterfaces.filter((i) => i.getTypeName() !== typeName);
        this._gqcIsModified = true;
        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;
    }
    getFieldExtensions(fieldName) {
        const field = this.getField(fieldName);
        return field.extensions || {};
    }
    setFieldExtensions(fieldName, extensions) {
        const field = this.getField(fieldName);
        this.setField(fieldName, Object.assign(Object.assign({}, field), { extensions }));
        return this;
    }
    extendFieldExtensions(fieldName, extensions) {
        const current = this.getFieldExtensions(fieldName);
        this.setFieldExtensions(fieldName, Object.assign(Object.assign({}, current), extensions));
        return this;
    }
    clearFieldExtensions(fieldName) {
        this.setFieldExtensions(fieldName, {});
        return this;
    }
    getFieldExtension(fieldName, extensionName) {
        const extensions = this.getFieldExtensions(fieldName);
        return extensions[extensionName];
    }
    hasFieldExtension(fieldName, extensionName) {
        const extensions = this.getFieldExtensions(fieldName);
        return extensionName in extensions;
    }
    setFieldExtension(fieldName, extensionName, value) {
        this.extendFieldExtensions(fieldName, {
            [extensionName]: value,
        });
        return this;
    }
    removeFieldExtension(fieldName, extensionName) {
        const extensions = Object.assign({}, this.getFieldExtensions(fieldName));
        delete extensions[extensionName];
        this.setFieldExtensions(fieldName, extensions);
        return this;
    }
    getFieldArgExtensions(fieldName, argName) {
        const ac = this.getFieldArg(fieldName, argName);
        return ac.extensions || {};
    }
    setFieldArgExtensions(fieldName, argName, extensions) {
        const ac = this.getFieldArg(fieldName, argName);
        this.setFieldArg(fieldName, argName, Object.assign(Object.assign({}, ac), { extensions }));
        return this;
    }
    extendFieldArgExtensions(fieldName, argName, extensions) {
        const current = this.getFieldArgExtensions(fieldName, argName);
        this.setFieldArgExtensions(fieldName, argName, Object.assign(Object.assign({}, current), extensions));
        return this;
    }
    clearFieldArgExtensions(fieldName, argName) {
        this.setFieldArgExtensions(fieldName, argName, {});
        return this;
    }
    getFieldArgExtension(fieldName, argName, extensionName) {
        const extensions = this.getFieldArgExtensions(fieldName, argName);
        return extensions[extensionName];
    }
    hasFieldArgExtension(fieldName, argName, extensionName) {
        const extensions = this.getFieldArgExtensions(fieldName, argName);
        return extensionName in extensions;
    }
    setFieldArgExtension(fieldName, argName, extensionName, value) {
        this.extendFieldArgExtensions(fieldName, argName, {
            [extensionName]: value,
        });
        return this;
    }
    removeFieldArgExtension(fieldName, argName, extensionName) {
        const extensions = Object.assign({}, this.getFieldArgExtensions(fieldName, argName));
        delete extensions[extensionName];
        this.setFieldArgExtensions(fieldName, argName, 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;
    }
    removeDirective(directiveName) {
        const directives = this.getDirectives().filter((d) => d.name !== directiveName);
        this.setDirectives(directives);
        return this;
    }
    getFieldDirectives(fieldName) {
        return this.getField(fieldName).directives || [];
    }
    setFieldDirectives(fieldName, directives) {
        const fc = this.getField(fieldName);
        fc.directives = directives;
        this._gqcIsModified = true;
        return this;
    }
    getFieldDirectiveNames(fieldName) {
        return this.getFieldDirectives(fieldName).map((d) => d.name);
    }
    getFieldDirectiveByName(fieldName, directiveName) {
        const directive = this.getFieldDirectives(fieldName).find((d) => d.name === directiveName);
        if (!directive)
            return undefined;
        return directive.args;
    }
    setFieldDirectiveByName(fieldName, directiveName, args) {
        const directives = this.getFieldDirectives(fieldName);
        const idx = directives.findIndex((d) => d.name === directiveName);
        if (idx >= 0) {
            directives[idx].args = args;
        }
        else {
            directives.push({ name: directiveName, args });
        }
        this.setFieldDirectives(fieldName, directives);
        return this;
    }
    getFieldDirectiveById(fieldName, idx) {
        const directive = this.getFieldDirectives(fieldName)[idx];
        if (!directive)
            return undefined;
        return directive.args;
    }
    removeFieldDirective(fieldName, directiveName) {
        const fc = this.getField(fieldName);
        const directives = (fc.directives || []).filter((d) => d.name !== directiveName);
        if (directiveName === 'deprecated') {
            fc.deprecationReason = undefined;
        }
        this.setFieldDirectives(fieldName, directives);
        return this;
    }
    getFieldArgDirectives(fieldName, argName) {
        return this.getFieldArg(fieldName, argName).directives || [];
    }
    setFieldArgDirectives(fieldName, argName, directives) {
        const ac = this.getFieldArg(fieldName, argName);
        ac.directives = directives;
        this._gqcIsModified = true;
        return this;
    }
    getFieldArgDirectiveNames(fieldName, argName) {
        return this.getFieldArgDirectives(fieldName, argName).map((d) => d.name);
    }
    getFieldArgDirectiveByName(fieldName, argName, directiveName) {
        const directive = this.getFieldArgDirectives(fieldName, argName).find((d) => d.name === directiveName);
        if (!directive)
            return undefined;
        return directive.args;
    }
    setFieldArgDirectiveByName(fieldName, argName, directiveName, args) {
        const directives = this.getFieldArgDirectives(fieldName, argName);
        const idx = directives.findIndex((d) => d.name === directiveName);
        if (idx >= 0) {
            directives[idx].args = args;
        }
        else {
            directives.push({ name: directiveName, args });
        }
        this.setFieldArgDirectives(fieldName, argName, directives);
        return this;
    }
    getFieldArgDirectiveById(fieldName, argName, idx) {
        const directive = this.getFieldArgDirectives(fieldName, argName)[idx];
        if (!directive)
            return undefined;
        return directive.args;
    }
    removeFieldArgDirective(fieldName, argName, directiveName) {
        const directives = this.getFieldArgDirectives(fieldName, argName).filter((d) => d.name !== directiveName);
        if (directiveName === 'deprecated') {
            this.getFieldArg(fieldName, argName).deprecationReason = undefined;
        }
        this.setFieldArgDirectives(fieldName, argName, directives);
        return this;
    }
    addRelation(fieldName, opts) {
        if (!this._gqcRelations) {
            this._gqcRelations = {};
        }
        this._gqcRelations[fieldName] = opts;
        this._gqcIsModified = true;
        if (opts.hasOwnProperty('resolver')) {
            if ((0, is_1.isFunction)(opts.resolver)) {
                this._gqcFields[fieldName] = (0, createThunkedObjectProxy_1.createThunkedObjectProxy)(() => this._relationWithResolverToFC(opts, fieldName));
            }
            else {
                this._gqcFields[fieldName] = this._relationWithResolverToFC(opts, fieldName);
            }
        }
        else if (opts.hasOwnProperty('type')) {
            const fc = opts;
            this.setField(fieldName, fc);
        }
        return this;
    }
    getRelations() {
        if (!this._gqcRelations) {
            this._gqcRelations = {};
        }
        return this._gqcRelations;
    }
    _relationWithResolverToFC(opts, fieldName = '') {
        const resolver = (0, is_1.isFunction)(opts.resolver) ? opts.resolver(this.schemaComposer) : opts.resolver;
        if (!(resolver instanceof Resolver_1.Resolver)) {
            throw new Error('You should provide correct Resolver object for relation ' +
                `${this.getTypeName()}.${fieldName}`);
        }
        if (opts.type) {
            throw new Error('You can not use `resolver` and `type` properties simultaneously for relation ' +
                `${this.getTypeName()}.${fieldName}`);
        }
        if (opts.resolve) {
            throw new Error('You can not use `resolver` and `resolve` properties simultaneously for relation ' +
                `${this.getTypeName()}.${fieldName}`);
        }
        const argsConfig = Object.assign({}, resolver.args);
        const argsProto = {};
        const argsRuntime = [];
        const optsArgs = opts.prepareArgs || {};
        Object.keys(optsArgs).forEach((argName) => {
            const argMapVal = optsArgs[argName];
            if (argMapVal !== undefined) {
                delete argsConfig[argName];
                if ((0, is_1.isFunction)(argMapVal)) {
                    argsRuntime.push([argName, argMapVal]);
                }
                else if (argMapVal !== null) {
                    argsProto[argName] = argMapVal;
                }
            }
        });
        const { catchErrors = true } = opts;
        const fieldConfig = resolver.getFieldConfig();
        const resolve = (source, args, context, info) => {
            const newArgs = Object.assign(Object.assign({}, args), argsProto);
            argsRuntime.forEach(([argName, argFn]) => {
                newArgs[argName] = argFn(source, args, context, info);
            });
            const payload = fieldConfig.resolve
                ? fieldConfig.resolve(source, newArgs, context, info)
                : null;
            return catchErrors
                ? Promise.resolve(payload).catch((e) => {
                    console.log(`GQC ERROR: relation for ${this.getTypeName()}.${fieldName} throws error:`);
                    console.log(e);
                    return null;
                })
                : payload;
        };
        return {
            type: resolver.type,
            description: opts.description || resolver.description,
            deprecationReason: opts.deprecationReason,
            args: argsConfig,
            resolve,
            projection: opts.projection,
            extensions: Object.assign(Object.assign({}, resolver.extensions), opts.extensions),
        };
    }
    setRecordIdFn(fn) {
        this._gqcGetRecordIdFn = fn;
        return this;
    }
    hasRecordIdFn() {
        return !!this._gqcGetRecordIdFn;
    }
    getRecordIdFn() {
        if (!this._gqcGetRecordIdFn) {
            throw new Error(`Type ${this.getTypeName()} does not have RecordIdFn`);
        }
        return this._gqcGetRecordIdFn;
    }
    getRecordId(source, args, context) {
        return this.getRecordIdFn()(source, args, context);
    }
    get(path) {
        return (0, typeByPath_1.typeByPath)(this, path);
    }
    getNestedTCs(opts = {}, passedTypes = new Set()) {
        const exclude = Array.isArray(opts.exclude) ? opts.exclude : [];
        this.getFieldNames().forEach((fieldName) => {
            const tc = this.getFieldTC(fieldName);
            if (!passedTypes.has(tc) && !exclude.includes(tc.getTypeName())) {
                passedTypes.add(tc);
                if (tc instanceof ObjectTypeComposer || tc instanceof UnionTypeComposer_1.UnionTypeComposer) {
                    tc.getNestedTCs(opts, passedTypes);
                }
            }
            this.getFieldArgNames(fieldName).forEach((argName) => {
                const itc = this.getFieldArgTC(fieldName, argName);
                if (!passedTypes.has(itc) && !exclude.includes(itc.getTypeName())) {
                    passedTypes.add(itc);
                    if (itc instanceof InputTypeComposer_1.InputTypeComposer) {
                        itc.getNestedTCs(opts, passedTypes);
                    }
                }
            });
        });
        this.getInterfaces().forEach((t) => {
            const iftc = t instanceof ThunkComposer_1.ThunkComposer ? t.ofType : t;
            if (!passedTypes.has(iftc) && !exclude.includes(iftc.getTypeName())) {
                passedTypes.add(iftc);
                iftc.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.printObject)(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.printObject)(this.getType(), innerOpts);
    }
}
exports.ObjectTypeComposer = ObjectTypeComposer;
//# sourceMappingURL=ObjectTypeComposer.js.map