controller.js 4.8 KB

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