RouteInjector/route-injector

View on GitHub
lib/engine/routeinjector/rest/put.js

Summary

Maintainability
F
3 days
Test Coverage
var Q = require('q');
var statusCode = require('http-status-codes');
var utils = require('../utils');
var _ = require('lodash');
var injector = require('../../../');
var async = require("async");
var log = injector.log;
var mongoose = injector.mongoose;

module.exports.putByField = function (Model) {
    var documentDb = {};
    var originalDoc = {};
    return function (req, res) {
        var gConfig = Model.injector();
        var field = gConfig.id;
        var config = utils.getConfigByProfile(gConfig.put, req);

        (config.pre) ? utils.runPreCallbacks(config.pre, Model, req, res, mainPutFn) : mainPutFn();

        function mainPutFn() {
            //ID CHECKING
            if (gConfig.id == "_id" && !mongoose.Types.ObjectId.isValid(req.params[gConfig.id])) {
                res.statusCode = statusCode.NOT_FOUND;
                res.json("Document not found. Invalid mongoose id");
                return res.end();
            }

            var incomingModel = req.body;

            //Check incoming Model
            if (incomingModel == undefined) {
                res.statusCode = statusCode.BAD_REQUEST;
                return res.end();
            }
            else {
                var fieldItem = req.params[field];
                var query = {};
                _.assign(query, config.mongo.query);
                query[field] = fieldItem;

                //Shard key insertion if shard is enabled
                if (gConfig.shard && gConfig.shard.shardKey && incomingModel[gConfig.shard.shardKey] != undefined) {
                    query[gConfig.shard.shardKey] = incomingModel[gConfig.shard.shardKey];
                }

                //Check if the Model exists in the database
                var promise = Model.findOne(query, {__v: 0}).exec();
                var promises = [];
                promises.push(checkDocument);
                if (Model.injector()._references && Model.injector()._references.fromDB) {
                    promises.push(deReference);
                }
                promises.push(update);
                promises.push(postCallbacks);
                promises.push(save);
                promises.push(dispatcher);
                promises.push(ok);
                promises.reduce(Q.when, promise).catch(error);
                //utils.allSeries(promises).then(ok, error);


                function postCallbacks(doc) {
                    var defer = Q.defer();
                    if (config.post) {
                        utils.runPostCallbacks(config, req, res, doc, function () {
                            defer.resolve(doc);
                        });
                    } else {
                        defer.resolve(doc);
                    }

                    return defer.promise;
                }

                function deReference(doc) {
                    //Create the Model object from the json received
                    return Model.injector()._references.fromDB(doc);
                }

                function checkDocument(doc) {
                    var defer = Q.defer();
                    if (!doc) {
                        res.statusCode = statusCode.NOT_FOUND;
                        defer.reject('Document not found');
                    } else {
                        defer.resolve(doc);
                    }
                    return defer.promise;
                }

                function update(doc) {
                    //TODO Alex, plis mira esta ñapa que he puesto, en las recetas viene un JSON plano y en los users viene un documento :?
                    if (doc.toObject) {
                        originalDoc = doc.toObject();
                    } else {
                        originalDoc = _.clone(doc, true);
                    }
                    _.assign(doc, incomingModel);

                    //Shard key insertion if shard is enabled
                    if (gConfig.shard && gConfig.shard.shardKey && gConfig.shard.shardKey != "") {
                        if (doc[gConfig.shard.shardKey] == undefined) {
                            if (req.query[gConfig.shard.shardKey == undefined]) {
                                log.error("Shard key not present in query");
                                res.statusCode = statusCode.BAD_REQUEST;
                                res.json("Shard key not present in query");
                                return res.end();
                            }
                            doc[gConfig.shard.shardKey] = req.query[gConfig.shard.shardKey];
                        }
                    }
                    return doc;
                }

                function save(doc) {
                    var defer = Q.defer();
                    //Create the Model object from the json received
                    if (Model.injector()._references) {
                        if (Model.injector()._references.toDB) {
                            return Model.injector()._references.toDB(doc).then(doUpdate);
                        }
                        else {//After a hook, doc is a javascript object. doUpdate() converts it to a mongo schema
                            return doUpdate(doc);
                        }
                    } else {
                        return doUpdate(doc);
                    }

                    function doUpdate(sdoc) {
                        var query = {_id: sdoc._id};

                        if (gConfig.shard && gConfig.shard.shardKey && gConfig.shard.shardKey != "" && sdoc[gConfig.shard.shardKey]) {
                            query[gConfig.shard.shardKey] = sdoc[gConfig.shard.shardKey];
                        }

                        log.debug("QUERY ", JSON.stringify(query));
                        Model.findOne(query, {__v: 0}, function (err, doc) {
                            log.debug("VERSION | DOC", doc.__v, "SDOC", sdoc.__v);
                            //Add to sdoc, then assing will copy its reference to doc. Coping to doc before or after assign will not work properly
                            //TODO: ADD REQUEST IS COMMENT OUT TO KEEP AAAIDA WORKING
                            //if (sdoc.addRequest) {
                            //    sdoc.addRequest(req);
                            //} else {
                            //    doc.addRequest(req);
                            //}
                            _.assign(doc, sdoc);
                            delete doc.__v;
                            utils.pruneDocument(doc);
                            doc.save(function (err) {
                                if (err) {
                                    defer.reject(err);
                                } else {
                                    defer.resolve(doc);
                                }
                            });
                            documentDb = doc;

                        });
                        return defer.promise;
                    }
                }

                function dispatcher(doc) {
                    if (doc) {
                        documentDb = doc;
                    }
                    //RETURN BACK THE ID MODIFIED
                    var returnJSON = {};
                    returnJSON[field] = fieldItem;
                    res.json(returnJSON);
                    res.statusCode = statusCode.OK;
                }

                function ok() {
                    res.end();
                }

                function error(err) {
                    log.error(err);
                    utils.runErrorCallbacks(config, req, err);

                    if (err.name === "ValidationError") {
                        res.statusCode = statusCode.FORBIDDEN;
                    } else if (res.statusCode == statusCode.OK) {
                        res.statusCode = statusCode.INTERNAL_SERVER_ERROR;
                    }

                    res.json(err);
                    return res.end();
                }

                res.on('finish', function () {
                    if (config.postsend)
                        config.postsend.forEach(function (fn) {
                            fn(documentDb, originalDoc, req);
                        });

                    if (Model.injector()._references && Model.injector()._references.propagate) {
                        Model.injector()._references.propagate(documentDb);
                    }
                });
            }
        }
    }

};