const _ = require('lodash') const defaults = require('./defaults') const { Op } = require('sequelize') const { diffBy, sanitize } = require('../util') const { sequelize } = require('../database') const crudController = (opts) => { opts = defaults(opts) const { Type } = opts const options = Object.assign({}, opts.options) const subset = async (req) => { const subset = {} const params = _.toPairs(req.params) .map(([key, value])=>({key, value})) .filter(({value}) => value !== opts.paramAll) for (let { key, value } of params) { const assoc = Type.associations[key] if (assoc) { subset[assoc.foreignKey] = (await assoc.target.findOne({ parameters: ['id'], where: { key: value } })).id } } return subset } const list = async (req, res) => { // TODO Pagination (http://docs.sequelizejs.com/manual/tutorial/querying.html#pagination-limiting) if (req.query && req.query.q) { const fields = [Type.tableAttributes.name, Type.tableAttributes.tag].filter(x => x) if (!fields) throw new Error('Table has no searchable fields') const or = fields.map(field => ({ [field.fieldName]: { [Op.like]: `%${req.query.q}%` } })) const where = { ...subset, [Op.or]: or } const data = await sanitize(req, await Type.findAll({ ...options, where })) res.status(200).send(data) } else if (res.query && res.query.ids) { const ids = res.query.ids.split(',') const data = await sanitize(req, await Type.findAll({ ...options, where: { ...(await subset(req)), id: { [Op.in]: ids }}})) res.status(200).send(data) } else { const data = await sanitize(req, await Type.findAll({ ...options, where: { ...(await subset(req)) }})) res.status(200).send(data) } } const setAssociations = async (record, data, transaction) => { for (let [key, assoc] of _.toPairs(Type.associations)) { if (record[key] && assoc.associationType === 'BelongsToMany' && assoc.target.attributes.key) { await assoc.set( data, await assoc.target.findAll({ attributes: ['id'], where: { key: { [Op.in]: record[key] } } }), {transaction} ) } } } const getAssociations = async (data, json) => { for (let [key, assoc] of _.toPairs(Type.associations)) { if (assoc.associationType === 'BelongsToMany' && assoc.target.attributes.key) { json[key] = (await assoc.get(data, { attributes: ['id', 'key'] })).map(x => x.key) } } } const create = async (req, res) => { const transaction = await sequelize.transaction() try { const record = { ...req.body, ...(await subset(req))} const data = (await Type.create(record, {transaction})) await setAssociations(record, data, transaction) await transaction.commit() res.status(200).send(await sanitize(req, data)) } catch (err) { await transaction.rollback() throw err } } const read = async (req, res) => { const data = (await Type.findOne({where: {id: req.params[opts.routeParam]}})) const json = await sanitize(req, data) await getAssociations(data, json) res.status(200).send(json) } const update = async (req, res) => { const transaction = await sequelize.transaction() try { const record = _.omit(req.body, _.keys(await subset(req))) const updated = (await Type.update(record, { where: { id: req.params[opts.routeParam] }, transaction })) const data = (await Type.findOne({where: { id: req.params[opts.routeParam] }})) const json = await sanitize(req, data) await setAssociations(record, data, transaction) await transaction.commit() await getAssociations(data, json) res.status(200).send(json) } catch (err) { await transaction.rollback() throw err } } const $delete = async (req, res) => { const data = (await Type.destroy({ where: { id: req.params[opts.routeParam] } })) res.status(204).end() } const trash = async (req, res) => { const data = (await Type.findAll({ model: Type, paranoid: false, where: { ...(await subset(req)), deletedAt: { [Op.ne]: null } } })) res.status(200).send(await sanitize(req, data)) } const undelete = async (req, res) => { const data = (await Type.update({ deletedAt: null }, { paranoid: false, where: { id: req.params[opts.routeParam] } })) res.status(200).send(await sanitize(req, data)) } // TODO: Create, Read, Update, Delete return { list, create, read, update, delete: $delete, trash, undelete } } module.exports = crudController