/** * @module mongoRouter * * Backend module to store React-Redux components in MongoDB. */ import mongoose from 'mongoose' import { Router } from 'express' mongoose.Promise = Promise const MAX_RESULTS = 100 class RESTController { constructor (model, key) { this.model = model this.modelName = model.modelName.toLowerCase() this.key = key } create (data) { return this.model .create(data) .then(instance => { const response = {} response[this.modelName] = instance return response }) } read (id) { const filter = {} filter[this.key] = id return this.model .findOne(filter) .then(instance => { const response = {} response[this.modelName] = instance return response }) } list () { return this.model .find({}) .limit(MAX_RESULTS) .then(instance => { const response = {} response[this.modelName] = instance return response }) } update (id, data) { const filter = {} filter[this.key] = id return this.model .findOne(filter) .then(instance => { for (let attribute in data) { if (data.hasOwnProperty(attribute) && attribute !== this.key && attribute !== '_id') { instance[attribute] = data[attribute] } } return instance.save() }) .then(instance => { const response = {} response[this.modelName] = instance return response }) } route () { const router = new Router() router.get('/', (req, res) => { this .list() .then(data => res.json(data)) .then(null, error => res.status(404).send(error)) }) router.post('/', (req, res) => { this .create(req.body) .then(data => res.json(data)) .then(null, error => res.status(404).send(error)) }) router.get('/:key', (req, res) => { this .read(req.params.key) .then(data => res.json(data)) .then(null, error => res.status(404).send(error)) }) router.put('/:key', (req, res) => { this .update(req.params.key, req.body) .then(data => res.json(data)) .then(null, error => res.status(404).send(error)) }) router.delete('/:key', (req, res) => { this .delete(req.params.key) .then(data => res.json(data)) .then(null, error => res.status(404).send(error)) }) return router } } export default RESTController /** * Generates GET, POST, UPDATE and DELETE handlers which interact with * MongoDB. * * For versioned objects, allow querying for tags or for IDs. When querying * tags, return the head version, when querying for IDs, just return the * element. * * @param {object} Model - mongoose model to use * @param {boolean} versioned - use a revision control mechanism * @return {[type]} */ function routeGen (Model, versioned = true) { /** GET handler (request one item or a list of items) */ const get = (req, res) => { let { id } = req.params /** check whether an id was specified. */ if (typeof id === 'string' && id.length > 1) { try { // try to convert the id string to a valid ObjectId const _id = mongoose.Types.ObjectId(id) // if yes, return the one item Model.findOne({ _id }, function (err, item) { console.log(item) if (err) { res.status(404) res.send(err) return } res.json(item) }) } catch (err) { // If id couldn't be converted, assume it's a tag. const tag = id // if yes, return the one item Model.findOne({ tag, 'history.head': true }, function (err, item) { if (err) { res.status(404) res.send(err) return } res.json(item) }) } } else { // if not, return a list of items // limit the number of returned items and use an offset const limit = req.query.limit ? Math.min(Math.max(parseInt(req.query.limit), 1), 100) : 20 const offset = req.query.offset ? Math.max(parseInt(req.query.offset), 0) : 0 Model.find({'history.head': true}, {limit: limit, skip: offset}, function (err, items) { console.log(err, items) if (err) { res.status(404) res.send(err) return } res.json(items) }) } } /** POST handler (insert a new item into the database) */ const post = (req, res) => { // create the new item console.log(req.body) const item = new Model(req.body) console.log(item) // Check, if the model supports revision control if (typeof Model.schema.obj.__history !== 'undefined') { console.log('creating new history') item.__history = { author: 'Tomi', created: new Date(), tag: 'myTag', reference: 'reference', head: true, comment: 'My Comment' } } console.log(item) // save the item console.log('before save') const p = item.save() console.log(p) p.then(savedItem => { console.log('in save', savedItem) res.status(200) res.send({ _id: savedItem._id }) }).catch(err => { console.log('in save', err) res.status(422) res.send(err) }) console.log('after save', p) } /** PUT handler (update existing project) */ const put = (req, res) => { let id = req.params['0'] /** check whether an id was specified. */ if (typeof id === 'string' && id.length > 1) { // remove leading slash id = id.substring(1) try { // try to convert the id string to a valid ObjectId id = mongoose.Types.ObjectId(req.params['1'].substring(1)) } catch (err) { // If id couldn't be converted, return an error message. res.status(422) res.send({error: err.message}) return } } // Find the object in the database Model.findOne({ _id: id }, function (err, item) { if (err) { res.status(404) res.send(err) } // Check, if the model supports revision control if (typeof Model.schema.obj.__history !== 'undefined') { // If yes, don't update the item, but create a new one based on the original const newItem = Model(item) // replace the requested elements for (let prop in req.body) { newItem[prop] = req.body[prop] } // give it a new history with updated reference array. Set the head-indicator to true newItem.__history.create({ author: '', created: Date(), tag: '', reference: [ ...item.__history.reference, item._id ], head: true }) // set the original item's head indicator to false item.__history.head = false // save the documents newItem.save(function (err) { if (err) { res.status(422) res.send(err) } res.json({ message: `${Model.modelName} updated.` }) }) } else { for (let prop in req.body) { item[prop] = req.body[prop] } } item.save(function (err) { if (err) { res.status(422) res.send(err) } res.json({ message: `${Model.modelName} updated.` }) }) }) } /** DELETE handler (delete project) */ const del = (req, res) => { const id = mongoose.Types.ObjectId(req.params['0']) Model.remove({ _id: id }, function (err, project) { if (err) { res.send(err) } res.json({ message: 'Movie deleted.' }) }) } return { get, post, put, del } } export { routeGen }