graphql-compose
Version:
GraphQL schema builder from different data sources with middleware extensions.
593 lines (536 loc) • 19.2 kB
JavaScript
var _extends = Object.assign || 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; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
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; }
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
/* eslint-disable no-use-before-define, no-restricted-syntax */
import objectPath from 'object-path';
import util from 'util';
import { GraphQLInputObjectType, GraphQLNonNull, GraphQLEnumType, GraphQLObjectType, isOutputType } from 'graphql';
import { deprecate } from './utils/debug';
import TypeMapper from './typeMapper';
import TypeComposer from './typeComposer';
import deepmerge from './utils/deepmerge';
import { resolveInputConfigsAsThunk } from './utils/configAsThunk';
import { only, clearName } from './utils/misc';
import { isFunction, isString } from './utils/is';
import { getProjectionFromAST } from './projection';
import InputTypeComposer from './inputTypeComposer';
import { typeByPath } from './typeByPath';
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) {
deprecate('Use opts.type instead.');
// $FlowFixMe
this.setType(opts.outputType);
}
if (opts.type) {
this.setType(opts.type);
}
if (opts.args) {
this.args = TypeMapper.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 Object.keys(this.args);
}
}, {
key: 'setArgs',
value: function setArgs(args) {
this.args = TypeMapper.convertArgConfigMap(args, this.name, 'Resolver');
return this;
}
}, {
key: 'setArg',
value: function setArg(argName, argConfig) {
this.args[argName] = TypeMapper.convertArgConfig(argConfig, argName, this.name, 'Resolver');
return this;
}
}, {
key: 'addArgs',
value: function addArgs(newArgs) {
this.setArgs(Object.assign({}, 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];
Object.keys(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 GraphQLInputObjectType)) {
throw new Error('Can not clone arg ' + argName + ' for resolver ' + this.name + '.' + 'Argument should be GraphQLInputObjectType (complex input type).');
}
if (!newTypeName || newTypeName !== 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: InputTypeComposer.create(this.args[argName].type).clone(newTypeName).getType()
});
return this;
}
}, {
key: 'isRequired',
value: function isRequired(argName) {
return this.getArgType(argName) instanceof 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 GraphQLNonNull)) {
args[argName].type = new 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 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 Promise.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() {
deprecate('Use Resolver.getType() instead.');
return this.getType();
}
/**
* @deprecated 2.0.0
*/
}, {
key: 'setOutputType',
value: function setOutputType(gqType) {
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';
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 GraphQLObjectType) {
return new TypeComposer(this.type);
}
return null;
}
}, {
key: 'setType',
value: function setType(gqType) {
var fc = TypeMapper.convertOutputFieldConfig(gqType, 'setType', 'Resolver');
// $FlowFixMe
if (!fc || !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: resolveInputConfigsAsThunk(this.getArgs()),
description: this.description,
resolve: function resolve(source, args, context, info) {
var projection = getProjectionFromAST(info);
if (opts.projection) {
// $FlowFixMe
projection = deepmerge(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 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 = Object.assign({}, this.args);
return new Resolver(Object.assign({}, 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 (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 = Object.assign({}, 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 GraphQLInputObjectType) {
filterITC = new InputTypeComposer(filter.type);
} else {
if (!opts.filterTypeNameFallback || !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 = InputTypeComposer.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({}, only(opts, ['name', 'type', 'defaultValue', 'description'])));
var resolveNext = resolver.getResolve();
if (isFunction(opts.query)) {
resolver.setResolve(function (resolveParams) {
var value = objectPath.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 GraphQLEnumType) {
sortEnumType = sort.type;
} else {
throw new Error('Resolver should have `sort` arg with type GraphQLEnumType. ' + ('But got: ' + util.inspect(sort.type, { depth: 2 }) + ' '));
}
} else {
if (!opts.sortTypeNameFallback || !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 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: isFunction(opts.value) ? opts.name : opts.value
});
// If sort value is evaluable (function), then wrap resolve method
var resolveNext = resolver.getResolve();
if (isFunction(opts.value)) {
resolver.setResolve(function (resolveParams) {
var value = objectPath.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: ' + util.inspect(resolver.type, { depth: 2 }) + ',', ' args: ' + util.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() {
deprecate('Use Resolver.getType() instead.');
return this.type;
}
/**
* @deprecated 2.0.0
*/
,
set: function set(gqType) {
deprecate('Use Resolver.setType() instead.');
this.type = gqType;
}
}]);
return Resolver;
}();
export default Resolver;