diff --git a/app/templates/_package.json b/app/templates/_package.json index 9dfdadc5d..630e40293 100644 --- a/app/templates/_package.json +++ b/app/templates/_package.json @@ -15,7 +15,9 @@ "lodash": "~2.4.1",<% if(filters.jade) { %> "jade": "~1.2.0",<% } %><% if(filters.html) { %> "ejs": "~0.8.4",<% } %><% if(filters.mongoose) { %> - "mongoose": "~3.8.8",<% } %><% if(filters.auth) { %> + "mongoose": "~3.8.8", + "mongoose-bird": "~0.0.1", + <% } %><% if(filters.auth) { %> "jsonwebtoken": "^0.3.0", "express-jwt": "^0.1.3", "passport": "~0.2.0", diff --git a/app/templates/server/api/thing/thing.controller.js b/app/templates/server/api/thing/thing.controller.js index 72a678cf4..6d9363f3d 100644 --- a/app/templates/server/api/thing/thing.controller.js +++ b/app/templates/server/api/thing/thing.controller.js @@ -9,11 +9,62 @@ 'use strict'; -var _ = require('lodash');<% if (filters.mongoose) { %> -var Thing = require('./thing.model');<% } %> +<% if (filters.mongoose) { %> +var _ = require('lodash'); +var Thing = require('./thing.model'); + +function handleError(res, statusCode){ + statusCode = statusCode || 500; + return function(err){ + res.send(statusCode, err); + }; +} + +function responseWithResult(res, statusCode){ + statusCode = statusCode || 200; + return function(entity){ + if(entity){ + return res.json(statusCode, entity); + } + }; +} + +function handleEntityNotFound(res){ + return function(entity){ + if(!entity){ + res.send(404); + return null; + } + return entity; + }; +} + +function saveUpdates(updates){ + return function(entity){ + var updated = _.merge(entity, updates); + return updated + .saveAsync() + .then(function () { + return updated; + }); + }; +} + +function removeEntity(res){ + return function (entity) { + if(entity){ + return entity.removeAsync() + .then(function() { + return res.send(204); + }); + } + }; +} +<% } %> // Get list of things -exports.index = function(req, res) {<% if (!filters.mongoose) { %> +exports.index = function(req, res) { +<% if (!filters.mongoose) { %> res.json([ { name : 'Development Tools', @@ -34,56 +85,50 @@ exports.index = function(req, res) {<% if (!filters.mongoose) { %> name : 'Deployment Ready', info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' } - ]);<% } %><% if (filters.mongoose) { %> - Thing.find(function (err, things) { - if(err) { return handleError(res, err); } - return res.json(200, things); - });<% } %> -};<% if (filters.mongoose) { %> + ]); +<% } %> +<% if (filters.mongoose) { %> + Thing.findAsync() + .then(responseWithResult(res)) + .catch(handleError(res)); +<% } %> +}; + +<% if (filters.mongoose) { %> // Get a single thing exports.show = function(req, res) { - Thing.findById(req.params.id, function (err, thing) { - if(err) { return handleError(res, err); } - if(!thing) { return res.send(404); } - return res.json(thing); - }); + Thing.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(responseWithResult(res)) + .catch(handleError(res)); }; // Creates a new thing in the DB. exports.create = function(req, res) { - Thing.create(req.body, function(err, thing) { - if(err) { return handleError(res, err); } - return res.json(201, thing); - }); + Thing.createAsync(req.body) + .then(responseWithResult(res, 201)) + .catch(handleError(res)); }; // Updates an existing thing in the DB. exports.update = function(req, res) { - if(req.body._id) { delete req.body._id; } - Thing.findById(req.params.id, function (err, thing) { - if (err) { return handleError(res, err); } - if(!thing) { return res.send(404); } - var updated = _.merge(thing, req.body); - updated.save(function (err) { - if (err) { return handleError(res, err); } - return res.json(200, thing); - }); - }); + if(req.body._id) { + delete req.body._id; + } + Thing.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(saveUpdates(req.body)) + .then(responseWithResult(res)) + .catch(handleError(res)); }; // Deletes a thing from the DB. exports.destroy = function(req, res) { - Thing.findById(req.params.id, function (err, thing) { - if(err) { return handleError(res, err); } - if(!thing) { return res.send(404); } - thing.remove(function(err) { - if(err) { return handleError(res, err); } - return res.send(204); - }); - }); + Thing.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(removeEntity(res)) + .catch(handleError(res)); }; -function handleError(res, err) { - return res.send(500, err); -}<% } %> +<% } %> diff --git a/app/templates/server/api/thing/thing.model(mongoose).js b/app/templates/server/api/thing/thing.model(mongoose).js index 92a791e70..a44bc710e 100644 --- a/app/templates/server/api/thing/thing.model(mongoose).js +++ b/app/templates/server/api/thing/thing.model(mongoose).js @@ -1,6 +1,6 @@ 'use strict'; -var mongoose = require('mongoose'), +var mongoose = require('mongoose-bird')(), Schema = mongoose.Schema; var ThingSchema = new Schema({ diff --git a/app/templates/server/api/user(auth)/user.controller.js b/app/templates/server/api/user(auth)/user.controller.js index 17e6e0e04..e88b691d6 100644 --- a/app/templates/server/api/user(auth)/user.controller.js +++ b/app/templates/server/api/user(auth)/user.controller.js @@ -5,19 +5,37 @@ var passport = require('passport'); var config = require('../../config/environment'); var jwt = require('jsonwebtoken'); -var validationError = function(res, err) { - return res.json(422, err); +var validationError = function(res, statusCode) { + statusCode = statusCode || 422; + return function(err){ + res.json(statusCode, err); + }; }; +function handleError(res, statusCode){ + statusCode = statusCode || 500; + return function(err){ + res.send(statusCode, err); + }; +} + +function respondWith(res, statusCode){ + statusCode = statusCode || 200; + return function(){ + res.send(statusCode); + }; +} + /** * Get list of users * restriction: 'admin' */ exports.index = function(req, res) { - User.find({}, '-salt -password', function (err, users) { - if(err) return res.send(500, err); - res.json(200, users); - }); + User.findAsync({}, '-salt -password') + .then(function (users) { + res.json(200, users); + }) + .catch(handleError(res)); }; /** @@ -27,11 +45,12 @@ exports.create = function (req, res, next) { var newUser = new User(req.body); newUser.provider = 'local'; newUser.role = 'user'; - newUser.save(function(err, user) { - if (err) return validationError(res, err); - var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60*5 }); - res.json({ token: token }); - }); + newUser.saveAsync() + .then(function(user) { + var token = jwt.sign({_id: user._id }, config.secrets.session, { expiresInMinutes: 60*5 }); + res.json({ token: token }); + }) + .catch(validationError(res)); }; /** @@ -40,11 +59,16 @@ exports.create = function (req, res, next) { exports.show = function (req, res, next) { var userId = req.params.id; - User.findById(userId, function (err, user) { - if (err) return next(err); - if (!user) return res.send(401); - res.json(user.profile); - }); + User.findByIdAsync(userId) + .then(function (user) { + if(!user) { + return res.send(401); + } + res.json(user.profile); + }) + .catch(function(err){ + return next(err); + }); }; /** @@ -52,10 +76,9 @@ exports.show = function (req, res, next) { * restriction: 'admin' */ exports.destroy = function(req, res) { - User.findByIdAndRemove(req.params.id, function(err, user) { - if(err) return res.send(500, err); - return res.send(204); - }); + User.findByIdAndRemoveAsync(req.params.id) + .then(respondWith(res, 204)) + .catch(handleError(res)); }; /** @@ -66,21 +89,24 @@ exports.changePassword = function(req, res, next) { var oldPass = String(req.body.oldPassword); var newPass = String(req.body.newPassword); - User.findById(userId, function (err, user) { - user.authenticate(oldPass, function(authErr, authenticated) { - if (authErr) res.send(403); + User.findByIdAsync(userId) + .then(function(user) { + user.authenticate(oldPass, function(authErr, authenticated) { + if (authErr) res.send(403); - if (authenticated) { - user.password = newPass; - user.save(function(err) { - if (err) return validationError(res, err); - res.send(200); - }); - } else { - res.send(403); - } + if (authenticated) { + user.password = newPass; + user.saveAsync() + .then(respondWith(res, 200)) + .catch(validationError(res)); + } else { + res.send(403); + } + }); + }) + .catch(function(authErr){ + res.send(403); }); - }); }; /** @@ -88,13 +114,18 @@ exports.changePassword = function(req, res, next) { */ exports.me = function(req, res, next) { var userId = req.user._id; - User.findOne({ - _id: userId - }, '-salt -password', function(err, user) { // don't ever give out the password or salt - if (err) return next(err); - if (!user) return res.json(401); - res.json(user); - }); + + User.findOneAsync({ _id: userId }, '-salt -password') + .then(function(user) { // don't ever give out the password or salt + if (!user) { + return res.json(401); + } + res.json(user); + }) + .catch(function(err){ + return next(err); + }); + }; /** diff --git a/app/templates/server/api/user(auth)/user.model.js b/app/templates/server/api/user(auth)/user.model.js index b3497f859..a774fa6cb 100644 --- a/app/templates/server/api/user(auth)/user.model.js +++ b/app/templates/server/api/user(auth)/user.model.js @@ -1,6 +1,6 @@ 'use strict'; -var mongoose = require('mongoose'); +var mongoose = require('mongoose-bird')(); var Schema = mongoose.Schema; var crypto = require('crypto');<% if(filters.oauth) { %> var authTypes = ['github', 'twitter', 'facebook', 'google'];<% } %> @@ -53,7 +53,9 @@ UserSchema UserSchema .path('email') .validate(function(email) {<% if (filters.oauth) { %> - if (authTypes.indexOf(this.provider) !== -1) return true;<% } %> + if (authTypes.indexOf(this.provider) !== -1){ + return true; + } <% } %> return email.length; }, 'Email cannot be blank'); @@ -70,14 +72,19 @@ UserSchema .path('email') .validate(function(value, respond) { var self = this; - this.constructor.findOne({email: value}, function(err, user) { - if(err) throw err; - if(user) { - if(self.id === user.id) return respond(true); - return respond(false); - } - respond(true); - }); + return this.constructor.findOneAsync({email: value}) + .then(function(user) { + if(user) { + if(self.id === user.id) { + return respond(true); + } + return respond(false); + } + return respond(true); + }) + .catch(function(err){ + throw err; + }); }, 'The specified email address is already in use.'); var validatePresenceOf = function(value) { diff --git a/app/templates/server/api/user(auth)/user.model.spec.js b/app/templates/server/api/user(auth)/user.model.spec.js index afe8af882..8a643bf63 100644 --- a/app/templates/server/api/user(auth)/user.model.spec.js +++ b/app/templates/server/api/user(auth)/user.model.spec.js @@ -34,21 +34,24 @@ describe('User Model:', function() { }); it('should fail when saving a duplicate user', function(done) { - user.save(function() { - var userDup = new User(user); - userDup.save(function(err) { - err.should.be.instanceOf(Error); - done(); + user.saveAsync() + .then(function() { + var userDup = new User(user); + userDup.saveAsync() + .catch(function(err) { + err.should.be.instanceOf(Error); + done(); + }); }); - }); }); it('should fail when saving without an email', function(done) { user.email = ''; - user.save(function(err) { - err.should.be.instanceOf(Error); - done(); - }); + user.saveAsync() + .catch(function(err) { + err.should.be.instanceOf(Error); + done(); + }); }); }); diff --git a/app/templates/server/app.js b/app/templates/server/app.js index 08b942f43..5369e94ef 100644 --- a/app/templates/server/app.js +++ b/app/templates/server/app.js @@ -8,7 +8,7 @@ process.env.NODE_ENV = process.env.NODE_ENV || 'development'; var express = require('express');<% if (filters.mongoose) { %> -var mongoose = require('mongoose');<% } %> +var mongoose = require('mongoose-bird')();<% } %> var config = require('./config/environment'); <% if (filters.mongoose) { %> // Connect to database @@ -34,4 +34,4 @@ server.listen(config.port, config.ip, function () { }); // Expose app -exports = module.exports = app; \ No newline at end of file +exports = module.exports = app; diff --git a/app/templates/server/auth(auth)/auth.service.js b/app/templates/server/auth(auth)/auth.service.js index 38ec34302..101dcc5c5 100644 --- a/app/templates/server/auth(auth)/auth.service.js +++ b/app/templates/server/auth(auth)/auth.service.js @@ -1,6 +1,6 @@ 'use strict'; -var mongoose = require('mongoose'); +var mongoose = require('mongoose-bird')(); var passport = require('passport'); var config = require('../config/environment'); var jwt = require('jsonwebtoken'); @@ -25,13 +25,17 @@ function isAuthenticated() { }) // Attach user to request .use(function(req, res, next) { - User.findById(req.user._id, function (err, user) { - if (err) return next(err); - if (!user) return res.send(401); - - req.user = user; - next(); - }); + User.findByIdAsync(req.user._id) + .then(function (user) { + if (!user) { + return res.send(401); + } + req.user = user; + next(); + }) + .catch(function(err){ + return next(err); + }); }); } @@ -39,7 +43,9 @@ function isAuthenticated() { * Checks if the user role meets the minimum requirements of the route */ function hasRole(roleRequired) { - if (!roleRequired) throw new Error('Required role needs to be set'); + if (!roleRequired) { + throw new Error('Required role needs to be set'); + } return compose() .use(isAuthenticated()) @@ -64,7 +70,9 @@ function signToken(id) { * Set token cookie directly for oAuth strategies */ function setTokenCookie(req, res) { - if (!req.user) return res.json(404, { message: 'Something went wrong, please try again.'}); + if (!req.user) { + return res.json(404, { message: 'Something went wrong, please try again.'}); + } var token = signToken(req.user._id, req.user.role); res.cookie('token', JSON.stringify(token)); res.redirect('/'); @@ -73,4 +81,4 @@ function setTokenCookie(req, res) { exports.isAuthenticated = isAuthenticated; exports.hasRole = hasRole; exports.signToken = signToken; -exports.setTokenCookie = setTokenCookie; \ No newline at end of file +exports.setTokenCookie = setTokenCookie; diff --git a/app/templates/server/config/express.js b/app/templates/server/config/express.js index 2243a7779..2403780f1 100644 --- a/app/templates/server/config/express.js +++ b/app/templates/server/config/express.js @@ -17,7 +17,7 @@ var config = require('./environment');<% if (filters.auth) { %> var passport = require('passport');<% } %><% if (filters.twitterAuth) { %> var session = require('express-session'); var mongoStore = require('connect-mongo')(session); -var mongoose = require('mongoose');<% } %> +var mongoose = require('mongoose-bird')();<% } %> module.exports = function(app) { var env = app.get('env'); @@ -57,4 +57,4 @@ module.exports = function(app) { app.use(morgan('dev')); app.use(errorHandler()); // Error handler - has to be last } -}; \ No newline at end of file +}; diff --git a/app/templates/server/config/seed(mongoose).js b/app/templates/server/config/seed(mongoose).js index 27ab19417..6d56010b6 100644 --- a/app/templates/server/config/seed(mongoose).js +++ b/app/templates/server/config/seed(mongoose).js @@ -6,44 +6,51 @@ 'use strict'; var Thing = require('../api/thing/thing.model'); -<% if (filters.auth) { %>var User = require('../api/user/user.model');<% } %> +<% if (filters.auth) { %> +var User = require('../api/user/user.model'); +<% } %> -Thing.find({}).remove(function() { - Thing.create({ - name : 'Development Tools', - info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' - }, { - name : 'Server and Client integration', - info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' - }, { - name : 'Smart Build System', - info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' - }, { - name : 'Modular Structure', - info : 'Best practice client and server structures allow for more code reusability and maximum scalability' - }, { - name : 'Optimized Build', - info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' - },{ - name : 'Deployment Ready', - info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' +Thing.find({}).removeAsync() + .then(function() { + Thing.create({ + name : 'Development Tools', + info : 'Integration with popular tools such as Bower, Grunt, Karma, Mocha, JSHint, Node Inspector, Livereload, Protractor, Jade, Stylus, Sass, CoffeeScript, and Less.' + }, { + name : 'Server and Client integration', + info : 'Built with a powerful and fun stack: MongoDB, Express, AngularJS, and Node.' + }, { + name : 'Smart Build System', + info : 'Build system ignores `spec` files, allowing you to keep tests alongside code. Automatic injection of scripts and styles into your index.html' + }, { + name : 'Modular Structure', + info : 'Best practice client and server structures allow for more code reusability and maximum scalability' + }, { + name : 'Optimized Build', + info : 'Build process packs up your templates as a single JavaScript payload, minifies your scripts/css/images, and rewrites asset names for caching.' + },{ + name : 'Deployment Ready', + info : 'Easily deploy your app to Heroku or Openshift with the heroku and openshift subgenerators' + }); }); -});<% if (filters.auth) { %> -User.find({}).remove(function() { - User.create({ - provider: 'local', - name: 'Test User', - email: 'test@test.com', - password: 'test' - }, { - provider: 'local', - role: 'admin', - name: 'Admin', - email: 'admin@admin.com', - password: 'admin' - }, function() { - console.log('finished populating users'); - } - ); -});<% } %> \ No newline at end of file +<% if (filters.auth) { %> + +User.find({}).removeAsync() + .then(function() { + User.create({ + provider: 'local', + name: 'Test User', + email: 'test@test.com', + password: 'test' + }, { + provider: 'local', + role: 'admin', + name: 'Admin', + email: 'admin@admin.com', + password: 'admin' + }, function() { + console.log('finished populating users'); + } + ); + }); +<% } %> diff --git a/endpoint/templates/name.controller.js b/endpoint/templates/name.controller.js index 1d9da544e..9adaacce1 100644 --- a/endpoint/templates/name.controller.js +++ b/endpoint/templates/name.controller.js @@ -1,60 +1,103 @@ 'use strict'; -var _ = require('lodash');<% if (filters.mongoose) { %> -var <%= classedName %> = require('./<%= name %>.model');<% } %> +<% if (filters.mongoose) { %> +var _ = require('lodash'); +var <%= classedName %> = require('./<%= name %>.model'); -// Get list of <%= name %>s -exports.index = function(req, res) {<% if (!filters.mongoose) { %> - res.json([]);<% } %><% if (filters.mongoose) { %> - <%= classedName %>.find(function (err, <%= name %>s) { - if(err) { return handleError(res, err); } - return res.json(200, <%= name %>s); - });<% } %> -};<% if (filters.mongoose) { %> +function handleError(res, statusCode){ + statusCode = statusCode || 500; + return function(err){ + res.send(statusCode, err); + }; +} + +function responseWithResult(res, statusCode){ + statusCode = statusCode || 200; + return function(entity){ + if(entity){ + return res.json(statusCode, entity); + } + }; +} + +function handleEntityNotFound(res){ + return function(entity){ + if(!entity){ + res.send(404); + return null; + } + return entity; + }; +} + +function saveUpdates(updates){ + return function(entity){ + var updated = _.merge(entity, updates); + return updated.saveAsync() + .then(function () { + return updated; + }); + }; +} +function removeEntity(res){ + return function (entity) { + if(entity){ + return entity.removeAsync() + .then(function() { + return res.send(204); + }); + } + }; +} + +<% } %> + +// Get list of <%= name %>s +exports.index = function(req, res) { + <% if (!filters.mongoose) { %> + res.json([]); + <% } %> + <% if (filters.mongoose) { %> + <%= classedName %>.findAsync() + .then(responseWithResult(res)) + .catch(handleError(res)); + <% } %> +}; +<% if (filters.mongoose) { %> // Get a single <%= name %> exports.show = function(req, res) { - <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { - if(err) { return handleError(res, err); } - if(!<%= name %>) { return res.send(404); } - return res.json(<%= name %>); - }); + <%= classedName %>.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(responseWithResult(res)) + .catch(handleError(res)); }; // Creates a new <%= name %> in the DB. exports.create = function(req, res) { - <%= classedName %>.create(req.body, function(err, <%= name %>) { - if(err) { return handleError(res, err); } - return res.json(201, <%= name %>); - }); + <%= classedName %>.createAsync(req.body) + .then(responseWithResult(res, 201)) + .catch(handleError(res)); }; // Updates an existing <%= name %> in the DB. exports.update = function(req, res) { - if(req.body._id) { delete req.body._id; } - <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { - if (err) { return handleError(res, err); } - if(!<%= name %>) { return res.send(404); } - var updated = _.merge(<%= name %>, req.body); - updated.save(function (err) { - if (err) { return handleError(res, err); } - return res.json(200, <%= name %>); - }); - }); + if(req.body._id) { + delete req.body._id; + } + <%= classedName %>.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(saveUpdates(req.body)) + .then(responseWithResult(res)) + .catch(handleError(res)); }; // Deletes a <%= name %> from the DB. exports.destroy = function(req, res) { - <%= classedName %>.findById(req.params.id, function (err, <%= name %>) { - if(err) { return handleError(res, err); } - if(!<%= name %>) { return res.send(404); } - <%= name %>.remove(function(err) { - if(err) { return handleError(res, err); } - return res.send(204); - }); - }); + <%= classedName %>.findByIdAsync(req.params.id) + .then(handleEntityNotFound(res)) + .then(removeEntity(res)) + .catch(handleError(res)); }; -function handleError(res, err) { - return res.send(500, err); -}<% } %> \ No newline at end of file +<% } %> diff --git a/endpoint/templates/name.e2e.js b/endpoint/templates/name.e2e.js index 5960b40a2..badc87b7e 100644 --- a/endpoint/templates/name.e2e.js +++ b/endpoint/templates/name.e2e.js @@ -79,7 +79,7 @@ describe('<%= classedName %> API:', function() { }); describe('PUT <%= route %>/:id', function() { - var updated<%= classedName %> + var updated<%= classedName %>; beforeEach(function(done) { request(app) diff --git a/endpoint/templates/name.model(mongoose).js b/endpoint/templates/name.model(mongoose).js index 89e0dfaa7..9b23d9d41 100644 --- a/endpoint/templates/name.model(mongoose).js +++ b/endpoint/templates/name.model(mongoose).js @@ -1,6 +1,6 @@ 'use strict'; -var mongoose = require('mongoose'), +var mongoose = require('mongoose-bird')(), Schema = mongoose.Schema; var <%= classedName %>Schema = new Schema({ @@ -9,4 +9,4 @@ var <%= classedName %>Schema = new Schema({ active: Boolean }); -module.exports = mongoose.model('<%= classedName %>', <%= classedName %>Schema); \ No newline at end of file +module.exports = mongoose.model('<%= classedName %>', <%= classedName %>Schema); diff --git a/endpoint/templates/name.spec.js b/endpoint/templates/name.spec.js new file mode 100644 index 000000000..e2d6f10f4 --- /dev/null +++ b/endpoint/templates/name.spec.js @@ -0,0 +1,26 @@ +'use strict'; + +var app = require('../../app'); +var request = require('supertest'); + +describe('GET <%= route %>', function() { + + it('should respond with JSON array', function(done) { + request(app) + .get('<%= route %>') + .expect(200) + .expect('Content-Type', /json/) + .end(function(err, res) { + if (err) return done(err); + res.body.should.be.instanceof(Array); + done(); + }); + }); + + it('should respond with 404 not found for an invalid id', function(done) { + request(app) + .get('<%= route %>/520169c8f727f28610e3395f') + .expect(404, done); + }); + +});