function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i] != null ? arguments[i] : {}; var ownKeys = Object.keys(source); if (typeof Object.getOwnPropertySymbols === 'function') { ownKeys = ownKeys.concat(Object.getOwnPropertySymbols(source).filter(function (sym) { return Object.getOwnPropertyDescriptor(source, sym).enumerable; })); } ownKeys.forEach(function (key) { _defineProperty(target, key, source[key]); }); } return target; } function _defineProperty(obj, key, value) { if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } /* eslint-disable no-use-before-define */ import { GraphQLInputObjectType, GraphQLNonNull, GraphQLList, getNamedType, isInputType } from './graphql'; import { resolveMaybeThunk, upperFirst, inspect } from './utils/misc'; import { isObject, isFunction, isString } from './utils/is'; import { resolveInputConfigMapAsThunk, resolveInputConfigAsThunk } from './utils/configAsThunk'; import { typeByPath } from './utils/typeByPath'; import { SchemaComposer } from './SchemaComposer'; import { ScalarTypeComposer } from './ScalarTypeComposer'; import { EnumTypeComposer } from './EnumTypeComposer'; import { graphqlVersion } from './utils/graphqlVersion'; import { defineInputFieldMap, defineInputFieldMapToConfig } from './utils/configToDefine'; export function isComposeInputType(type) { return isInputType(type) || Array.isArray(type) && isComposeInputType(type[0]) || type instanceof InputTypeComposer || type instanceof EnumTypeComposer || type instanceof ScalarTypeComposer; } export class InputTypeComposer { static create(typeDef, schemaComposer) { if (!(schemaComposer instanceof SchemaComposer)) { throw new Error('You must provide SchemaComposer instance as a second argument for `InputTypeComposer.create(typeDef, schemaComposer)`'); } const itc = this.createTemp(typeDef, schemaComposer); schemaComposer.add(itc); return itc; } static createTemp(typeDef, schemaComposer) { const sc = schemaComposer || new SchemaComposer(); let ITC; if (isString(typeDef)) { const typeName = typeDef; const NAME_RX = /^[_a-zA-Z][_a-zA-Z0-9]*$/; if (NAME_RX.test(typeName)) { ITC = new InputTypeComposer(new GraphQLInputObjectType({ name: typeName, fields: () => ({}) }), sc); } else { ITC = sc.typeMapper.createType(typeName); if (!(ITC instanceof InputTypeComposer)) { throw new Error('You should provide correct GraphQLInputObjectType type definition.' + 'Eg. `input MyInputType { name: String! }`'); } } } else if (typeDef instanceof GraphQLInputObjectType) { ITC = new InputTypeComposer(typeDef, sc); } else if (isObject(typeDef)) { const fields = typeDef.fields; const type = new GraphQLInputObjectType({ name: typeDef.name, description: typeDef.description, fields: isFunction(fields) ? () => resolveInputConfigMapAsThunk(sc, fields(), typeDef.name) : () => ({}) }); ITC = new InputTypeComposer(type, sc); if (isObject(typeDef.fields)) ITC.addFields(typeDef.fields); ITC.gqType._gqcExtensions = typeDef.extensions || {}; } else { throw new Error(`You should provide InputObjectConfig or string with type name to InputTypeComposer.create(typeDef). Provided:\n${inspect(typeDef)}`); } return ITC; } constructor(gqType, schemaComposer) { if (!(schemaComposer instanceof SchemaComposer)) { throw new Error('You must provide SchemaComposer instance as a second argument for `new InputTypeComposer(GraphQLInputType, SchemaComposer)`'); } this.schemaComposer = schemaComposer; if (!(gqType instanceof GraphQLInputObjectType)) { throw new Error('InputTypeComposer accept only GraphQLInputObjectType in constructor'); } this.gqType = gqType; // alive proper Flow type casting in autosuggestions for class with Generics /* :: return this; */ } // ----------------------------------------------- // Field methods // ----------------------------------------------- getFields() { if (!this.gqType._gqcFields) { if (graphqlVersion >= 14) { this.gqType._gqcFields = defineInputFieldMapToConfig(this.gqType._fields); } else { const fields = this.gqType._typeConfig.fields; this.gqType._gqcFields = resolveMaybeThunk(fields) || {}; } } return this.gqType._gqcFields; } getFieldNames() { return Object.keys(this.getFields()); } hasField(fieldName) { const fields = this.getFields(); return !!fields[fieldName]; } /** * Completely replace all fields in GraphQL type * WARNING: this method rewrite an internal GraphQL instance variable. */ setFields(fields) { this.gqType._gqcFields = fields; if (graphqlVersion >= 14) { this.gqType._fields = () => { return defineInputFieldMap(this.gqType, resolveInputConfigMapAsThunk(this.schemaComposer, fields, this.getTypeName())); }; } else { this.gqType._typeConfig.fields = () => { return resolveInputConfigMapAsThunk(this.schemaComposer, fields, this.getTypeName()); }; delete this.gqType._fields; // if schema was builded, delete defineFieldMap } return this; } setField(fieldName, fieldConfig) { this.addFields({ [fieldName]: fieldConfig }); return this; } /** * Add new fields or replace existed in a GraphQL type */ addFields(newFields) { this.setFields(_objectSpread({}, this.getFields(), newFields)); return this; } /** * Add new fields or replace existed (where field name may have dots) */ addNestedFields(newFields) { Object.keys(newFields).forEach(fieldName => { const fc = newFields[fieldName]; const names = fieldName.split('.'); const name = names.shift(); if (names.length === 0) { // single field this.setField(name, fc); } else { // nested field let childTC; if (!this.hasField(name)) { childTC = InputTypeComposer.createTemp(`${this.getTypeName()}${upperFirst(name)}`, this.schemaComposer); this.setField(name, childTC); } else { childTC = this.getFieldTC(name); } if (childTC instanceof InputTypeComposer) { childTC.addNestedFields({ [names.join('.')]: fc }); } } }); return this; } getField(fieldName) { const fields = this.getFields(); let field = fields[fieldName]; if (!field) { throw new Error(`Cannot get field '${fieldName}' from input type '${this.getTypeName()}'. Field does not exist.`); } if (isFunction(field)) field = field(); if (typeof field === 'string' || isComposeInputType(field) || Array.isArray(field)) { return { type: field }; } return field; } removeField(fieldNameOrArray) { const fieldNames = Array.isArray(fieldNameOrArray) ? fieldNameOrArray : [fieldNameOrArray]; const fields = this.getFields(); fieldNames.forEach(fieldName => delete fields[fieldName]); this.setFields(fields); return this; } removeOtherFields(fieldNameOrArray) { const keepFieldNames = Array.isArray(fieldNameOrArray) ? fieldNameOrArray : [fieldNameOrArray]; const fields = this.getFields(); Object.keys(fields).forEach(fieldName => { if (keepFieldNames.indexOf(fieldName) === -1) { delete fields[fieldName]; } }); this.setFields(fields); return this; } extendField(fieldName, partialFieldConfig) { let prevFieldConfig; try { prevFieldConfig = this.getField(fieldName); } catch (e) { throw new Error(`Cannot extend field '${fieldName}' from input type '${this.getTypeName()}'. Field does not exist.`); } this.setField(fieldName, _objectSpread({}, prevFieldConfig, partialFieldConfig, { extensions: _objectSpread({}, prevFieldConfig.extensions || {}, partialFieldConfig.extensions || {}) })); return this; } reorderFields(names) { const orderedFields = {}; const fields = this.getFields(); names.forEach(name => { if (fields[name]) { orderedFields[name] = fields[name]; delete fields[name]; } }); this.setFields(_objectSpread({}, orderedFields, fields)); return this; } isFieldNonNull(fieldName) { return this.getFieldType(fieldName) instanceof GraphQLNonNull; } // alias for isFieldNonNull isRequired(fieldName) { return this.isFieldNonNull(fieldName); } getFieldConfig(fieldName) { const fc = this.getField(fieldName); if (!fc) { throw new Error(`Type ${this.getTypeName()} does not have field with name '${fieldName}'`); } return resolveInputConfigAsThunk(this.schemaComposer, fc, fieldName, this.getTypeName()); } getFieldType(fieldName) { return this.getFieldConfig(fieldName).type; } getFieldTC(fieldName) { const fieldType = getNamedType(this.getFieldType(fieldName)); const tc = this.schemaComposer.createTC(fieldType); if (tc instanceof InputTypeComposer || tc instanceof EnumTypeComposer || tc instanceof ScalarTypeComposer) { return tc; } else { throw new Error(`Type ${this.getTypeName()} has invalid field ${fieldName} which is not of an input type.`); } } /** * Alias for `getFieldTC()` but returns statically checked InputTypeComposer. * If field have other type then error will be thrown. */ getFieldITC(fieldName) { const tc = this.getFieldTC(fieldName); if (!(tc instanceof InputTypeComposer)) { throw new Error(`${this.getTypeName()}.getFieldITC('${fieldName}') must be InputTypeComposer, but recieved ${tc.constructor.name}. Maybe you need to use 'getFieldTC()' method which returns any type composer?`); } return tc; } makeFieldNonNull(fieldNameOrArray) { const fieldNames = Array.isArray(fieldNameOrArray) ? fieldNameOrArray : [fieldNameOrArray]; fieldNames.forEach(fieldName => { if (this.hasField(fieldName)) { const fieldType = this.getFieldType(fieldName); if (!(fieldType instanceof GraphQLNonNull)) { this.extendField(fieldName, { type: new GraphQLNonNull(fieldType) }); } } }); return this; } // alias for makeFieldNonNull makeRequired(fieldNameOrArray) { return this.makeFieldNonNull(fieldNameOrArray); } makeFieldNullable(fieldNameOrArray) { const fieldNames = Array.isArray(fieldNameOrArray) ? fieldNameOrArray : [fieldNameOrArray]; fieldNames.forEach(fieldName => { if (this.hasField(fieldName)) { const fieldType = this.getFieldType(fieldName); if (fieldType instanceof GraphQLNonNull) { this.extendField(fieldName, { type: fieldType.ofType }); } } }); return this; } makeOptional(fieldNameOrArray) { return this.makeFieldNullable(fieldNameOrArray); } // ----------------------------------------------- // Type methods // ----------------------------------------------- getType() { return this.gqType; } getTypePlural() { return new GraphQLList(this.gqType); } getTypeNonNull() { return new GraphQLNonNull(this.gqType); } getTypeName() { return this.gqType.name; } setTypeName(name) { this.gqType.name = name; this.schemaComposer.add(this); return this; } getDescription() { return this.gqType.description || ''; } setDescription(description) { this.gqType.description = description; return this; } clone(newTypeName) { if (!newTypeName) { throw new Error('You should provide new type name for clone() method'); } const newFields = {}; this.getFieldNames().forEach(fieldName => { const fc = this.getFieldConfig(fieldName); newFields[fieldName] = _objectSpread({}, fc); }); return new InputTypeComposer(new GraphQLInputObjectType({ name: newTypeName, fields: newFields }), this.schemaComposer); } merge(type) { if (type instanceof GraphQLInputObjectType) { this.addFields(defineInputFieldMapToConfig(type.getFields())); } else if (type instanceof InputTypeComposer) { this.addFields(type.getFields()); } else { throw new Error(`Cannot merge ${inspect(type)} with InputObjectType(${this.getTypeName()}). Provided type should be GraphQLInputObjectType or InputTypeComposer.`); } return this; } // ----------------------------------------------- // Extensions methods // ----------------------------------------------- getExtensions() { if (!this.gqType._gqcExtensions) { return {}; } else { return this.gqType._gqcExtensions; } } setExtensions(extensions) { this.gqType._gqcExtensions = extensions; return this; } extendExtensions(extensions) { const current = this.getExtensions(); this.setExtensions(_objectSpread({}, 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 = _objectSpread({}, 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, _objectSpread({}, field, { extensions })); return this; } extendFieldExtensions(fieldName, extensions) { const current = this.getFieldExtensions(fieldName); this.setFieldExtensions(fieldName, _objectSpread({}, 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 = _objectSpread({}, this.getFieldExtensions(fieldName)); delete extensions[extensionName]; this.setFieldExtensions(fieldName, extensions); return this; } // ----------------------------------------------- // Directive methods // ----------------------------------------------- getDirectives() { const directives = this.getExtension('directives'); if (Array.isArray(directives)) { return directives; } return []; } 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; } getDirectiveById(idx) { const directive = this.getDirectives()[idx]; if (!directive) return undefined; return directive.args; } getFieldDirectives(fieldName) { const directives = this.getFieldExtension(fieldName, 'directives'); if (Array.isArray(directives)) { return directives; } return []; } 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; } getFieldDirectiveById(fieldName, idx) { const directive = this.getFieldDirectives(fieldName)[idx]; if (!directive) return undefined; return directive.args; } // ----------------------------------------------- // Misc methods // ----------------------------------------------- get(path) { return typeByPath(this, path); } }