UNPKG

graphql-compose

Version:

GraphQL schema builder from different data sources with middleware extensions.

639 lines (555 loc) 20.6 kB
'use strict'; var _promise = require('babel-runtime/core-js/promise'); var _promise2 = _interopRequireDefault(_promise); var _keys = require('babel-runtime/core-js/object/keys'); var _keys2 = _interopRequireDefault(_keys); var _defineProperty2 = require('babel-runtime/core-js/object/define-property'); var _defineProperty3 = _interopRequireDefault(_defineProperty2); var _assign = require('babel-runtime/core-js/object/assign'); var _assign2 = _interopRequireDefault(_assign); Object.defineProperty(exports, "__esModule", { value: true }); var _extends = _assign2.default || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; (0, _defineProperty3.default)(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); /* eslint-disable no-use-before-define, no-restricted-syntax */ var _objectPath = require('object-path'); var _objectPath2 = _interopRequireDefault(_objectPath); var _util = require('util'); var _util2 = _interopRequireDefault(_util); var _graphql = require('graphql'); var _debug = require('./utils/debug'); var _typeMapper = require('./typeMapper'); var _typeMapper2 = _interopRequireDefault(_typeMapper); var _typeComposer = require('./typeComposer'); var _typeComposer2 = _interopRequireDefault(_typeComposer); var _deepmerge = require('./utils/deepmerge'); var _deepmerge2 = _interopRequireDefault(_deepmerge); var _configAsThunk = require('./utils/configAsThunk'); var _misc = require('./utils/misc'); var _is = require('./utils/is'); var _projection = require('./projection'); var _inputTypeComposer = require('./inputTypeComposer'); var _inputTypeComposer2 = _interopRequireDefault(_inputTypeComposer); var _typeByPath = require('./typeByPath'); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _defineProperty(obj, key, value) { if (key in obj) { (0, _defineProperty3.default)(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; } function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } var Resolver = function () { function Resolver() { var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; _classCallCheck(this, Resolver); if (!opts.name) { throw new Error('For Resolver constructor the `opts.name` is required option.'); } this.name = opts.name; this.parent = opts.parent || null; this.kind = opts.kind || null; this.description = opts.description || ''; /** * @deprecated 2.0.0 */ if (opts.outputType) { (0, _debug.deprecate)('Use opts.type instead.'); // $FlowFixMe this.setType(opts.outputType); } if (opts.type) { this.setType(opts.type); } if (opts.args) { this.args = _typeMapper2.default.convertArgConfigMap(opts.args, this.name, 'Resolver'); } else { this.args = {}; } if (opts.resolve) { this.resolve = opts.resolve; } } _createClass(Resolver, [{ key: 'hasArg', value: function hasArg(argName) { return !!this.args[argName]; } }, { key: 'getArg', value: function getArg(argName) { if (this.hasArg(argName)) { return this.args[argName]; } return undefined; } }, { key: 'getArgType', value: function getArgType(argName) { var arg = this.getArg(argName); if (arg) { return arg.type; } return undefined; } }, { key: 'getArgs', value: function getArgs() { return this.args; } }, { key: 'getArgNames', value: function getArgNames() { return (0, _keys2.default)(this.args); } }, { key: 'setArgs', value: function setArgs(args) { this.args = _typeMapper2.default.convertArgConfigMap(args, this.name, 'Resolver'); return this; } }, { key: 'setArg', value: function setArg(argName, argConfig) { this.args[argName] = _typeMapper2.default.convertArgConfig(argConfig, argName, this.name, 'Resolver'); return this; } }, { key: 'addArgs', value: function addArgs(newArgs) { this.setArgs((0, _assign2.default)({}, this.getArgs(), newArgs)); return this; } }, { key: 'removeArg', value: function removeArg(argNameOrArray) { var _this = this; var argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray]; argNames.forEach(function (argName) { delete _this.args[argName]; }); return this; } }, { key: 'removeOtherArgs', value: function removeOtherArgs(argNameOrArray) { var _this2 = this; var keepArgNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray]; (0, _keys2.default)(this.args).forEach(function (argName) { if (!keepArgNames.includes(argName)) { delete _this2.args[argName]; } }); return this; } }, { key: 'reorderArgs', value: function reorderArgs(names) { var _this3 = this; var orderedArgs = {}; names.forEach(function (name) { if (_this3.args[name]) { orderedArgs[name] = _this3.args[name]; delete _this3.args[name]; } }); this.args = _extends({}, orderedArgs, this.args); return this; } }, { key: 'cloneArg', value: function cloneArg(argName, newTypeName) { if (!{}.hasOwnProperty.call(this.args, argName)) { throw new Error('Can not clone arg ' + argName + ' for resolver ' + this.name + '. Argument does not exist.'); } if (!(this.args[argName].type instanceof _graphql.GraphQLInputObjectType)) { throw new Error('Can not clone arg ' + argName + ' for resolver ' + this.name + '.' + 'Argument should be GraphQLInputObjectType (complex input type).'); } if (!newTypeName || newTypeName !== (0, _misc.clearName)(newTypeName)) { throw new Error('You should provide new type name as second argument'); } if (newTypeName === this.args[argName].type.name) { throw new Error('You should provide new type name. It is equal to current name.'); } this.args[argName] = _extends({}, this.args[argName], { type: _inputTypeComposer2.default.create(this.args[argName].type).clone(newTypeName).getType() }); return this; } }, { key: 'isRequired', value: function isRequired(argName) { return this.getArgType(argName) instanceof _graphql.GraphQLNonNull; } }, { key: 'makeRequired', value: function makeRequired(argNameOrArray) { var argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray]; var args = this.getArgs(); argNames.forEach(function (argName) { if (args[argName]) { if (!(args[argName].type instanceof _graphql.GraphQLNonNull)) { args[argName].type = new _graphql.GraphQLNonNull(args[argName].type); } } }); this.setArgs(args); return this; } }, { key: 'makeOptional', value: function makeOptional(argNameOrArray) { var argNames = Array.isArray(argNameOrArray) ? argNameOrArray : [argNameOrArray]; var args = this.getArgs(); argNames.forEach(function (argName) { if (argNames.includes(argName)) { if (args[argName].type instanceof _graphql.GraphQLNonNull) { args[argName].type = args[argName].type.ofType; } } }); this.setArgs(args); return this; } /* * This method should be overriden via constructor */ // eslint-disable-next-line }, { key: 'resolve', value: function resolve(resolveParams) { return _promise2.default.resolve(); } }, { key: 'getResolve', value: function getResolve() { return this.resolve; } }, { key: 'setResolve', value: function setResolve(resolve) { this.resolve = resolve; return this; } /** * @deprecated 2.0.0 */ }, { key: 'getOutputType', value: function getOutputType() { (0, _debug.deprecate)('Use Resolver.getType() instead.'); return this.getType(); } /** * @deprecated 2.0.0 */ }, { key: 'setOutputType', value: function setOutputType(gqType) { (0, _debug.deprecate)('Use Resolver.setType() instead.'); this.setType(gqType); } /** * @deprecated 2.0.0 */ }, { key: 'wrapOutputType', value: function wrapOutputType(cb) { var wrapperName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'wrapType'; (0, _debug.deprecate)('Use Resolver.wrapType() instead.'); return this.wrapType(cb, wrapperName); } /** * @deprecated 2.0.0 */ }, { key: 'getType', value: function getType() { return this.type; } }, { key: 'getTypeComposer', value: function getTypeComposer() { if (this.type instanceof _graphql.GraphQLObjectType) { return new _typeComposer2.default(this.type); } return null; } }, { key: 'setType', value: function setType(gqType) { var fc = _typeMapper2.default.convertOutputFieldConfig(gqType, 'setType', 'Resolver'); // $FlowFixMe if (!fc || !(0, _graphql.isOutputType)(fc.type)) { throw new Error('You should provide correct OutputType for Resolver.type.'); } // $FlowFixMe this.type = fc.type; return this; } }, { key: 'getFieldConfig', value: function getFieldConfig() { var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var _resolve = this.getResolve(); return { type: this.getType(), args: (0, _configAsThunk.resolveInputConfigsAsThunk)(this.getArgs()), description: this.description, resolve: function resolve(source, args, context, info) { var projection = (0, _projection.getProjectionFromAST)(info); if (opts.projection) { // $FlowFixMe projection = (0, _deepmerge2.default)(projection, opts.projection); } return _resolve({ source: source, args: args, context: context, info: info, projection: projection }); } }; } }, { key: 'getKind', value: function getKind() { return this.kind; } }, { key: 'setKind', value: function setKind(kind) { if (kind !== 'query' && kind !== 'mutation' && kind !== 'subscription') { throw new Error('You provide incorrect value \'' + kind + '\' for Resolver.setKind method. ' + 'Valid values are: query | mutation | subscription'); } this.kind = kind; return this; } }, { key: 'getDescription', value: function getDescription() { return this.description; } }, { key: 'setDescription', value: function setDescription(description) { this.description = description; return this; } }, { key: 'get', value: function get(path) { // $FlowFixMe return (0, _typeByPath.typeByPath)(this, path); } }, { key: 'clone', value: function clone() { var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; var oldOpts = {}; for (var key in this) { if ({}.hasOwnProperty.call(this, key)) { // $FlowFixMe oldOpts[key] = this[key]; } } oldOpts.args = (0, _assign2.default)({}, this.args); return new Resolver((0, _assign2.default)({}, oldOpts, opts)); } }, { key: 'wrap', value: function wrap(cb) { var newResolverOpts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; var prevResolver = this; var newResolver = this.clone(_extends({ name: 'wrap', parent: prevResolver }, newResolverOpts)); if ((0, _is.isFunction)(cb)) { // $FlowFixMe cb(newResolver, prevResolver); } return newResolver; } }, { key: 'wrapResolve', value: function wrapResolve(cb) { var wrapperName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'wrapResolve'; return this.wrap(function (newResolver, prevResolver) { var newResolve = cb(prevResolver.getResolve()); newResolver.setResolve(newResolve); return newResolver; }, { name: wrapperName }); } }, { key: 'wrapArgs', value: function wrapArgs(cb) { var wrapperName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'wrapArgs'; return this.wrap(function (newResolver, prevResolver) { // clone prevArgs, to avoid changing args in callback var prevArgs = (0, _assign2.default)({}, prevResolver.getArgs()); var newArgs = cb(prevArgs); newResolver.setArgs(newArgs); return newResolver; }, { name: wrapperName }); } }, { key: 'wrapCloneArg', value: function wrapCloneArg(argName, newTypeName) { return this.wrap(function (newResolver) { newResolver.cloneArg(argName, newTypeName); return newResolver; }, { name: 'cloneFilterArg' }); } }, { key: 'wrapType', value: function wrapType(cb) { var wrapperName = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'wrapType'; return this.wrap(function (newResolver, prevResolver) { var prevType = prevResolver.getType(); var newType = cb(prevType); newResolver.setType(newType); return newResolver; }, { name: wrapperName }); } }, { key: 'addFilterArg', value: function addFilterArg(opts) { if (!opts.name) { throw new Error('For Resolver.addFilterArg the arg name `opts.name` is required.'); } if (!opts.type) { throw new Error('For Resolver.addFilterArg the arg type `opts.type` is required.'); } var resolver = this.wrap(null, { name: 'addFilterArg' }); // get filterTC or create new one argument var filter = resolver.getArg('filter'); var filterITC = void 0; if (filter && filter.type instanceof _graphql.GraphQLInputObjectType) { filterITC = new _inputTypeComposer2.default(filter.type); } else { if (!opts.filterTypeNameFallback || !(0, _is.isString)(opts.filterTypeNameFallback)) { throw new Error('For Resolver.addFilterArg needs to provide `opts.filterTypeNameFallback: string`. ' + 'This string will be used as unique name for `filter` type of input argument. ' + 'Eg. FilterXXXXXInput'); } filterITC = _inputTypeComposer2.default.create(opts.filterTypeNameFallback); } var defaultValue = void 0; if (filter && filter.defaultValue) { defaultValue = filter.defaultValue; } if (opts.defaultValue) { if (!defaultValue) { defaultValue = {}; } // $FlowFixMe defaultValue[opts.name] = opts.defaultValue; } resolver.setArg('filter', { type: filterITC.getType(), description: filter && filter.description || undefined, defaultValue: defaultValue }); filterITC.setField(opts.name, _extends({}, (0, _misc.only)(opts, ['name', 'type', 'defaultValue', 'description']))); var resolveNext = resolver.getResolve(); if ((0, _is.isFunction)(opts.query)) { resolver.setResolve(function (resolveParams) { var value = _objectPath2.default.get(resolveParams, ['args', 'filter', opts.name]); if (value !== null && value !== undefined) { if (!resolveParams.rawQuery) { resolveParams.rawQuery = {}; // eslint-disable-line } opts.query(resolveParams.rawQuery, value, resolveParams); } return resolveNext(resolveParams); }); } return resolver; } }, { key: 'addSortArg', value: function addSortArg(opts) { if (!opts.name) { throw new Error('For Resolver.addSortArg the `opts.name` is required.'); } if (!opts.value) { throw new Error('For Resolver.addSortArg the `opts.value` is required.'); } var resolver = this.wrap(null, { name: 'addSortArg' }); // get sortEnumType or create new one var sort = resolver.getArg('sort'); var sortEnumType = void 0; if (sort) { if (sort.type instanceof _graphql.GraphQLEnumType) { sortEnumType = sort.type; } else { throw new Error('Resolver should have `sort` arg with type GraphQLEnumType. ' + ('But got: ' + _util2.default.inspect(sort.type, { depth: 2 }) + ' ')); } } else { if (!opts.sortTypeNameFallback || !(0, _is.isString)(opts.sortTypeNameFallback)) { throw new Error('For Resolver.addSortArg needs to provide `opts.sortTypeNameFallback: string`. ' + 'This string will be used as unique name for `sort` type of input argument. ' + 'Eg. SortXXXXXEnum'); } sortEnumType = new _graphql.GraphQLEnumType({ name: opts.sortTypeNameFallback, values: _defineProperty({}, opts.name, {}) }); resolver.setArg('sort', { type: sortEnumType }); } // extend sortEnumType with new sorting value var existedIdx = sortEnumType._values.findIndex(function (o) { return o.name === opts.name; }); if (existedIdx >= 0) { sortEnumType._values.splice(existedIdx, 1); } delete sortEnumType._nameLookup; delete sortEnumType._valueLookup; sortEnumType._values.push({ name: opts.name, description: opts.description, isDeprecated: Boolean(opts.deprecationReason), deprecationReason: opts.deprecationReason, value: (0, _is.isFunction)(opts.value) ? opts.name : opts.value }); // If sort value is evaluable (function), then wrap resolve method var resolveNext = resolver.getResolve(); if ((0, _is.isFunction)(opts.value)) { resolver.setResolve(function (resolveParams) { var value = _objectPath2.default.get(resolveParams, ['args', 'sort']); if (value === opts.name) { // $FlowFixMe var newSortValue = opts.value(resolveParams); resolveParams.args.sort = newSortValue; // eslint-disable-line } return resolveNext(resolveParams); }); } return resolver; } }, { key: 'getNestedName', value: function getNestedName() { if (this.parent) { return this.name + '(' + this.parent.getNestedName() + ')'; } return this.name; } }, { key: 'toString', value: function toString() { function extendedInfo(resolver) { var spaces = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : ''; return ['Resolver(', ' name: ' + resolver.name + ',', ' type: ' + _util2.default.inspect(resolver.type, { depth: 2 }) + ',', ' args: ' + _util2.default.inspect(resolver.args, { depth: 3 }).replace('\n', '\n ' + spaces) + ',', ' resolve: ' + (resolver.resolve ? resolver.resolve.toString().replace('\n', '\n ' + spaces) : 'undefined') + ',', ' parent: ' + (resolver.parent ? extendedInfo(resolver.parent, ' ' + spaces) : ''), ')'].filter(function (s) { return !!s; }).join('\n ' + spaces); } return extendedInfo(this); } }, { key: 'outputType', get: function get() { (0, _debug.deprecate)('Use Resolver.getType() instead.'); return this.type; } /** * @deprecated 2.0.0 */ , set: function set(gqType) { (0, _debug.deprecate)('Use Resolver.setType() instead.'); this.type = gqType; } }]); return Resolver; }(); exports.default = Resolver;