UNPKG

@prodigyems/graphql-sequelize

Version:

GraphQL & Relay for MySQL & Postgres via Sequelize

248 lines (190 loc) 10.4 kB
'use strict'; var _bluebird = require('bluebird'); var _bluebird2 = _interopRequireDefault(_bluebird); 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 _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }(); var _graphql = require('graphql'); var _lodash = require('lodash'); var _lodash2 = _interopRequireDefault(_lodash); var _argsToFindOptions = require('./argsToFindOptions'); var _argsToFindOptions2 = _interopRequireDefault(_argsToFindOptions); var _relay = require('./relay'); var _assert = require('assert'); var _assert2 = _interopRequireDefault(_assert); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; } function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } } function whereQueryVarsToValues(o, vals) { [].concat(_toConsumableArray(Object.getOwnPropertyNames(o)), _toConsumableArray(Object.getOwnPropertySymbols(o))).forEach(k => { if (_lodash2.default.isFunction(o[k])) { o[k] = o[k](vals); return; } if (_lodash2.default.isObject(o[k])) { whereQueryVarsToValues(o[k], vals); } }); } function checkIsModel(target) { return !!target.getTableName; } function checkIsAssociation(target) { return !!target.associationType; } function resolverFactory(targetMaybeThunk, models, requiredFilters) { let options = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : {}; (0, _assert2.default)(typeof targetMaybeThunk === 'function' || checkIsModel(targetMaybeThunk) || checkIsAssociation(targetMaybeThunk), 'resolverFactory should be called with a model, an association or a function (which resolves to a model or an association)'); const contextToOptions = _lodash2.default.assign({}, resolverFactory.contextToOptions, options.contextToOptions); (0, _assert2.default)(options.include === undefined, 'Include support has been removed in favor of dataloader batching'); if (options.before === undefined) options.before = options => options; if (options.after === undefined) options.after = result => result; if (options.handleConnection === undefined) options.handleConnection = true; return (() => { var _ref = (0, _bluebird.coroutine)(function* (source, args, context, info) { let target = typeof targetMaybeThunk === 'function' && !checkIsModel(targetMaybeThunk) ? yield _bluebird2.default.resolve(targetMaybeThunk(source, args, context, info)) : targetMaybeThunk, isModel = checkIsModel(target), isAssociation = checkIsAssociation(target), association = isAssociation && target, model = isAssociation && target.target || isModel && target, type = info.returnType, list = options.list || type instanceof _graphql.GraphQLList || type instanceof _graphql.GraphQLNonNull && type.ofType instanceof _graphql.GraphQLList; const attributes = Object.entries(model.getAttributes()).filter(function (_ref2) { var _ref3 = _slicedToArray(_ref2, 2); let attr = _ref3[1]; return !!attr.filterable; }).map(function (_ref4) { var _ref5 = _slicedToArray(_ref4, 1); let key = _ref5[0]; return key; }); const associations = Object.keys(targetMaybeThunk.associations); const filterableAttributesFields = {}; const filterableAttributes = [].concat(_toConsumableArray(attributes), _toConsumableArray(Object.entries(models).filter(function (_ref6) { var _ref7 = _slicedToArray(_ref6, 1); let key = _ref7[0]; return associations.includes(key) || key === targetMaybeThunk.name; }).map(function (_ref8) { var _ref9 = _slicedToArray(_ref8, 2); let model = _ref9[1]; return Object.entries(model.getAttributes()).filter(function (_ref10) { var _ref11 = _slicedToArray(_ref10, 2); let attr = _ref11[1]; return !!attr.filterable; }).map(function (_ref12) { var _ref13 = _slicedToArray(_ref12, 2); let key = _ref13[0], attr = _ref13[1]; filterableAttributesFields[key] = attr.field; return key; }); }).reduce(function (curr, next) { return [].concat(_toConsumableArray(curr), _toConsumableArray(next)); }))); let targetAttributes = model.options.defaultScope && model.options.defaultScope.attributes || Object.keys(model.getAttributes()), targetFields = Object.values(model.getAttributes()).filter(function (attr) { return targetAttributes.includes(attr.fieldName); }).map(function (attr) { return attr.field; }), findOptions = (0, _argsToFindOptions2.default)(args, filterableAttributes, filterableAttributesFields, [].concat(_toConsumableArray(associations), [targetMaybeThunk.name]), requiredFilters); info = _extends({}, info, { type: type, source: source, target: target }); context = context || {}; if ((0, _relay.isConnection)(type)) { type = (0, _relay.nodeType)(type); } type = type.ofType || type; findOptions.attributes = targetAttributes; findOptions.logging = findOptions.logging || context.logging; findOptions.graphqlContext = context; findOptions.include = associations; if (args.orderBy && Array.isArray(args.orderBy)) { findOptions.order = args.orderBy.map(function (order) { const firstOrder = order.splice(0, 1)[0].split('.'); return [].concat(_toConsumableArray(firstOrder), _toConsumableArray(order)).map(function (field) { if (!associations.includes(field) && !filterableAttributes.includes(field) && !['ASC', 'DESC'].includes(field)) { throw new Error(`Unknown order by: ${field}`); } return filterableAttributesFields[field] || field; }); }); } _lodash2.default.each(contextToOptions, function (as, key) { findOptions[as] = context[key]; }); return _bluebird2.default.resolve(options.before(findOptions, args, context, info)).then((() => { var _ref14 = (0, _bluebird.coroutine)(function* (findOptions) { if (args.where && !_lodash2.default.isEmpty(info.variableValues)) { whereQueryVarsToValues(args.where, info.variableValues); whereQueryVarsToValues(findOptions.where, info.variableValues); } if (list && !findOptions.order) { findOptions.order = [[model.primaryKeyAttribute, 'ASC']]; } if (association) { if (source[association.as] !== undefined) { // The user did a manual include const result = source[association.as]; if (options.handleConnection && (0, _relay.isConnection)(info.returnType)) { return (0, _relay.handleConnection)(result, args); } return result; } else { return source[association.accessors.get](findOptions).then(function (result) { if (options.handleConnection && (0, _relay.isConnection)(info.returnType)) { return (0, _relay.handleConnection)(result, args); } return result; }); } } if (options.operation === 'update') { const dataArr = Object.entries(args.data).map(function (_ref15) { var _ref16 = _slicedToArray(_ref15, 2); let key = _ref16[0], value = _ref16[1]; return `${key} = ${value}`; }); if (dataArr.length === 0) { throw new Error('No data provided to perform an update.'); } const updateData = dataArr.join(', '); const joinsArr = Object.entries(targetMaybeThunk.options.associations).map(function (_ref17) { var _ref18 = _slicedToArray(_ref17, 2); let key = _ref18[0], association = _ref18[1]; return `LEFT OUTER JOIN ${models[key].tableName} AS ${key} ON [${key}].[${association.foreignKey}] = [${targetMaybeThunk.name}].[${association.sourceKey}]`; }); const updateJoins = joinsArr.length === 0 ? '' : joinsArr.join(','); const whereObj = targetMaybeThunk.sequelize.getQueryInterface().queryGenerator.getWhereConditions(findOptions.where); const sql = `UPDATE ${targetMaybeThunk.tableName} SET ${updateData} FROM ${targetMaybeThunk.tableName} AS ${targetMaybeThunk.name} ${updateJoins} WHERE ${whereObj}`; yield targetMaybeThunk.sequelize.query(sql); } Object.assign(context, { count: function count() { return model.count({ where: findOptions.where, include: findOptions.include, distinct: true }); } }); findOptions.group = targetFields; return model[list ? 'findAll' : 'findOne'](findOptions); }); return function (_x6) { return _ref14.apply(this, arguments); }; })()).then(function (result) { return options.after(result, args, context, info); }); }); return function (_x2, _x3, _x4, _x5) { return _ref.apply(this, arguments); }; })(); } resolverFactory.contextToOptions = {}; module.exports = resolverFactory;