controller.js 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. const _ = require('lodash')
  2. const defaults = require('./defaults')
  3. const { Op } = require('sequelize')
  4. const { diffBy, sanitize } = require('../util')
  5. const { sequelize } = require('../database')
  6. const crudController = (opts) => {
  7. opts = defaults(opts)
  8. const { Type } = opts
  9. const options = Object.assign({}, opts.options)
  10. const subset = async (req) => {
  11. const subset = {}
  12. const params = _.toPairs(req.params)
  13. .map(([key, value])=>({key, value}))
  14. .filter(({value}) => value !== opts.paramAll)
  15. for (let { key, value } of params) {
  16. const assoc = Type.associations[key]
  17. if (assoc) {
  18. subset[assoc.foreignKey] = (await assoc.target.findOne({
  19. parameters: ['id'],
  20. where: { key: value }
  21. })).id
  22. }
  23. }
  24. return subset
  25. }
  26. const list = async (req, res) => {
  27. // TODO Pagination (http://docs.sequelizejs.com/manual/tutorial/querying.html#pagination-limiting)
  28. if (req.query && req.query.q) {
  29. const fields = [Type.tableAttributes.name, Type.tableAttributes.tag].filter(x => x)
  30. if (!fields) throw new Error('Table has no searchable fields')
  31. const or = fields.map(field => ({
  32. [field.fieldName]: { [Op.like]: `%${req.query.q}%` }
  33. }))
  34. const where = { ...subset, [Op.or]: or }
  35. const data = await sanitize(req, await Type.findAll({ ...options, where }))
  36. res.status(200).send(data)
  37. } else if (res.query && res.query.ids) {
  38. const ids = res.query.ids.split(',')
  39. const data = await sanitize(req, await Type.findAll({ ...options, where: { ...(await subset(req)), id: { [Op.in]: ids }}}))
  40. res.status(200).send(data)
  41. } else {
  42. const data = await sanitize(req, await Type.findAll({ ...options, where: { ...(await subset(req)) }}))
  43. res.status(200).send(data)
  44. }
  45. }
  46. const setAssociations = async (record, data, transaction) => {
  47. for (let [key, assoc] of _.toPairs(Type.associations)) {
  48. if (record[key] && assoc.associationType === 'BelongsToMany' && assoc.target.attributes.key) {
  49. await assoc.set(
  50. data,
  51. await assoc.target.findAll({
  52. attributes: ['id'],
  53. where: {
  54. key: {
  55. [Op.in]: record[key]
  56. }
  57. }
  58. }),
  59. {transaction}
  60. )
  61. }
  62. }
  63. }
  64. const getAssociations = async (data, json) => {
  65. for (let [key, assoc] of _.toPairs(Type.associations)) {
  66. if (assoc.associationType === 'BelongsToMany' && assoc.target.attributes.key) {
  67. json[key] = (await assoc.get(data, {
  68. attributes: ['id', 'key']
  69. })).map(x => x.key)
  70. }
  71. }
  72. }
  73. const create = async (req, res) => {
  74. const transaction = await sequelize.transaction()
  75. try {
  76. const record = { ...req.body, ...(await subset(req))}
  77. const data = (await Type.create(record, {transaction}))
  78. await setAssociations(record, data, transaction)
  79. await transaction.commit()
  80. res.status(200).send(await sanitize(req, data))
  81. } catch (err) {
  82. await transaction.rollback()
  83. throw err
  84. }
  85. }
  86. const read = async (req, res) => {
  87. const data = (await Type.findOne({where: {id: req.params[opts.routeParam]}}))
  88. const json = await sanitize(req, data)
  89. await getAssociations(data, json)
  90. res.status(200).send(json)
  91. }
  92. const update = async (req, res) => {
  93. const transaction = await sequelize.transaction()
  94. try {
  95. const record = _.omit(req.body, _.keys(await subset(req)))
  96. const updated = (await Type.update(record, { where: { id: req.params[opts.routeParam] }, transaction }))
  97. const data = (await Type.findOne({where: { id: req.params[opts.routeParam] }}))
  98. const json = await sanitize(req, data)
  99. await setAssociations(record, data, transaction)
  100. await transaction.commit()
  101. await getAssociations(data, json)
  102. res.status(200).send(json)
  103. } catch (err) {
  104. await transaction.rollback()
  105. throw err
  106. }
  107. }
  108. const $delete = async (req, res) => {
  109. const data = (await Type.destroy({ where: { id: req.params[opts.routeParam] } }))
  110. res.status(204).end()
  111. }
  112. const trash = async (req, res) => {
  113. const data = (await Type.findAll({
  114. model: Type,
  115. paranoid: false,
  116. where: {
  117. ...(await subset(req)),
  118. deletedAt: { [Op.ne]: null }
  119. }
  120. }))
  121. res.status(200).send(await sanitize(req, data))
  122. }
  123. const undelete = async (req, res) => {
  124. const data = (await Type.update({ deletedAt: null }, {
  125. paranoid: false,
  126. where: { id: req.params[opts.routeParam] }
  127. }))
  128. res.status(200).send(await sanitize(req, data))
  129. }
  130. // TODO: Create, Read, Update, Delete
  131. return {
  132. list,
  133. create,
  134. read,
  135. update,
  136. delete: $delete,
  137. trash,
  138. undelete
  139. }
  140. }
  141. module.exports = crudController