routeGen.js 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303
  1. /**
  2. * @module mongoRouter
  3. *
  4. * Backend module to store React-Redux components in MongoDB.
  5. */
  6. import mongoose from 'mongoose'
  7. import { Router } from 'express'
  8. mongoose.Promise = Promise
  9. const MAX_RESULTS = 100
  10. class RESTController {
  11. constructor (model, key) {
  12. this.model = model
  13. this.modelName = model.modelName.toLowerCase()
  14. this.key = key
  15. }
  16. create (data) {
  17. return this.model
  18. .create(data)
  19. .then(instance => {
  20. const response = {}
  21. response[this.modelName] = instance
  22. return response
  23. })
  24. }
  25. read (id) {
  26. const filter = {}
  27. filter[this.key] = id
  28. return this.model
  29. .findOne(filter)
  30. .then(instance => {
  31. const response = {}
  32. response[this.modelName] = instance
  33. return response
  34. })
  35. }
  36. list () {
  37. return this.model
  38. .find({})
  39. .limit(MAX_RESULTS)
  40. .then(instance => {
  41. const response = {}
  42. response[this.modelName] = instance
  43. return response
  44. })
  45. }
  46. update (id, data) {
  47. const filter = {}
  48. filter[this.key] = id
  49. return this.model
  50. .findOne(filter)
  51. .then(instance => {
  52. for (let attribute in data) {
  53. if (data.hasOwnProperty(attribute) && attribute !== this.key && attribute !== '_id') {
  54. instance[attribute] = data[attribute]
  55. }
  56. }
  57. return instance.save()
  58. })
  59. .then(instance => {
  60. const response = {}
  61. response[this.modelName] = instance
  62. return response
  63. })
  64. }
  65. route () {
  66. const router = new Router()
  67. router.get('/', (req, res) => {
  68. this
  69. .list()
  70. .then(data => res.json(data))
  71. .then(null, error => res.status(404).send(error))
  72. })
  73. router.post('/', (req, res) => {
  74. this
  75. .create(req.body)
  76. .then(data => res.json(data))
  77. .then(null, error => res.status(404).send(error))
  78. })
  79. router.get('/:key', (req, res) => {
  80. this
  81. .read(req.params.key)
  82. .then(data => res.json(data))
  83. .then(null, error => res.status(404).send(error))
  84. })
  85. router.put('/:key', (req, res) => {
  86. this
  87. .update(req.params.key, req.body)
  88. .then(data => res.json(data))
  89. .then(null, error => res.status(404).send(error))
  90. })
  91. router.delete('/:key', (req, res) => {
  92. this
  93. .delete(req.params.key)
  94. .then(data => res.json(data))
  95. .then(null, error => res.status(404).send(error))
  96. })
  97. return router
  98. }
  99. }
  100. export default RESTController
  101. /**
  102. * Generates GET, POST, UPDATE and DELETE handlers which interact with
  103. * MongoDB.
  104. *
  105. * For versioned objects, allow querying for tags or for IDs. When querying
  106. * tags, return the head version, when querying for IDs, just return the
  107. * element.
  108. *
  109. * @param {object} Model - mongoose model to use
  110. * @param {boolean} versioned - use a revision control mechanism
  111. * @return {[type]}
  112. */
  113. function routeGen (Model, versioned = true) {
  114. /** GET handler (request one item or a list of items) */
  115. const get = (req, res) => {
  116. let { id } = req.params
  117. /** check whether an id was specified. */
  118. if (typeof id === 'string' && id.length > 1) {
  119. try {
  120. // try to convert the id string to a valid ObjectId
  121. const _id = mongoose.Types.ObjectId(id)
  122. // if yes, return the one item
  123. Model.findOne({ _id }, function (err, item) {
  124. console.log(item)
  125. if (err) {
  126. res.status(404)
  127. res.send(err)
  128. return
  129. }
  130. res.json(item)
  131. })
  132. } catch (err) {
  133. // If id couldn't be converted, assume it's a tag.
  134. const tag = id
  135. // if yes, return the one item
  136. Model.findOne({ tag, 'history.head': true }, function (err, item) {
  137. if (err) {
  138. res.status(404)
  139. res.send(err)
  140. return
  141. }
  142. res.json(item)
  143. })
  144. }
  145. } else {
  146. // if not, return a list of items
  147. // limit the number of returned items and use an offset
  148. const limit = req.query.limit ? Math.min(Math.max(parseInt(req.query.limit), 1), 100) : 20
  149. const offset = req.query.offset ? Math.max(parseInt(req.query.offset), 0) : 0
  150. Model.find({'history.head': true}, {limit: limit, skip: offset}, function (err, items) {
  151. console.log(err, items)
  152. if (err) {
  153. res.status(404)
  154. res.send(err)
  155. return
  156. }
  157. res.json(items)
  158. })
  159. }
  160. }
  161. /** POST handler (insert a new item into the database) */
  162. const post = (req, res) => {
  163. // create the new item
  164. console.log(req.body)
  165. const item = new Model(req.body)
  166. console.log(item)
  167. // Check, if the model supports revision control
  168. if (typeof Model.schema.obj.__history !== 'undefined') {
  169. console.log('creating new history')
  170. item.__history = {
  171. author: 'Tomi',
  172. created: new Date(),
  173. tag: 'myTag',
  174. reference: 'reference',
  175. head: true,
  176. comment: 'My Comment'
  177. }
  178. }
  179. console.log(item)
  180. // save the item
  181. console.log('before save')
  182. const p = item.save()
  183. console.log(p)
  184. p.then(savedItem => {
  185. console.log('in save', savedItem)
  186. res.status(200)
  187. res.send({ _id: savedItem._id })
  188. }).catch(err => {
  189. console.log('in save', err)
  190. res.status(422)
  191. res.send(err)
  192. })
  193. console.log('after save', p)
  194. }
  195. /** PUT handler (update existing project) */
  196. const put = (req, res) => {
  197. let id = req.params['0']
  198. /** check whether an id was specified. */
  199. if (typeof id === 'string' && id.length > 1) {
  200. // remove leading slash
  201. id = id.substring(1)
  202. try {
  203. // try to convert the id string to a valid ObjectId
  204. id = mongoose.Types.ObjectId(req.params['1'].substring(1))
  205. } catch (err) {
  206. // If id couldn't be converted, return an error message.
  207. res.status(422)
  208. res.send({error: err.message})
  209. return
  210. }
  211. }
  212. // Find the object in the database
  213. Model.findOne({ _id: id }, function (err, item) {
  214. if (err) {
  215. res.status(404)
  216. res.send(err)
  217. }
  218. // Check, if the model supports revision control
  219. if (typeof Model.schema.obj.__history !== 'undefined') {
  220. // If yes, don't update the item, but create a new one based on the original
  221. const newItem = Model(item)
  222. // replace the requested elements
  223. for (let prop in req.body) {
  224. newItem[prop] = req.body[prop]
  225. }
  226. // give it a new history with updated reference array. Set the head-indicator to true
  227. newItem.__history.create({
  228. author: '',
  229. created: Date(),
  230. tag: '',
  231. reference: [ ...item.__history.reference, item._id ],
  232. head: true
  233. })
  234. // set the original item's head indicator to false
  235. item.__history.head = false
  236. // save the documents
  237. newItem.save(function (err) {
  238. if (err) {
  239. res.status(422)
  240. res.send(err)
  241. }
  242. res.json({ message: `${Model.modelName} updated.` })
  243. })
  244. } else {
  245. for (let prop in req.body) {
  246. item[prop] = req.body[prop]
  247. }
  248. }
  249. item.save(function (err) {
  250. if (err) {
  251. res.status(422)
  252. res.send(err)
  253. }
  254. res.json({ message: `${Model.modelName} updated.` })
  255. })
  256. })
  257. }
  258. /** DELETE handler (delete project) */
  259. const del = (req, res) => {
  260. const id = mongoose.Types.ObjectId(req.params['0'])
  261. Model.remove({ _id: id }, function (err, project) {
  262. if (err) {
  263. res.send(err)
  264. }
  265. res.json({ message: 'Movie deleted.' })
  266. })
  267. }
  268. return {
  269. get,
  270. post,
  271. put,
  272. del
  273. }
  274. }
  275. export { routeGen }