diff --git a/core/bootstrap.js b/core/bootstrap.js index 8e0737f06d..c632e0c776 100644 --- a/core/bootstrap.js +++ b/core/bootstrap.js @@ -6,7 +6,7 @@ var fs = require('fs'), url = require('url'), - when = require('when'), + Promise = require('bluebird'), validator = require('validator'), errors = require('./server/errors'), config = require('./server/config'), @@ -20,43 +20,42 @@ function readConfigFile(envVal) { } function writeConfigFile() { - var written = when.defer(); - /* Check for config file and copy from config.example.js if one doesn't exist. After that, start the server. */ - fs.exists(configExample, function checkTemplate(templateExists) { - var read, - write, - error; + return new Promise(function (resolve, reject) { + fs.exists(configExample, function checkTemplate(templateExists) { + var read, + write, + error; - if (!templateExists) { - error = new Error('Could not locate a configuration file.'); - error.context = appRoot; - error.help = 'Please check your deployment for config.js or config.example.js.'; + if (!templateExists) { + error = new Error('Could not locate a configuration file.'); + error.context = appRoot; + error.help = 'Please check your deployment for config.js or config.example.js.'; - return written.reject(error); - } + return reject(error); + } - // Copy config.example.js => config.js - read = fs.createReadStream(configExample); - read.on('error', function (err) { - errors.logError(new Error('Could not open config.example.js for read.'), appRoot, 'Please check your deployment for config.js or config.example.js.'); + // Copy config.example.js => config.js + read = fs.createReadStream(configExample); + read.on('error', function (err) { + errors.logError(new Error('Could not open config.example.js for read.'), appRoot, 'Please check your deployment for config.js or config.example.js.'); - return written.reject(err); + reject(err); + }); + + write = fs.createWriteStream(configFile); + write.on('error', function (err) { + errors.logError(new Error('Could not open config.js for write.'), appRoot, 'Please check your deployment for config.js or config.example.js.'); + + reject(err); + }); + + write.on('finish', resolve); + + read.pipe(write); }); - - write = fs.createWriteStream(configFile); - write.on('error', function (err) { - errors.logError(new Error('Could not open config.js for write.'), appRoot, 'Please check your deployment for config.js or config.example.js.'); - - return written.reject(err); - }); - write.on('finish', written.resolve); - - read.pipe(write); }); - - return written.promise; } function validateConfigEnvironment() { @@ -70,7 +69,7 @@ function validateConfigEnvironment() { config = readConfigFile(envVal); } catch (e) { - return when.reject(e); + return Promise.reject(e); } // Check if we don't even have a config @@ -78,14 +77,14 @@ function validateConfigEnvironment() { errors.logError(new Error('Cannot find the configuration for the current NODE_ENV'), 'NODE_ENV=' + envVal, 'Ensure your config.js has a section for the current NODE_ENV value and is formatted properly.'); - return when.reject(new Error('Unable to load config for NODE_ENV=' + envVal)); + return Promise.reject(new Error('Unable to load config for NODE_ENV=' + envVal)); } // Check that our url is valid if (!validator.isURL(config.url, { protocols: ['http', 'https'], require_protocol: true })) { errors.logError(new Error('Your site url in config.js is invalid.'), config.url, 'Please make sure this is a valid url before restarting'); - return when.reject(new Error('invalid site url')); + return Promise.reject(new Error('invalid site url')); } parsedUrl = url.parse(config.url || 'invalid', false, true); @@ -93,14 +92,14 @@ function validateConfigEnvironment() { if (/\/ghost(\/|$)/.test(parsedUrl.pathname)) { errors.logError(new Error('Your site url in config.js cannot contain a subdirectory called ghost.'), config.url, 'Please rename the subdirectory before restarting'); - return when.reject(new Error('ghost subdirectory not allowed')); + return Promise.reject(new Error('ghost subdirectory not allowed')); } // Check that we have database values if (!config.database || !config.database.client) { errors.logError(new Error('Your database configuration in config.js is invalid.'), JSON.stringify(config.database), 'Please make sure this is a valid Bookshelf database configuration'); - return when.reject(new Error('invalid database configuration')); + return Promise.reject(new Error('invalid database configuration')); } hasHostAndPort = config.server && !!config.server.host && !!config.server.port; @@ -110,33 +109,32 @@ function validateConfigEnvironment() { if (!config.server || !(hasHostAndPort || hasSocket)) { errors.logError(new Error('Your server values (socket, or host and port) in config.js are invalid.'), JSON.stringify(config.server), 'Please provide them before restarting.'); - return when.reject(new Error('invalid server configuration')); + return Promise.reject(new Error('invalid server configuration')); } - return when.resolve(config); + return Promise.resolve(config); } function loadConfig(configFilePath) { - var loaded = when.defer(), - pendingConfig; - - // Allow config file path to be taken from, in order of importance: - // environment process, passed in value, default location configFile = process.env.GHOST_CONFIG || configFilePath || config.paths.config; /* Check for config file and copy from config.example.js if one doesn't exist. After that, start the server. */ - fs.exists(configFile, function checkConfig(configExists) { - if (!configExists) { - pendingConfig = writeConfigFile(); - } + return new Promise(function (resolve, reject) { + fs.exists(configFile, function (exists) { + var pendingConfig; - when(pendingConfig).then(validateConfigEnvironment).then(function (rawConfig) { - return config.init(rawConfig).then(loaded.resolve); - }).catch(loaded.reject); + if (!exists) { + pendingConfig = writeConfigFile(); + } + + Promise.resolve(pendingConfig) + .then(validateConfigEnvironment) + .then(function (rawConfig) { + resolve(config.init(rawConfig)); + }).catch(reject); + }); }); - - return loaded.promise; } module.exports = loadConfig; diff --git a/core/index.js b/core/index.js index 3f2b8dddb0..66f4471918 100644 --- a/core/index.js +++ b/core/index.js @@ -2,33 +2,17 @@ // Orchestrates the loading of Ghost // When run from command line. -var when = require('when'), - bootstrap = require('./bootstrap'), +var bootstrap = require('./bootstrap'), server = require('./server'); process.env.NODE_ENV = process.env.NODE_ENV || 'development'; function makeGhost(options) { - var deferred = when.defer(); - options = options || {}; - bootstrap(options.config).then(function () { - try { - return server(options.app) - .then(deferred.resolve) - .catch(function (err) { - // We don't return the rejected promise to stop - // the propagation of the rejection and just - // allow the user to manage what to do. - deferred.reject(err); - }); - } catch (e) { - deferred.reject(e); - } - }).catch(deferred.reject); - - return deferred.promise; + return bootstrap(options.config).then(function () { + return server(options.app); + }); } module.exports = makeGhost; diff --git a/core/server/GhostServer.js b/core/server/GhostServer.js index 4866366cd9..98eea8a787 100644 --- a/core/server/GhostServer.js +++ b/core/server/GhostServer.js @@ -1,4 +1,4 @@ -var when = require('when'), +var Promise = require('bluebird'), fs = require('fs'), semver = require('semver'), packageInfo = require('../../package.json'), @@ -88,54 +88,59 @@ GhostServer.prototype.logUpgradeWarning = function () { // Starts the ghost server listening on the configured port GhostServer.prototype.start = function () { + var self = this; + // ## Start Ghost App - var deferred = when.defer(); - if (config.getSocket()) { - // Make sure the socket is gone before trying to create another - try { - fs.unlinkSync(config.getSocket()); - } catch (e) { - // We can ignore this. + return new Promise(function (resolve) { + if (config.getSocket()) { + // Make sure the socket is gone before trying to create another + try { + fs.unlinkSync(config.getSocket()); + } catch (e) { + // We can ignore this. + } + + self.httpServer = self.app.listen( + config.getSocket() + ); + + fs.chmod(config.getSocket(), '0660'); + + } else { + self.httpServer = self.app.listen( + config.server.port, + config.server.host + ); } - this.httpServer = this.app.listen( - config.getSocket() - ); - fs.chmod(config.getSocket(), '0660'); - - } else { - this.httpServer = this.app.listen( - config.server.port, - config.server.host - ); - } - - this.httpServer.on('connection', this.connection.bind(this)); - this.httpServer.on('listening', function () { - this.logStartMessages(); - clearTimeout(this.upgradeWarning); - deferred.resolve(this); - }.bind(this)); - return deferred.promise; + self.httpServer.on('connection', self.connection.bind(self)); + self.httpServer.on('listening', function () { + self.logStartMessages(); + clearTimeout(self.upgradeWarning); + resolve(self); + }); + }); }; // Returns a promise that will be fulfilled when the server stops. // If the server has not been started, the promise will be fulfilled // immediately GhostServer.prototype.stop = function () { - var deferred = when.defer(); + var self = this; - if (this.httpServer === null) { - deferred.resolve(this); - } else { - this.httpServer.close(function () { - this.httpServer = null; - this.logShutdownMessages(); - deferred.resolve(this); - }.bind(this)); - this.closeConnections(); - } - return deferred.promise; + return new Promise(function (resolve) { + if (self.httpServer === null) { + resolve(self); + } else { + self.httpServer.close(function () { + self.httpServer = null; + self.logShutdownMessages(); + resolve(self); + }); + + self.closeConnections(); + } + }); }; // Restarts the ghost application @@ -146,8 +151,8 @@ GhostServer.prototype.restart = function () { // To be called after `stop` GhostServer.prototype.hammertime = function () { console.log('Can\'t touch this'.green); - return this; + + return Promise.resolve(this); }; module.exports = GhostServer; - diff --git a/core/server/api/authentication.js b/core/server/api/authentication.js index de4df0b270..6e61b431fe 100644 --- a/core/server/api/authentication.js +++ b/core/server/api/authentication.js @@ -5,7 +5,7 @@ var _ = require('lodash'), globalUtils = require('../utils'), utils = require('./utils'), users = require('./users'), - when = require('when'), + Promise = require('bluebird'), errors = require('../errors'), config = require('../config'), authentication; @@ -31,7 +31,7 @@ authentication = { var setup = result.setup[0].status; if (!setup) { - return when.reject(new errors.NoPermissionError('Setup must be completed before making this request.')); + return Promise.reject(new errors.NoPermissionError('Setup must be completed before making this request.')); } return utils.checkObject(object, 'passwordreset'); @@ -39,13 +39,10 @@ authentication = { if (checkedPasswordReset.passwordreset[0].email) { email = checkedPasswordReset.passwordreset[0].email; } else { - return when.reject(new errors.BadRequestError('No email provided.')); + return Promise.reject(new errors.BadRequestError('No email provided.')); } - return users.read({ context: {internal: true}, email: email, status: 'active' }).then(function (foundUser) { - if (!foundUser) { - when.reject(new errors.NotFound('Invalid email address')); - } + return users.read({ context: {internal: true}, email: email, status: 'active' }).then(function () { return settings.read({context: {internal: true}, key: 'dbHash'}); }).then(function (response) { var dbHash = response.settings[0].value; @@ -69,9 +66,9 @@ authentication = { }; return mail.send(payload, {context: {internal: true}}); }).then(function () { - return when.resolve({passwordreset: [{message: 'Check your email for further instructions.'}]}); - }).otherwise(function (error) { - return when.reject(error); + return Promise.resolve({passwordreset: [{message: 'Check your email for further instructions.'}]}); + }).catch(function (error) { + return Promise.reject(error); }); }); }, @@ -91,7 +88,7 @@ authentication = { var setup = result.setup[0].status; if (!setup) { - return when.reject(new errors.NoPermissionError('Setup must be completed before making this request.')); + return Promise.reject(new errors.NoPermissionError('Setup must be completed before making this request.')); } return utils.checkObject(object, 'passwordreset'); @@ -104,9 +101,9 @@ authentication = { var dbHash = response.settings[0].value; return dataProvider.User.resetPassword(resetToken, newPassword, ne2Password, dbHash); }).then(function () { - return when.resolve({passwordreset: [{message: 'Password changed successfully.'}]}); - }).otherwise(function (error) { - return when.reject(new errors.UnauthorizedError(error.message)); + return Promise.resolve({passwordreset: [{message: 'Password changed successfully.'}]}); + }).catch(function (error) { + return Promise.reject(new errors.UnauthorizedError(error.message)); }); }); }, @@ -127,7 +124,7 @@ authentication = { var setup = result.setup[0].status; if (!setup) { - return when.reject(new errors.NoPermissionError('Setup must be completed before making this request.')); + return Promise.reject(new errors.NoPermissionError('Setup must be completed before making this request.')); } return utils.checkObject(object, 'invitation'); @@ -144,9 +141,9 @@ authentication = { }).then(function (user) { return dataProvider.User.edit({name: name, email: email}, {id: user.id}); }).then(function () { - return when.resolve({invitation: [{message: 'Invitation accepted.'}]}); - }).otherwise(function (error) { - return when.reject(new errors.UnauthorizedError(error.message)); + return Promise.resolve({invitation: [{message: 'Invitation accepted.'}]}); + }).catch(function (error) { + return Promise.reject(new errors.UnauthorizedError(error.message)); }); }); }, @@ -156,9 +153,9 @@ authentication = { qb.whereIn('status', ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked']); }).fetch().then(function (users) { if (users) { - return when.resolve({ setup: [{status: true}]}); + return Promise.resolve({ setup: [{status: true}]}); } else { - return when.resolve({ setup: [{status: false}]}); + return Promise.resolve({ setup: [{status: false}]}); } }); }, @@ -171,7 +168,7 @@ authentication = { var setup = result.setup[0].status; if (setup) { - return when.reject(new errors.NoPermissionError('Setup has already been completed.')); + return Promise.reject(new errors.NoPermissionError('Setup has already been completed.')); } return utils.checkObject(object, 'setup'); @@ -226,7 +223,7 @@ authentication = { }] }; - return mail.send(payload, {context: {internal: true}}).otherwise(function (error) { + return mail.send(payload, {context: {internal: true}}).catch(function (error) { errors.logError( error.message, "Unable to send welcome email, your blog will continue to function.", @@ -235,7 +232,7 @@ authentication = { }); }).then(function () { - return when.resolve({ users: [setupUser]}); + return Promise.resolve({ users: [setupUser]}); }); } }; diff --git a/core/server/api/db.js b/core/server/api/db.js index c22064d7e6..b13e90c93b 100644 --- a/core/server/api/db.js +++ b/core/server/api/db.js @@ -4,8 +4,7 @@ var dataExport = require('../data/export'), dataImport = require('../data/import'), dataProvider = require('../models'), fs = require('fs-extra'), - when = require('when'), - nodefn = require('when/node'), + Promise = require('bluebird'), _ = require('lodash'), path = require('path'), errors = require('../../server/errors'), @@ -41,14 +40,14 @@ db = { // Export data, otherwise send error 500 return canThis(options.context).exportContent.db().then(function () { - return dataExport().then(function (exportedData) { - return when.resolve({ db: [exportedData] }); - }).otherwise(function (error) { - return when.reject(new errors.InternalServerError(error.message || error)); + return dataExport().then(function (exportedData) { + return { db: [exportedData] }; + }).catch(function (error) { + return Promise.reject(new errors.InternalServerError(error.message || error)); + }); + }, function () { + return Promise.reject(new errors.NoPermissionError('You do not have permission to export data. (no rights)')); }); - }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to export data. (no rights)')); - }); }, /** * ### Import Content @@ -67,16 +66,16 @@ db = { return canThis(options.context).importContent.db().then(function () { if (!options.importfile || !options.importfile.type || !options.importfile.path) { - return when.reject(new errors.NoPermissionError('Please select a file to import.')); + return Promise.reject(new errors.NoPermissionError('Please select a file to import.')); } type = options.importfile.type; ext = path.extname(options.importfile.name).toLowerCase(); filepath = options.importfile.path; - return when(isValidFile(ext)).then(function (result) { + return Promise.resolve(isValidFile(ext)).then(function (result) { if (!result) { - return when.reject(new errors.UnsupportedMediaTypeError('Please select a .json file to import.')); + return Promise.reject(new errors.UnsupportedMediaTypeError('Please select a .json file to import.')); } }).then(function () { return api.settings.read( @@ -84,12 +83,12 @@ db = { ).then(function (response) { var setting = response.settings[0]; - return when(setting.value); + return setting.value; }); }).then(function (version) { databaseVersion = version; // Read the file contents - return nodefn.call(fs.readFile, filepath); + return Promise.promisify(fs.readFile)(filepath); }).then(function (fileContents) { var importData; @@ -103,11 +102,11 @@ db = { } } catch (e) { errors.logError(e, 'API DB import content', 'check that the import file is valid JSON.'); - return when.reject(new errors.BadRequest('Failed to parse the import JSON file')); + return Promise.reject(new errors.BadRequest('Failed to parse the import JSON file')); } if (!importData.meta || !importData.meta.version) { - return when.reject( + return Promise.reject( new errors.ValidationError('Import data does not specify version', 'meta.version') ); } @@ -115,16 +114,14 @@ db = { // Import for the current version return dataImport(databaseVersion, importData); - }).then(function importSuccess() { - return api.settings.updateSettingsCache(); - }).then(function () { - return when.resolve({ db: [] }); - }).finally(function () { + }).then(api.settings.updateSettingsCache) + .return({ db: [] }) + .finally(function () { // Unlink the file after import - return nodefn.call(fs.unlink, filepath); + return Promise.promisify(fs.unlink)(filepath); }); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to import data. (no rights)')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to import data. (no rights)')); }); }, /** @@ -139,14 +136,13 @@ db = { options = options || {}; return canThis(options.context).deleteAllContent.db().then(function () { - return when(dataProvider.deleteAllContent()) - .then(function () { - return when.resolve({ db: [] }); - }, function (error) { - return when.reject(new errors.InternalServerError(error.message || error)); + return Promise.resolve(dataProvider.deleteAllContent()) + .return({ db: [] }) + .catch(function (error) { + return Promise.reject(new errors.InternalServerError(error.message || error)); }); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to export data. (no rights)')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to export data. (no rights)')); }); } }; diff --git a/core/server/api/index.js b/core/server/api/index.js index 81efc15a13..359af6c31f 100644 --- a/core/server/api/index.js +++ b/core/server/api/index.js @@ -5,7 +5,7 @@ // from a theme, an app, or from an external app, you'll use the Ghost JSON API to do so. var _ = require('lodash'), - when = require('when'), + Promise = require('bluebird'), config = require('../config'), // Include Endpoints db = require('./db'), @@ -90,7 +90,7 @@ cacheInvalidationHeader = function (req, result) { } } - return when(cacheInvalidate); + return Promise.resolve(cacheInvalidate); }; /** @@ -122,7 +122,7 @@ locationHeader = function (req, result) { } } - return when(location); + return Promise.resolve(location); }; /** @@ -219,7 +219,7 @@ addHeaders = function (apiMethod, req, res, result) { ops.push(contentDisposition); } - return when.all(ops); + return Promise.all(ops); }; /** diff --git a/core/server/api/mail.js b/core/server/api/mail.js index ede252eb2f..545a0d4977 100644 --- a/core/server/api/mail.js +++ b/core/server/api/mail.js @@ -1,7 +1,7 @@ // # Mail API // API for sending Mail var _ = require('lodash'), - when = require('when'), + Promise = require('bluebird'), config = require('../config'), canThis = require('../permissions').canThis, errors = require('../errors'), @@ -42,12 +42,12 @@ mail = { }; return object; }) - .otherwise(function (error) { - return when.reject(new errors.EmailError(error.message)); + .catch(function (error) { + return Promise.reject(new errors.EmailError(error.message)); }); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to send mail.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to send mail.')); }); }, @@ -73,7 +73,7 @@ mail = { return mail.send(payload, options); }); }, function () { - return when.reject(new errors.NotFoundError('Could not find the current user')); + return Promise.reject(new errors.NotFoundError('Could not find the current user')); }); }, @@ -95,7 +95,7 @@ mail = { _.templateSettings.interpolate = /{{([\s\S]+?)}}/g; //read the proper email body template - return when.promise(function (resolve, reject) { + return new Promise(function (resolve, reject) { fs.readFile(templatesDir + '/' + options.template + '.html', {encoding: 'utf8'}, function (err, fileContent) { if (err) { reject(err); diff --git a/core/server/api/notifications.js b/core/server/api/notifications.js index 649e0bf718..29a07a4f34 100644 --- a/core/server/api/notifications.js +++ b/core/server/api/notifications.js @@ -1,6 +1,6 @@ // # Notifications API // RESTful API for creating notifications -var when = require('when'), +var Promise = require('bluebird'), _ = require('lodash'), canThis = require('../permissions').canThis, errors = require('../errors'), @@ -26,9 +26,9 @@ notifications = { */ browse: function browse(options) { return canThis(options.context).browse.notification().then(function () { - return when({ 'notifications': notificationsStore }); + return { 'notifications': notificationsStore }; }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to browse notifications.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to browse notifications.')); }); }, @@ -69,10 +69,10 @@ notifications = { addedNotifications.push(notification); }); - return when({ notifications: addedNotifications}); + return { notifications: addedNotifications }; }); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to add notifications.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to add notifications.')); }); }, @@ -90,21 +90,21 @@ notifications = { }); if (notification && !notification.dismissible) { - return when.reject( + return Promise.reject( new errors.NoPermissionError('You do not have permission to dismiss this notification.') ); } if (!notification) { - return when.reject(new errors.NotFoundError('Notification does not exist.')); + return Promise.reject(new errors.NotFoundError('Notification does not exist.')); } notificationsStore = _.reject(notificationsStore, function (element) { return element.id === parseInt(options.id, 10); }); - return when({notifications: [notification]}); + return { notifications: [notification] }; }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to destroy notifications.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to destroy notifications.')); }); }, @@ -119,9 +119,10 @@ notifications = { return canThis(options.context).destroy.notification().then(function () { notificationsStore = []; notificationCounter = 0; - return when(notificationsStore); + + return notificationsStore; }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to destroy notifications.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to destroy notifications.')); }); } }; diff --git a/core/server/api/posts.js b/core/server/api/posts.js index f6cfcdb814..371b255f8a 100644 --- a/core/server/api/posts.js +++ b/core/server/api/posts.js @@ -1,6 +1,6 @@ // # Posts API // RESTful API for the Post resource -var when = require('when'), +var Promise = require('bluebird'), _ = require('lodash'), dataProvider = require('../models'), canThis = require('../permissions').canThis, @@ -88,8 +88,7 @@ posts = { return { posts: [ result.toJSON() ]}; } - return when.reject(new errors.NotFoundError('Post not found.')); - + return Promise.reject(new errors.NotFoundError('Post not found.')); }); }, @@ -122,10 +121,10 @@ posts = { return { posts: [ post ]}; } - return when.reject(new errors.NotFoundError('Post not found.')); + return Promise.reject(new errors.NotFoundError('Post not found.')); }); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to edit this post.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to edit posts.')); }); }, @@ -158,7 +157,7 @@ posts = { return { posts: [ post ]}; }); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to add posts.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to add posts.')); }); }, @@ -188,7 +187,7 @@ posts = { }); }); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to remove posts.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to remove posts.')); }); } diff --git a/core/server/api/roles.js b/core/server/api/roles.js index 9f8781b5d9..ea07821aec 100644 --- a/core/server/api/roles.js +++ b/core/server/api/roles.js @@ -1,6 +1,6 @@ // # Roles API // RESTful API for the Role resource -var when = require('when'), +var Promise = require('bluebird'), _ = require('lodash'), canThis = require('../permissions').canThis, dataProvider = require('../models'), @@ -45,12 +45,12 @@ roles = { return null; } return role; - }, function () { + }).catch(function () { return null; })); }); - return when.all(permissionMap).then(function (resolved) { + return Promise.all(permissionMap).then(function (resolved) { return { roles: _.filter(resolved, function (role) { return role !== null; }) }; diff --git a/core/server/api/settings.js b/core/server/api/settings.js index ae537b31e7..f7072f6268 100644 --- a/core/server/api/settings.js +++ b/core/server/api/settings.js @@ -2,7 +2,7 @@ // RESTful API for the Setting resource var _ = require('lodash'), dataProvider = require('../models'), - when = require('when'), + Promise = require('bluebird'), config = require('../config'), canThis = require('../permissions').canThis, errors = require('../errors'), @@ -44,7 +44,7 @@ updateSettingsCache = function (settings) { settingsCache[key] = setting; }); - return when(settingsCache); + return Promise.resolve(settingsCache); } return dataProvider.Settings.findAll() @@ -206,14 +206,14 @@ populateDefaultSetting = function (key) { }).then(function () { // Get the result from the cache with permission checks }); - }).otherwise(function (err) { + }).catch(function (err) { // Pass along NotFoundError if (typeof err === errors.NotFoundError) { - return when.reject(err); + return Promise.reject(err); } // TODO: Different kind of error? - return when.reject(new errors.NotFoundError('Problem finding setting: ' + key)); + return Promise.reject(new errors.NotFoundError('Problem finding setting: ' + key)); }); }; @@ -227,13 +227,13 @@ populateDefaultSetting = function (key) { canEditAllSettings = function (settingsInfo, options) { var checkSettingPermissions = function (setting) { if (setting.type === 'core' && !(options.context && options.context.internal)) { - return when.reject( + return Promise.reject( new errors.NoPermissionError('Attempted to access core setting from external request') ); } return canThis(options.context).edit.setting(setting.key).catch(function () { - return when.reject(new errors.NoPermissionError('You do not have permission to edit settings.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to edit settings.')); }); }, @@ -251,7 +251,7 @@ canEditAllSettings = function (settingsInfo, options) { return checkSettingPermissions(setting); }); - return when.all(checks); + return Promise.all(checks); }; /** @@ -281,7 +281,7 @@ settings = { // If there is no context, return only blog settings if (!options.context) { - return when(_.filter(result.settings, function (setting) { return setting.type === 'blog'; })); + return Promise.resolve(_.filter(result.settings, function (setting) { return setting.type === 'blog'; })); } // Otherwise return whatever this context is allowed to browse @@ -312,19 +312,19 @@ settings = { result[options.key] = setting; if (setting.type === 'core' && !(options.context && options.context.internal)) { - return when.reject( + return Promise.reject( new errors.NoPermissionError('Attempted to access core setting from external request') ); } if (setting.type === 'blog') { - return when(settingsResult(result)); + return Promise.resolve(settingsResult(result)); } return canThis(options.context).read.setting(options.key).then(function () { return settingsResult(result); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to read settings.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to read settings.')); }); }; diff --git a/core/server/api/slugs.js b/core/server/api/slugs.js index 2a723ebffc..ac1b8dc96a 100644 --- a/core/server/api/slugs.js +++ b/core/server/api/slugs.js @@ -3,7 +3,7 @@ var canThis = require('../permissions').canThis, dataProvider = require('../models'), errors = require('../errors'), - when = require('when'), + Promise = require('bluebird'), slugs, allowedTypes; @@ -35,25 +35,25 @@ slugs = { return canThis(options.context).generate.slug().then(function () { if (allowedTypes[options.type] === undefined) { - return when.reject(new errors.BadRequestError('Unknown slug type \'' + options.type + '\'.')); + return Promise.reject(new errors.BadRequestError('Unknown slug type \'' + options.type + '\'.')); } return dataProvider.Base.Model.generateSlug(allowedTypes[options.type], options.name, {status: 'all'}).then(function (slug) { if (!slug) { - return when.reject(new errors.InternalServerError('Could not generate slug.')); + return Promise.reject(new errors.InternalServerError('Could not generate slug.')); } return { slugs: [{ slug: slug }] }; }); }).catch(function (err) { if (err) { - return when.reject(err); + return Promise.reject(err); } - return when.reject(new errors.NoPermissionError('You do not have permission to generate a slug.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to generate a slug.')); }); } }; -module.exports = slugs; \ No newline at end of file +module.exports = slugs; diff --git a/core/server/api/tags.js b/core/server/api/tags.js index ba29198a57..b49bc59aa6 100644 --- a/core/server/api/tags.js +++ b/core/server/api/tags.js @@ -1,6 +1,6 @@ // # Tag API // RESTful API for the Tag resource -var when = require('when'), +var Promise = require('bluebird'), canThis = require('../permissions').canThis, dataProvider = require('../models'), errors = require('../errors'), @@ -24,9 +24,9 @@ tags = { }); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to browse tags.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to browse tags.')); }); } }; -module.exports = tags; \ No newline at end of file +module.exports = tags; diff --git a/core/server/api/themes.js b/core/server/api/themes.js index f007cab45e..e3f23c812e 100644 --- a/core/server/api/themes.js +++ b/core/server/api/themes.js @@ -1,12 +1,11 @@ // # Themes API // RESTful API for Themes -var when = require('when'), +var Promise = require('bluebird'), _ = require('lodash'), canThis = require('../permissions').canThis, config = require('../config'), errors = require('../errors'), settings = require('./settings'), - when = require('when'), themes; /** @@ -25,7 +24,7 @@ themes = { options = options || {}; return canThis(options.context).browse.theme().then(function () { - return when.all([ + return Promise.all([ settings.read({key: 'activeTheme', context: {internal: true}}), config.paths.availableThemes ]).then(function (result) { @@ -57,7 +56,7 @@ themes = { return { themes: themes }; }); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to browse themes.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to browse themes.')); }); }, @@ -73,7 +72,7 @@ themes = { // Check whether the request is properly formatted. if (!_.isArray(object.themes)) { - return when.reject({type: 'BadRequest', message: 'Invalid request.'}); + return Promise.reject({type: 'BadRequest', message: 'Invalid request.'}); } themeName = object.themes[0].uuid; @@ -88,7 +87,7 @@ themes = { }); if (!theme) { - return when.reject(new errors.BadRequestError('Theme does not exist.')); + return Promise.reject(new errors.BadRequestError('Theme does not exist.')); } // Activate the theme @@ -100,7 +99,7 @@ themes = { }); }); }, function () { - return when.reject(new errors.NoPermissionError('You do not have permission to edit themes.')); + return Promise.reject(new errors.NoPermissionError('You do not have permission to edit themes.')); }); } }; diff --git a/core/server/api/upload.js b/core/server/api/upload.js index 4b7c15c66c..5429fe266f 100644 --- a/core/server/api/upload.js +++ b/core/server/api/upload.js @@ -1,14 +1,11 @@ -var when = require('when'), +var Promise = require('bluebird'), path = require('path'), - nodefn = require('when/node'), fs = require('fs-extra'), storage = require('../storage'), errors = require('../errors'), upload; - - function isImage(type, ext) { if ((type === 'image/jpeg' || type === 'image/png' || type === 'image/gif' || type === 'image/svg+xml') && (ext === '.jpg' || ext === '.jpeg' || ext === '.png' || ext === '.gif' || ext === '.svg' || ext === '.svgz')) { @@ -17,7 +14,6 @@ function isImage(type, ext) { return false; } - /** * ## Upload API Methods * @@ -32,33 +28,33 @@ upload = { * @param {{context}} options * @returns {Promise} Success */ - 'add': function (options) { + add: function (options) { var store = storage.get_storage(), type, ext, filepath; if (!options.uploadimage || !options.uploadimage.type || !options.uploadimage.path) { - return when.reject(new errors.NoPermissionError('Please select an image.')); + return Promise.reject(new errors.NoPermissionError('Please select an image.')); } type = options.uploadimage.type; ext = path.extname(options.uploadimage.name).toLowerCase(); filepath = options.uploadimage.path; - return when(isImage(type, ext)).then(function (result) { + return Promise.resolve(isImage(type, ext)).then(function (result) { if (!result) { - return when.reject(new errors.UnsupportedMediaTypeError('Please select a valid image.')); + return Promise.reject(new errors.UnsupportedMediaTypeError('Please select a valid image.')); } }).then(function () { return store.save(options.uploadimage); }).then(function (url) { - return when.resolve(url); + return url; }).finally(function () { // Remove uploaded file from tmp location - return nodefn.call(fs.unlink, filepath); + return Promise.promisify(fs.unlink)(filepath); }); } }; -module.exports = upload; \ No newline at end of file +module.exports = upload; diff --git a/core/server/api/users.js b/core/server/api/users.js index 9397dd8b85..23f6e5612a 100644 --- a/core/server/api/users.js +++ b/core/server/api/users.js @@ -1,6 +1,6 @@ // # Users API // RESTful API for the User resource -var when = require('when'), +var Promise = require('bluebird'), _ = require('lodash'), dataProvider = require('../models'), settings = require('./settings'), @@ -26,7 +26,7 @@ function prepareInclude(include) { sendInviteEmail = function sendInviteEmail(user) { var emailData; - return when.join( + return Promise.join( users.read({'id': user.created_by}), settings.read({'key': 'title'}), settings.read({context: {internal: true}, key: 'dbHash'}) @@ -114,7 +114,7 @@ users = { return { users: [result.toJSON()] }; } - return when.reject(new errors.NotFoundError('User not found.')); + return Promise.reject(new errors.NotFoundError('User not found.')); }); }, @@ -143,7 +143,7 @@ users = { return { users: [result.toJSON()]}; } - return when.reject(new errors.NotFoundError('User not found.')); + return Promise.reject(new errors.NotFoundError('User not found.')); }); }; @@ -160,7 +160,7 @@ users = { if (roleId !== contextRoleId && parseInt(options.id, 10) === parseInt(options.context.user, 10)) { - return when.reject(new errors.NoPermissionError('You cannot change your own role.')); + return Promise.reject(new errors.NoPermissionError('You cannot change your own role.')); } else if (roleId !== contextRoleId) { return dataProvider.User.findOne({role: 'Owner'}).then(function (result) { if (parseInt(result.id, 10) !== parseInt(options.id, 10)) { @@ -168,7 +168,7 @@ users = { return editOperation(); }); } else { - return when.reject(new errors.NoPermissionError('There has to be one owner.')); + return Promise.reject(new errors.NoPermissionError('There has to be one owner.')); } }); } @@ -208,7 +208,7 @@ users = { newUser.password = globalUtils.uid(50); newUser.status = 'invited'; } else { - return when.reject(new errors.BadRequestError('No email provided.')); + return Promise.reject(new errors.BadRequestError('No email provided.')); } return dataProvider.User.getByEmail( @@ -221,7 +221,7 @@ users = { if (foundUser.get('status') === 'invited' || foundUser.get('status') === 'invited-pending') { return foundUser; } else { - return when.reject(new errors.BadRequestError('User is already registered.')); + return Promise.reject(new errors.BadRequestError('User is already registered.')); } } }).then(function (invitedUser) { @@ -237,7 +237,7 @@ users = { }); } }).then(function () { - return when.resolve({users: [user]}); + return Promise.resolve({users: [user]}); }).catch(function (error) { if (error && error.type === 'EmailError') { error.message = 'Error sending email: ' + error.message + ' Please check your email settings and resend the invitation.'; @@ -250,7 +250,7 @@ users = { }); }); } - return when.reject(error); + return Promise.reject(error); }); }; @@ -262,7 +262,7 @@ users = { // Make sure user is allowed to add a user with this role return dataProvider.Role.findOne({id: roleId}).then(function (role) { if (role.get('name') === 'Owner') { - return when.reject(new errors.NoPermissionError('Not allowed to create an owner user.')); + return Promise.reject(new errors.NoPermissionError('Not allowed to create an owner user.')); } return canThis(options.context).assign.role(role); @@ -300,7 +300,7 @@ users = { }).then(function () { return result; }, function (error) { - return when.reject(new errors.InternalServerError(error)); + return Promise.reject(new errors.InternalServerError(error)); }); }, function (error) { return errors.handleAPIError(error); @@ -327,9 +327,9 @@ users = { ne2Password = checkedPasswordReset.password[0].ne2Password; return dataProvider.User.changePassword(oldPassword, newPassword, ne2Password, options).then(function () { - return when.resolve({password: [{message: 'Password changed successfully.'}]}); + return Promise.resolve({password: [{message: 'Password changed successfully.'}]}); }).catch(function (error) { - return when.reject(new errors.ValidationError(error.message)); + return Promise.reject(new errors.ValidationError(error.message)); }); }); }, @@ -343,9 +343,9 @@ users = { }).then(function () { return utils.checkObject(object, 'owner').then(function (checkedOwnerTransfer) { return dataProvider.User.transferOwnership(checkedOwnerTransfer.owner[0], options).then(function (updatedUsers) { - return when.resolve({ users: updatedUsers }); + return Promise.resolve({ users: updatedUsers }); }).catch(function (error) { - return when.reject(new errors.ValidationError(error.message)); + return Promise.reject(new errors.ValidationError(error.message)); }); }); }).catch(function (error) { diff --git a/core/server/api/utils.js b/core/server/api/utils.js index 948adc060b..cfc77bc75d 100644 --- a/core/server/api/utils.js +++ b/core/server/api/utils.js @@ -1,6 +1,6 @@ // # API Utils // Shared helpers for working with the API -var when = require('when'), +var Promise = require('bluebird'), _ = require('lodash'), errors = require('../errors'), utils; @@ -27,8 +27,8 @@ utils = { delete object.posts[0].author; } } - return when(object); + return Promise.resolve(object); } }; -module.exports = utils; \ No newline at end of file +module.exports = utils; diff --git a/core/server/apps/dependencies.js b/core/server/apps/dependencies.js index 843fade7c0..8a255309f6 100644 --- a/core/server/apps/dependencies.js +++ b/core/server/apps/dependencies.js @@ -2,7 +2,7 @@ var _ = require('lodash'), fs = require('fs'), path = require('path'), - when = require('when'), + Promise = require('bluebird'), spawn = require('child_process').spawn, win32 = process.platform === 'win32'; @@ -11,32 +11,33 @@ function AppDependencies(appPath) { } AppDependencies.prototype.install = function installAppDependencies() { - var def = when.defer(), - spawnOpts; + var spawnOpts, + self = this; - fs.exists(path.join(this.appPath, 'package.json'), function (exists) { - if (!exists) { - // Nothing to do, resolve right away? - def.resolve(); - } else { - // Run npm install in the app directory - spawnOpts = { - cwd: this.appPath - }; + return new Promise(function (resolve, reject) { + fs.exists(path.join(self.appPath, 'package.json'), function (exists) { + if (!exists) { + // Nothing to do, resolve right away? + resolve(); + } + else { + // Run npm install in the app directory + spawnOpts = { + cwd: self.appPath + }; - this.spawnCommand('npm', ['install', '--production'], spawnOpts) - .on('error', def.reject) - .on('exit', function (err) { - if (err) { - def.reject(err); - } + self.spawnCommand('npm', ['install', '--production'], spawnOpts) + .on('error', reject) + .on('exit', function (err) { + if (err) { + reject(err); + } - def.resolve(); - }); - } - }.bind(this)); - - return def.promise; + resolve(); + }); + } + }); + }); }; // Normalize a command across OS and spawn it; taken from yeoman/generator @@ -49,4 +50,4 @@ AppDependencies.prototype.spawnCommand = function (command, args, opt) { return spawn(winCommand, winArgs, _.defaults({ stdio: 'inherit' }, opt)); }; -module.exports = AppDependencies; \ No newline at end of file +module.exports = AppDependencies; diff --git a/core/server/apps/index.js b/core/server/apps/index.js index ab59e686e7..77d4cf6ba9 100644 --- a/core/server/apps/index.js +++ b/core/server/apps/index.js @@ -1,13 +1,12 @@ var _ = require('lodash'), - when = require('when'), + Promise = require('bluebird'), errors = require('../errors'), api = require('../api'), loader = require('./loader'), // Holds the available apps availableApps = {}; - function getInstalledApps() { return api.settings.read({context: {internal: true}, key: 'installedApps'}).then(function (response) { var installed = response.settings[0]; @@ -17,7 +16,7 @@ function getInstalledApps() { try { installed = JSON.parse(installed.value); } catch (e) { - return when.reject(e); + return Promise.reject(e); } return installed; @@ -49,7 +48,8 @@ module.exports = { 'Your apps will not be loaded.', 'Check your settings table for typos in the activeApps value. It should look like: ["app-1", "app2"] (double quotes required).' ); - return when.resolve(); + + return Promise.resolve(); } // Grab all installed apps, install any not already installed that are in appsToLoad. @@ -59,7 +59,7 @@ module.exports = { // After loading the app, add it to our hash of loaded apps loadedApps[name] = loadedApp; - return when.resolve(loadedApp); + return Promise.resolve(loadedApp); }, loadPromises = _.map(appsToLoad, function (app) { // If already installed, just activate the app @@ -77,13 +77,13 @@ module.exports = { }); }); - return when.all(loadPromises).then(function () { + return Promise.all(loadPromises).then(function () { // Save our installed apps to settings return saveInstalledApps(_.keys(loadedApps)); }).then(function () { // Extend the loadedApps onto the available apps _.extend(availableApps, loadedApps); - }).otherwise(function (err) { + }).catch(function (err) { errors.logError( err.message || err, 'The app will not be loaded', @@ -93,4 +93,4 @@ module.exports = { }); }, availableApps: availableApps -}; \ No newline at end of file +}; diff --git a/core/server/apps/loader.js b/core/server/apps/loader.js index 60af307faa..ca6d9bcf20 100644 --- a/core/server/apps/loader.js +++ b/core/server/apps/loader.js @@ -1,7 +1,7 @@ var path = require('path'), _ = require('lodash'), - when = require('when'), + Promise = require('bluebird'), AppProxy = require('./proxy'), config = require('../config'), AppSandbox = require('./sandbox'), @@ -64,9 +64,9 @@ loader = { // Load app permissions var perms = new AppPermissions(appPath); - return perms.read().otherwise(function (err) { + return perms.read().catch(function (err) { // Provide a helpful error about which app - return when.reject(new Error("Error loading app named " + name + "; problem reading permissions: " + err.message)); + return Promise.reject(new Error("Error loading app named " + name + "; problem reading permissions: " + err.message)); }); }) .then(function (appPerms) { @@ -76,15 +76,13 @@ loader = { // Check for an install() method on the app. if (!_.isFunction(app.install)) { - return when.reject(new Error("Error loading app named " + name + "; no install() method defined.")); + return Promise.reject(new Error("Error loading app named " + name + "; no install() method defined.")); } // Run the app.install() method // Wrapping the install() with a when because it's possible // to not return a promise from it. - return when(app.install(appProxy)).then(function () { - return when.resolve(app); - }); + return Promise.resolve(app.install(appProxy)).return(app); }); }, @@ -99,16 +97,14 @@ loader = { // Check for an activate() method on the app. if (!_.isFunction(app.activate)) { - return when.reject(new Error("Error loading app named " + name + "; no activate() method defined.")); + return Promise.reject(new Error("Error loading app named " + name + "; no activate() method defined.")); } // Wrapping the activate() with a when because it's possible // to not return a promise from it. - return when(app.activate(appProxy)).then(function () { - return when.resolve(app); - }); + return Promise.resolve(app.activate(appProxy)).return(app); }); } }; -module.exports = loader; \ No newline at end of file +module.exports = loader; diff --git a/core/server/apps/permissions.js b/core/server/apps/permissions.js index 25202d6144..d287b98a8c 100644 --- a/core/server/apps/permissions.js +++ b/core/server/apps/permissions.js @@ -1,6 +1,6 @@ var fs = require('fs'), - when = require('when'), + Promise = require('bluebird'), path = require('path'), parsePackageJson = require('../require-tree').parsePackageJson; @@ -10,44 +10,35 @@ function AppPermissions(appPath) { } AppPermissions.prototype.read = function () { - var self = this, - def = when.defer(); + var self = this; - this.checkPackageContentsExists() - .then(function (exists) { + return this.checkPackageContentsExists().then(function (exists) { if (!exists) { // If no package.json, return default permissions - return def.resolve(AppPermissions.DefaultPermissions); + return Promise.resolve(AppPermissions.DefaultPermissions); } // Read and parse the package.json - self.getPackageContents() - .then(function (parsed) { + return self.getPackageContents().then(function (parsed) { // If no permissions in the package.json then return the default permissions. if (!(parsed.ghost && parsed.ghost.permissions)) { - return def.resolve(AppPermissions.DefaultPermissions); + return Promise.resolve(AppPermissions.DefaultPermissions); } // TODO: Validation on permissions object? - def.resolve(parsed.ghost.permissions); - }) - .otherwise(def.reject); - }) - .otherwise(def.reject); - - return def.promise; + return Promise.resolve(parsed.ghost.permissions); + }); + }); }; AppPermissions.prototype.checkPackageContentsExists = function () { // Mostly just broken out for stubbing in unit tests - var def = when.defer(); - - fs.exists(this.packagePath, function (exists) { - def.resolve(exists); + return new Promise(function (resolve) { + fs.exists(this.packagePath, function (exists) { + resolve(exists); + }); }); - - return def.promise; }; // Get the contents of the package.json in the appPath root @@ -60,7 +51,7 @@ AppPermissions.prototype.getPackageContents = function () { return parsePackageJson(this.packagePath, messages) .then(function (parsed) { if (!parsed) { - return when.reject(new Error(messages.errors[0].message)); + return Promise.reject(new Error(messages.errors[0].message)); } return parsed; @@ -72,4 +63,4 @@ AppPermissions.DefaultPermissions = { posts: ['browse', 'read'] }; -module.exports = AppPermissions; \ No newline at end of file +module.exports = AppPermissions; diff --git a/core/server/config/index.js b/core/server/config/index.js index a76c547260..ccb8fc4cb9 100644 --- a/core/server/config/index.js +++ b/core/server/config/index.js @@ -4,7 +4,7 @@ // All other files that need to reference config.js should use this file. var path = require('path'), - when = require('when'), + Promise = require('bluebird'), url = require('url'), _ = require('lodash'), knex = require('knex'), @@ -101,7 +101,7 @@ function initConfig(rawConfig) { // just the object appropriate for this NODE_ENV updateConfig(rawConfig); - return when.all([requireTree(ghostConfig.paths.themePath), requireTree(ghostConfig.paths.appPath)]).then(function (paths) { + return Promise.all([requireTree(ghostConfig.paths.themePath), requireTree(ghostConfig.paths.appPath)]).then(function (paths) { ghostConfig.paths.availableThemes = paths[0]; ghostConfig.paths.availableApps = paths[1]; return ghostConfig; diff --git a/core/server/config/theme.js b/core/server/config/theme.js index 23686ac396..89794414c6 100644 --- a/core/server/config/theme.js +++ b/core/server/config/theme.js @@ -1,7 +1,7 @@ // Holds all theme configuration information // that as mostly used by templates and handlebar helpers. -var when = require('when'), +var Promise = require('bluebird'), // Variables themeConfig = {}; @@ -18,7 +18,7 @@ function theme() { // tries to access the config() object before it is created. function update(settings, configUrl) { // TODO: Pass the context into this method instead of hard coding internal: true? - return when.all([ + return Promise.all([ settings.read('title'), settings.read('description'), settings.read('logo'), @@ -30,7 +30,6 @@ function update(settings, configUrl) { themeConfig.description = globals[1].settings[0].value; themeConfig.logo = globals[2].settings[0] ? globals[2].settings[0].value : ''; themeConfig.cover = globals[3].settings[0] ? globals[3].settings[0].value : ''; - return; }); } diff --git a/core/server/controllers/admin.js b/core/server/controllers/admin.js index 7f0fd2eaf9..134fc58e5a 100644 --- a/core/server/controllers/admin.js +++ b/core/server/controllers/admin.js @@ -1,5 +1,4 @@ var _ = require('lodash'), - when = require('when'), api = require('../api'), errors = require('../errors'), updateCheck = require('../update-check'), @@ -9,7 +8,7 @@ adminControllers = { // Route: index // Path: /ghost/ // Method: GET - 'index': function (req, res) { + index: function (req, res) { /*jslint unparam:true*/ function renderIndex() { @@ -20,7 +19,7 @@ adminControllers = { return updateCheck.showUpdateNotification(); }).then(function (updateVersion) { if (!updateVersion) { - return when.resolve(); + return; } var notification = { diff --git a/core/server/controllers/frontend.js b/core/server/controllers/frontend.js index f5a51cc96a..96025b67c7 100644 --- a/core/server/controllers/frontend.js +++ b/core/server/controllers/frontend.js @@ -8,8 +8,7 @@ var moment = require('moment'), RSS = require('rss'), _ = require('lodash'), url = require('url'), - when = require('when'), - + Promise = require('bluebird'), api = require('../api'), config = require('../config'), filters = require('../../server/filters'), @@ -84,9 +83,7 @@ function formatResponse(post) { function handleError(next) { return function (err) { - var e = new Error(err.message); - e.status = err.code; - return next(e); + return next(err); }; } @@ -152,7 +149,7 @@ frontendControllers = { res.render(view, formatPageResponse(posts, page)); }); }); - }).otherwise(handleError(next)); + }).catch(handleError(next)); }, 'tag': function (req, res, next) { // Parse the page number @@ -206,7 +203,7 @@ frontendControllers = { res.render(view, result); }); }); - }).otherwise(handleError(next)); + }).catch(handleError(next)); }, 'author': function (req, res, next) { @@ -263,7 +260,7 @@ frontendControllers = { res.render(view, result); }); }); - }).otherwise(handleError(next)); + }).catch(handleError(next)); }, 'single': function (req, res, next) { @@ -290,7 +287,7 @@ frontendControllers = { // If there are still no matches then return. if (staticPostPermalink.match(path) === false) { // Reject promise chain with type 'NotFound' - return when.reject(new errors.NotFoundError()); + return Promise.reject(new errors.NotFoundError()); } permalink = staticPostPermalink; @@ -324,7 +321,7 @@ frontendControllers = { return res.redirect(config.paths.subdir + '/ghost/editor/' + post.id + '/'); } else if (params.edit !== undefined) { // reject with type: 'NotFound' - return when.reject(new errors.NotFoundError()); + return Promise.reject(new errors.NotFoundError()); } setReqCtx(req, post); @@ -380,7 +377,7 @@ frontendControllers = { return render(); - }).otherwise(function (err) { + }).catch(function (err) { // If we've thrown an error message // of type: 'NotFound' then we found // no path match. @@ -422,13 +419,13 @@ frontendControllers = { return res.redirect(baseUrl); } - return when.settle([ + return Promise.all([ api.settings.read('title'), api.settings.read('description'), api.settings.read('permalinks') ]).then(function (result) { - var options = {}; + if (pageParam) { options.page = pageParam; } if (isTag()) { options.tag = slugParam; } if (isAuthor()) { options.author = slugParam; } @@ -436,16 +433,14 @@ frontendControllers = { options.include = 'author,tags,fields'; return api.posts.browse(options).then(function (page) { - - var title = result[0].value.settings[0].value, - description = result[1].value.settings[0].value, - permalinks = result[2].value.settings[0], + var title = result[0].settings[0].value, + description = result[1].settings[0].value, + permalinks = result[2].settings[0], majorMinor = /^(\d+\.)?(\d+)/, trimmedVersion = res.locals.version, siteUrl = config.urlFor('home', {secure: req.secure}, true), feedUrl = config.urlFor('rss', {secure: req.secure}, true), maxPage = page.meta.pagination.pages, - feedItems = [], feed; trimmedVersion = trimmedVersion ? trimmedVersion.match(majorMinor)[0] : '?'; @@ -482,8 +477,7 @@ frontendControllers = { filters.doFilter('prePostsRender', page.posts).then(function (posts) { posts.forEach(function (post) { - var deferred = when.defer(), - item = { + var item = { title: post.title, guid: post.uuid, url: config.urlFor('post', {post: post, permalinks: permalinks}, true), @@ -499,25 +493,23 @@ frontendControllers = { p1 = url.resolve(siteUrl, p1); return "src='" + p1 + "' "; }); + //set a href to absolute url content = content.replace(/href=["|'|\s]?([\w\/\?\$\.\+\-;%:@&=,_]+)["|'|\s]?/gi, function (match, p1) { /*jslint unparam:true*/ p1 = url.resolve(siteUrl, p1); return "href='" + p1 + "' "; }); + item.description = content; feed.item(item); - feedItems.push(deferred.promise); - deferred.resolve(); }); - }); - - when.all(feedItems).then(function () { + }).then(function () { res.set('Content-Type', 'text/xml; charset=UTF-8'); res.send(feed.xml()); }); }); - }).otherwise(handleError(next)); + }).catch(handleError(next)); } }; diff --git a/core/server/data/export/index.js b/core/server/data/export/index.js index a3b77e1318..09810ca97a 100644 --- a/core/server/data/export/index.js +++ b/core/server/data/export/index.js @@ -1,6 +1,5 @@ var _ = require('lodash'), - when = require('when'), - + Promise = require('bluebird'), versioning = require('../versioning'), config = require('../../config'), utils = require('../utils'), @@ -28,7 +27,7 @@ exportFileName = function () { }; exporter = function () { - return when.join(versioning.getDatabaseVersion(), utils.getTables()).then(function (results) { + return Promise.join(versioning.getDatabaseVersion(), utils.getTables()).then(function (results) { var version = results[0], tables = results[1], selectOps = _.map(tables, function (name) { @@ -37,7 +36,7 @@ exporter = function () { } }); - return when.all(selectOps).then(function (tableData) { + return Promise.all(selectOps).then(function (tableData) { var exportData = { meta: { exported_on: new Date().getTime(), @@ -52,7 +51,7 @@ exporter = function () { exportData.data[name] = tableData[i]; }); - return when.resolve(exportData); + return exportData; }).catch(function (err) { errors.logAndThrowError(err, 'Error exporting data', ''); }); diff --git a/core/server/data/fixtures/index.js b/core/server/data/fixtures/index.js index d7294c5544..8a9f7bbe10 100644 --- a/core/server/data/fixtures/index.js +++ b/core/server/data/fixtures/index.js @@ -5,8 +5,8 @@ // rather than abstracted into a migration system. The upgrade function checks that its changes are safe before // making them. -var when = require('when'), - sequence = require('when/sequence'), +var Promise = require('bluebird'), + sequence = require('../../utils/sequence'), _ = require('lodash'), errors = require('../../errors'), utils = require('../../utils'), @@ -101,7 +101,7 @@ populate = function () { }); }); - return when.all(ops).then(function () { + return Promise.all(ops).then(function () { return sequence(relations); }).then(function () { return permissions.populate(options); @@ -150,7 +150,7 @@ to003 = function () { }); ops.push(upgradeOp); - return when.all(ops).then(function () { + return Promise.all(ops).then(function () { return permissions.to003(options); }).then(function () { return convertAdminToOwner(); diff --git a/core/server/data/fixtures/permissions/index.js b/core/server/data/fixtures/permissions/index.js index ebc4edfa13..c1dbe1d99f 100644 --- a/core/server/data/fixtures/permissions/index.js +++ b/core/server/data/fixtures/permissions/index.js @@ -1,7 +1,7 @@ // # Permissions Fixtures // Sets up the permissions, and the default permissions_roles relationships -var when = require('when'), - sequence = require('when/sequence'), +var Promise = require('bluebird'), + sequence = require('../../../utils/sequence'), _ = require('lodash'), errors = require('../../../errors'), models = require('../../../models'), @@ -52,7 +52,7 @@ addAllRolesPermissions = function () { ops.push(addRolesPermissionsForRole(roleName)); }); - return when.all(ops); + return Promise.all(ops); }; @@ -100,7 +100,7 @@ to003 = function (options) { }); // Now we can perfom the normal populate - return when.all(ops).then(function () { + return Promise.all(ops).then(function () { return populate(options); }); }; diff --git a/core/server/data/import/000.js b/core/server/data/import/000.js index b80bdf5805..0054d25e67 100644 --- a/core/server/data/import/000.js +++ b/core/server/data/import/000.js @@ -1,7 +1,7 @@ -var when = require('when'), - _ = require('lodash'), - models = require('../../models'), - utils = require('./utils'), +var Promise = require('bluebird'), + _ = require('lodash'), + models = require('../../models'), + utils = require('./utils'), Importer000; @@ -23,17 +23,15 @@ Importer000.prototype.importData = function (data) { return this.canImport(data) .then(function (importerFunc) { return importerFunc(data); - }, function (reason) { - return when.reject(reason); }); }; Importer000.prototype.canImport = function (data) { if (data.meta && data.meta.version && this.importFrom[data.meta.version]) { - return when.resolve(this.importFrom[data.meta.version]); + return Promise.resolve(this.importFrom[data.meta.version]); } - return when.reject('Unsupported version of data: ' + data.meta.version); + return Promise.reject('Unsupported version of data: ' + data.meta.version); }; @@ -49,10 +47,10 @@ Importer000.prototype.loadUsers = function () { }); if (!users.owner) { - return when.reject('Unable to find an owner'); + return Promise.reject('Unable to find an owner'); } - return when.resolve(users); + return users; }); }; @@ -72,12 +70,12 @@ Importer000.prototype.doUserImport = function (t, tableData, users, errors) { // Import users, deduplicating with already present users userOps = utils.importUsers(tableData.users, users, t); - return when.settle(userOps).then(function (descriptors) { + return Promise.settle(userOps).then(function (descriptors) { descriptors.forEach(function (d) { - if (d.state === 'rejected') { - errors = errors.concat(d.reason); + if (d.isRejected()) { + errors = errors.concat(d.reason()); } else { - imported.push(d.value.toJSON()); + imported.push(d.value().toJSON()); } }); @@ -85,12 +83,12 @@ Importer000.prototype.doUserImport = function (t, tableData, users, errors) { if (errors.length > 0) { t.rollback(errors); } else { - return when.resolve(imported); + return imported; } }); } - return when.resolve({}); + return Promise.resolve({}); }; Importer000.prototype.doImport = function (data) { @@ -149,13 +147,12 @@ Importer000.prototype.doImport = function (data) { */ // Write changes to DB, if successful commit, otherwise rollback - // when.all() does not work as expected, when.settle() does. - when.settle(ops).then(function (descriptors) { + Promise.settle(ops).then(function (descriptors) { var errors = []; descriptors.forEach(function (d) { - if (d.state === 'rejected') { - errors = errors.concat(d.reason); + if (d.isRejected()) { + errors = errors.concat(d.reason()); } }); @@ -168,9 +165,7 @@ Importer000.prototype.doImport = function (data) { }); }).then(function () { //TODO: could return statistics of imported items - return when.resolve(); - }, function (error) { - return when.reject(error); + return Promise.resolve(); }); }); }; diff --git a/core/server/data/import/index.js b/core/server/data/import/index.js index a3be3734ed..a90a74b7df 100644 --- a/core/server/data/import/index.js +++ b/core/server/data/import/index.js @@ -1,4 +1,4 @@ -var when = require('when'), +var Promise = require('bluebird'), _ = require('lodash'), validation = require('../validation'), errors = require('../../errors'), @@ -43,7 +43,7 @@ handleErrors = function handleErrors(errorList) { var processedErrors = []; if (!_.isArray(errorList)) { - return when.reject(errorList); + return Promise.reject(errorList); } _.each(errorList, function (error) { @@ -57,7 +57,7 @@ handleErrors = function handleErrors(errorList) { } }); - return when.reject(processedErrors); + return Promise.reject(processedErrors); }; validate = function validate(data) { @@ -69,20 +69,18 @@ validate = function validate(data) { }); }); - return when.settle(validateOps).then(function (descriptors) { + return Promise.settle(validateOps).then(function (descriptors) { var errorList = []; _.each(descriptors, function (d) { - if (d.state === 'rejected') { - errorList = errorList.concat(d.reason); + if (d.isRejected()) { + errorList = errorList.concat(d.reason()); } }); if (!_.isEmpty(errorList)) { - return when.reject(errorList); + return Promise.reject(errorList); } - - return when.resolve(); }); }; @@ -97,7 +95,7 @@ module.exports = function (version, data) { } if (!importer) { - return when.reject('No importer found'); + return Promise.reject('No importer found'); } return importer.importData(data); diff --git a/core/server/data/import/utils.js b/core/server/data/import/utils.js index 7607115fd3..2abdbddc6d 100644 --- a/core/server/data/import/utils.js +++ b/core/server/data/import/utils.js @@ -1,4 +1,4 @@ -var when = require('when'), +var Promise = require('bluebird'), _ = require('lodash'), models = require('../../models'), errors = require('../../errors'), @@ -162,10 +162,11 @@ utils = { return models.Tag.add(tag, _.extend(internal, {transacting: transaction})) // add pass-through error handling so that bluebird doesn't think we've dropped it .catch(function (error) { - return when.reject({raw: error, model: 'tag', data: tag}); + return Promise.reject({raw: error, model: 'tag', data: tag}); }); } - return when.resolve(_tag); + + return _tag; })); }); }, @@ -180,7 +181,7 @@ utils = { ops.push(models.Post.add(post, _.extend(internal, {transacting: transaction, importing: true})) // add pass-through error handling so that bluebird doesn't think we've dropped it .catch(function (error) { - return when.reject({raw: error, model: 'post', data: post}); + return Promise.reject({raw: error, model: 'post', data: post}); })); }); }, @@ -207,7 +208,7 @@ utils = { ops.push(models.User.add(user, _.extend(internal, {transacting: transaction})) // add pass-through error handling so that bluebird doesn't think we've dropped it .catch(function (error) { - return when.reject({raw: error, model: 'user', data: user}); + return Promise.reject({raw: error, model: 'user', data: user}); })); }); @@ -233,7 +234,7 @@ utils = { ops.push(models.Settings.edit(tableData, _.extend(internal, {transacting: transaction})) // add pass-through error handling so that bluebird doesn't think we've dropped it .catch(function (error) { - return when.reject({raw: error, model: 'setting', data: tableData}); + return Promise.reject({raw: error, model: 'setting', data: tableData}); })); }, @@ -247,13 +248,14 @@ utils = { return models.App.add(app, _.extend(internal, {transacting: transaction})) // add pass-through error handling so that bluebird doesn't think we've dropped it .catch(function (error) { - return when.reject({raw: error, model: 'app', data: app}); + return Promise.reject({raw: error, model: 'app', data: app}); }); } - return when.resolve(_app); + + return _app; })); }); } }; -module.exports = utils; \ No newline at end of file +module.exports = utils; diff --git a/core/server/data/migration/index.js b/core/server/data/migration/index.js index c8f3f871b5..11bc2a4f79 100644 --- a/core/server/data/migration/index.js +++ b/core/server/data/migration/index.js @@ -1,11 +1,9 @@ var _ = require('lodash'), - when = require('when'), + Promise = require('bluebird'), + sequence = require('../../utils/sequence'), path = require('path'), fs = require('fs'), - nodefn = require('when/node'), errors = require('../../errors'), - sequence = require('when/sequence'), - commands = require('./commands'), versioning = require('../versioning'), models = require('../../models'), @@ -47,7 +45,7 @@ backupDatabase = function backupDatabase() { return dataExport.fileName().then(function (fileName) { fileName = path.resolve(config.paths.contentPath + '/data/' + fileName); - return nodefn.call(fs.writeFile, fileName, JSON.stringify(exportedData)).then(function () { + return Promise.promisify(fs.writeFile)(fileName, JSON.stringify(exportedData)).then(function () { logInfo('Database backup written to: ' + fileName); }); }); @@ -80,7 +78,7 @@ init = function (tablesOnly) { if (databaseVersion === defaultVersion) { // 1. The database exists and is up-to-date logInfo('Up to date at version ' + databaseVersion); - return when.resolve(); + return; } if (databaseVersion > defaultVersion) { @@ -155,7 +153,7 @@ migrateUp = function (fromVersion, toVersion) { }).then(function () { migrateOps = migrateOps.concat(commands.getDeleteCommands(oldTables, schemaTables)); migrateOps = migrateOps.concat(commands.getAddCommands(oldTables, schemaTables)); - return when.all( + return Promise.all( _.map(oldTables, function (table) { return utils.getIndexes(table).then(function (indexes) { modifyUniCommands = modifyUniCommands.concat(commands.modifyUniqueCommands(table, indexes)); @@ -163,7 +161,7 @@ migrateUp = function (fromVersion, toVersion) { }) ); }).then(function () { - return when.all( + return Promise.all( _.map(oldTables, function (table) { return utils.getColumns(table).then(function (columns) { migrateOps = migrateOps.concat(commands.addColumnCommands(table, columns)); @@ -177,9 +175,9 @@ migrateUp = function (fromVersion, toVersion) { // execute the commands in sequence if (!_.isEmpty(migrateOps)) { logInfo('Running migrations'); + return sequence(migrateOps); } - return; }).then(function () { return fixtures.update(fromVersion, toVersion); }).then(function () { @@ -192,4 +190,4 @@ module.exports = { reset: reset, migrateUp: migrateUp, migrateUpFreshDb: migrateUpFreshDb -}; \ No newline at end of file +}; diff --git a/core/server/data/utils/clients/mysql.js b/core/server/data/utils/clients/mysql.js index c882b0c2d7..4076bd3c7c 100644 --- a/core/server/data/utils/clients/mysql.js +++ b/core/server/data/utils/clients/mysql.js @@ -1,5 +1,4 @@ var _ = require('lodash'), - when = require('when'), config = require('../../../config/index'), //private @@ -43,9 +42,7 @@ checkPostTable = function checkPostTable() { return config.database.knex.raw('SHOW FIELDS FROM posts where Field ="html" OR Field = "markdown"').then(function (response) { return _.flatten(_.map(response[0], function (entry) { if (entry.Type.toLowerCase() !== 'mediumtext') { - return config.database.knex.raw('ALTER TABLE posts MODIFY ' + entry.Field + ' MEDIUMTEXT').then(function () { - return when.resolve(); - }); + return config.database.knex.raw('ALTER TABLE posts MODIFY ' + entry.Field + ' MEDIUMTEXT'); } })); }); @@ -56,4 +53,4 @@ module.exports = { getTables: getTables, getIndexes: getIndexes, getColumns: getColumns -}; \ No newline at end of file +}; diff --git a/core/server/data/utils/clients/sqlite3.js b/core/server/data/utils/clients/sqlite3.js index b0a12ff773..a24738ddcb 100644 --- a/core/server/data/utils/clients/sqlite3.js +++ b/core/server/data/utils/clients/sqlite3.js @@ -1,13 +1,13 @@ var _ = require('lodash'), config = require('../../../config/index'), - //private - doRaw, + //private + doRaw, - // public - getTables, - getIndexes, - getColumns; + // public + getTables, + getIndexes, + getColumns; doRaw = function doRaw(query, fn) { diff --git a/core/server/data/utils/index.js b/core/server/data/utils/index.js index 64c2a0d99f..48ae15871c 100644 --- a/core/server/data/utils/index.js +++ b/core/server/data/utils/index.js @@ -1,5 +1,5 @@ var _ = require('lodash'), - when = require('when'), + Promise = require('bluebird'), config = require('../../config'), schema = require('../schema').tables, clients = require('./clients'), @@ -87,7 +87,7 @@ function getTables() { return clients[client].getTables(); } - return when.reject('No support for database client ' + client); + return Promise.reject('No support for database client ' + client); } function getIndexes(table) { @@ -98,7 +98,7 @@ function getIndexes(table) { return clients[client].getIndexes(table); } - return when.reject('No support for database client ' + client); + return Promise.reject('No support for database client ' + client); } function getColumns(table) { @@ -109,7 +109,7 @@ function getColumns(table) { return clients[client].getColumns(table); } - return when.reject('No support for database client ' + client); + return Promise.reject('No support for database client ' + client); } function checkTables() { diff --git a/core/server/data/validation/index.js b/core/server/data/validation/index.js index e4ab57f9f8..90e72eb8b2 100644 --- a/core/server/data/validation/index.js +++ b/core/server/data/validation/index.js @@ -1,7 +1,7 @@ var schema = require('../schema').tables, _ = require('lodash'), validator = require('validator'), - when = require('when'), + Promise = require('bluebird'), errors = require('../../errors'), config = require('../../config'), requireTree = require('../../require-tree').readAll, @@ -73,10 +73,10 @@ validateSchema = function (tableName, model) { }); if (validationErrors.length !== 0) { - return when.reject(validationErrors); + return Promise.reject(validationErrors); } - return when.resolve(); + return Promise.resolve(); }; // Validation for settings @@ -92,10 +92,10 @@ validateSettings = function (defaultSettings, model) { } if (validationErrors.length !== 0) { - return when.reject(validationErrors); + return Promise.reject(validationErrors); } - return when.resolve(); + return Promise.resolve(); }; // A Promise that will resolve to an object with a property for each installed theme. @@ -107,15 +107,13 @@ validateActiveTheme = function (themeName) { // If Ghost is running and its availableThemes collection exists // give it priority. if (config.paths.availableThemes && Object.keys(config.paths.availableThemes).length > 0) { - availableThemes = when(config.paths.availableThemes); + availableThemes = Promise.resolve(config.paths.availableThemes); } return availableThemes.then(function (themes) { if (!themes.hasOwnProperty(themeName)) { - return when.reject(new errors.ValidationError(themeName + ' cannot be activated because it is not currently installed.', 'activeTheme')); + return Promise.reject(new errors.ValidationError(themeName + ' cannot be activated because it is not currently installed.', 'activeTheme')); } - - return when.resolve(); }); }; diff --git a/core/server/errors/index.js b/core/server/errors/index.js index 60ffa9ddf5..4f807c5cc5 100644 --- a/core/server/errors/index.js +++ b/core/server/errors/index.js @@ -3,7 +3,7 @@ var _ = require('lodash'), colors = require('colors'), config = require('../config'), path = require('path'), - when = require('when'), + Promise = require('bluebird'), hbs = require('express-hbs'), NotFoundError = require('./notfounderror'), BadRequestError = require('./badrequesterror'), @@ -47,7 +47,7 @@ errors = { // ## Reject Error // Used to pass through promise errors when we want to handle them at a later time rejectError: function (err) { - return when.reject(err); + return Promise.reject(err); }, logInfo: function (component, info) { diff --git a/core/server/filters.js b/core/server/filters.js index b4601603b7..ac91fc18d0 100644 --- a/core/server/filters.js +++ b/core/server/filters.js @@ -1,10 +1,8 @@ -var when = require('when'), +var Promise = require('bluebird'), + pipeline = require('./utils/pipeline'), _ = require('lodash'), - defaults; -when.pipeline = require('when/pipeline'); - // ## Default values /** * A hash of default values to use instead of 'magic' numbers/strings. @@ -64,7 +62,7 @@ Filters.prototype.doFilter = function (name, args, context) { // Bug out early if no callbacks by that name if (!callbacks) { - return when.resolve(args); + return Promise.resolve(args); } // For each priorityLevel @@ -75,7 +73,7 @@ Filters.prototype.doFilter = function (name, args, context) { // Bug out if no handlers on this priority if (!_.isArray(callbacks[priority])) { - return when.resolve(currentArgs); + return Promise.resolve(currentArgs); } callables = _.map(callbacks[priority], function (callback) { @@ -84,11 +82,11 @@ Filters.prototype.doFilter = function (name, args, context) { }; }); // Call each handler for this priority level, allowing for promises or values - return when.pipeline(callables, currentArgs); + return pipeline(callables, currentArgs); }); }); - return when.pipeline(priorityCallbacks, args); + return pipeline(priorityCallbacks, args); }; module.exports = new Filters(); diff --git a/core/server/helpers/index.js b/core/server/helpers/index.js index b7294b17e3..727efee2b2 100644 --- a/core/server/helpers/index.js +++ b/core/server/helpers/index.js @@ -2,8 +2,7 @@ var downsize = require('downsize'), hbs = require('express-hbs'), moment = require('moment'), _ = require('lodash'), - when = require('when'), - + Promise = require('bluebird'), api = require('../api'), config = require('../config'), errors = require('../errors'), @@ -147,15 +146,15 @@ coreHelpers.url = function (options) { } if (schema.isTag(this)) { - return when(config.urlFor('tag', {tag: this}, absolute)); + return Promise.resolve(config.urlFor('tag', {tag: this}, absolute)); } if (schema.isUser(this)) { - return when(config.urlFor('author', {author: this}, absolute)); + return Promise.resolve(config.urlFor('author', {author: this}, absolute)); } - return when(config.urlFor(this, absolute)); + return Promise.resolve(config.urlFor(this, absolute)); }; // ### Asset helper @@ -786,9 +785,9 @@ function registerAsyncHelper(hbs, name, fn) { hbs.registerAsyncHelper(name, function (options, cb) { // Wrap the function passed in with a when.resolve so it can // return either a promise or a value - when.resolve(fn.call(this, options)).then(function (result) { + Promise.resolve(fn.call(this, options)).then(function (result) { cb(result); - }).otherwise(function (err) { + }).catch(function (err) { errors.logAndThrowError(err, 'registerAsyncThemeHelper: ' + name); }); }); diff --git a/core/server/index.js b/core/server/index.js index 86832df547..c2da0d66b4 100644 --- a/core/server/index.js +++ b/core/server/index.js @@ -6,7 +6,7 @@ var crypto = require('crypto'), fs = require('fs'), uuid = require('node-uuid'), _ = require('lodash'), - when = require('when'), + Promise = require('bluebird'), api = require('./api'), config = require('./config'), @@ -24,13 +24,6 @@ var crypto = require('crypto'), // Variables dbHash; -// If we're in development mode, require "when/console/monitor" -// for help in seeing swallowed promise errors, and log any -// stderr messages from bluebird promises. -if (process.env.NODE_ENV === 'development') { - require('when/monitor/console'); -} - function doFirstRun() { var firstRunMessage = [ 'Welcome to Ghost.', @@ -80,30 +73,29 @@ function builtFilesExist() { helpers.scriptFiles.production : helpers.scriptFiles.development; function checkExist(fileName) { - var deferred = when.defer(), - errorMessage = "Javascript files have not been built.", + var errorMessage = "Javascript files have not been built.", errorHelp = "\nPlease read the getting started instructions at:" + "\nhttps://github.com/TryGhost/Ghost#getting-started-guide-for-developers"; - fs.exists(fileName, function (exists) { - if (exists) { - deferred.resolve(true); - } else { - var err = new Error(errorMessage); + return new Promise(function (resolve, reject) { + fs.exists(fileName, function (exists) { + if (exists) { + resolve(true); + } else { + var err = new Error(errorMessage); - err.help = errorHelp; - deferred.reject(err); - } + err.help = errorHelp; + reject(err); + } + }); }); - - return deferred.promise; } fileNames.forEach(function (fileName) { deferreds.push(checkExist(location + fileName)); }); - return when.all(deferreds); + return Promise.all(deferreds); } // This is run after every initialization is done, right before starting server. @@ -174,7 +166,7 @@ function init(server) { // into this method due to circular dependencies. return config.theme.update(api.settings, config.url); }).then(function () { - return when.join( + return Promise.join( // Check for or initialise a dbHash. initDbHashAndFirstRun(), // Initialize mail @@ -219,7 +211,6 @@ function init(server) { errors.logWarn(warn.message, warn.context, warn.help); }); - return new GhostServer(server); }); } diff --git a/core/server/mail.js b/core/server/mail.js index 0c76217ef5..26a9d9e28b 100644 --- a/core/server/mail.js +++ b/core/server/mail.js @@ -1,7 +1,6 @@ var cp = require('child_process'), _ = require('lodash'), - when = require('when'), - nodefn = require('when/node'), + Promise = require('bluebird'), nodemailer = require('nodemailer'), config = require('./config'); @@ -17,7 +16,7 @@ GhostMailer.prototype.init = function () { self.state = {}; if (config.mail && config.mail.transport) { this.createTransport(); - return when.resolve(); + return Promise.resolve(); } // Attempt to detect and fallback to `sendmail` @@ -26,11 +25,9 @@ GhostMailer.prototype.init = function () { path: binpath }); self.state.usingSendmail = true; - }, function () { + }).catch(function () { self.state.emailDisabled = true; self.transport = null; - }).ensure(function () { - return when.resolve(); }); }; @@ -40,13 +37,15 @@ GhostMailer.prototype.isWindows = function () { GhostMailer.prototype.detectSendmail = function () { if (this.isWindows()) { - return when.reject(); + return Promise.reject(); } - return when.promise(function (resolve, reject) { + + return new Promise(function (resolve, reject) { cp.exec('which sendmail', function (err, stdout) { if (err && !/bin\/sendmail/.test(stdout)) { return reject(); } + resolve(stdout.toString().replace(/(\n|\r|\r\n)$/, '')); }); }); @@ -63,7 +62,7 @@ GhostMailer.prototype.fromAddress = function () { if (!from) { // Extract the domain name from url set in config.js - domain = config.url.match(new RegExp("^https?://([^/:?#]+)(?:[/:?#]|$)", "i")); + domain = config.url.match(new RegExp('^https?://([^/:?#]+)(?:[/:?#]|$)', 'i')); domain = domain && domain[1]; // Default to ghost@[blog.url] @@ -84,12 +83,12 @@ GhostMailer.prototype.send = function (message) { to = message.to || false; if (!this.transport) { - return when.reject(new Error('Email Error: No e-mail transport configured.')); + return Promise.reject(new Error('Email Error: No e-mail transport configured.')); } if (!(message && message.subject && message.html && message.to)) { - return when.reject(new Error('Email Error: Incomplete message data.')); + return Promise.reject(new Error('Email Error: Incomplete message data.')); } - sendMail = nodefn.lift(self.transport.sendMail.bind(self.transport)); + sendMail = Promise.promisify(self.transport.sendMail.bind(self.transport)); message = _.extend(message, { from: self.fromAddress(), diff --git a/core/server/middleware/index.js b/core/server/middleware/index.js index ce0e110c3a..76af9b0693 100644 --- a/core/server/middleware/index.js +++ b/core/server/middleware/index.js @@ -130,7 +130,7 @@ function updateActiveTheme(req, res, next) { } } next(); - }).otherwise(function (err) { + }).catch(function (err) { // Trying to start up without the active theme present, setup a simple hbs instance // and render an error page straight away. expressServer.engine('hbs', hbs.express3()); @@ -147,7 +147,7 @@ function redirectToSetup(req, res, next) { return res.redirect(config.paths.subdir + '/ghost/setup/'); } next(); - }).otherwise(function (err) { + }).catch(function (err) { return next(new Error(err)); }); } diff --git a/core/server/models/base.js b/core/server/models/base.js index 2670252c17..2a4c796aaa 100644 --- a/core/server/models/base.js +++ b/core/server/models/base.js @@ -6,7 +6,7 @@ // accesses the models directly. All other parts of Ghost, including the blog frontend, admin UI, and apps are only // allowed to access data via the API. var bookshelf = require('bookshelf'), - when = require('when'), + Promise = require('bluebird'), moment = require('moment'), _ = require('lodash'), uuid = require('node-uuid'), @@ -55,7 +55,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ this.on('creating', this.creating, this); this.on('saving', function (model, attributes, options) { - return when(self.saving(model, attributes, options)).then(function () { + return Promise.resolve(self.saving(model, attributes, options)).then(function () { return self.validate(model, attributes, options); }); }); @@ -327,7 +327,7 @@ ghostBookshelf.Model = ghostBookshelf.Model.extend({ var trimSpace; if (!found) { - return when.resolve(slugToFind); + return slugToFind; } slugTryCount += 1; diff --git a/core/server/models/index.js b/core/server/models/index.js index 8242010e60..dcd1bf6006 100644 --- a/core/server/models/index.js +++ b/core/server/models/index.js @@ -1,5 +1,5 @@ -var _ = require('lodash'), - when = require('when'), +var _ = require('lodash'), + Promise = require('bluebird'), requireTree = require('../require-tree'), models; @@ -48,12 +48,12 @@ models = { var self = this; return self.Post.findAll().then(function (posts) { - return when.all(_.map(posts.toJSON(), function (post) { + return Promise.all(_.map(posts.toJSON(), function (post) { return self.Post.destroy({id: post.id}); })); }).then(function () { return self.Tag.findAll().then(function (tags) { - return when.all(_.map(tags.toJSON(), function (tag) { + return Promise.all(_.map(tags.toJSON(), function (tag) { return self.Tag.destroy({id: tag.id}); })); }); diff --git a/core/server/models/post.js b/core/server/models/post.js index 0e74c19bf2..03838f2270 100644 --- a/core/server/models/post.js +++ b/core/server/models/post.js @@ -1,7 +1,7 @@ // # Post Model var _ = require('lodash'), uuid = require('node-uuid'), - when = require('when'), + Promise = require('bluebird'), errors = require('../errors'), Showdown = require('showdown'), ghostgfm = require('../../shared/lib/showdown/extensions/ghostgfm'), @@ -123,7 +123,7 @@ Post = ghostBookshelf.Model.extend({ tagOps.push(post.tags().detach(null, _.omit(options, 'query'))); if (_.isEmpty(self.myTags)) { - return when.all(tagOps); + return Promise.all(tagOps); } return ghostBookshelf.collection('Tags').forge().query('whereIn', 'name', _.pluck(self.myTags, 'name')).fetch(options).then(function (existingTags) { @@ -157,7 +157,7 @@ Post = ghostBookshelf.Model.extend({ tagOps.push(post.tags().attach(tag.id, _.omit(options, 'query'))); }); - return when.all(tagOps); + return Promise.all(tagOps); }); }); }, @@ -341,7 +341,7 @@ Post = ghostBookshelf.Model.extend({ return false; } - return when.join(fetchTagQuery(), fetchAuthorQuery()) + return Promise.join(fetchTagQuery(), fetchAuthorQuery()) // Set the limit & offset for the query, fetching // with the opts (to specify any eager relations, etc.) @@ -536,16 +536,16 @@ Post = ghostBookshelf.Model.extend({ options = this.filterOptions(options, 'destroyByAuthor'); if (authorId) { return postCollection.query('where', 'author_id', '=', authorId).fetch(options).then(function (results) { - return when.map(results.models, function (post) { + return Promise.map(results.models, function (post) { return post.related('tags').detach(null, options).then(function () { return post.destroy(options); }); }); }, function (error) { - return when.reject(new errors.InternalServerError(error.message || error)); + return Promise.reject(new errors.InternalServerError(error.message || error)); }); } - return when.reject(new errors.NotFoundError('No user found')); + return Promise.reject(new errors.NotFoundError('No user found')); }, @@ -574,10 +574,10 @@ Post = ghostBookshelf.Model.extend({ } if (hasUserPermission && hasAppPermission) { - return when.resolve(); + return Promise.resolve(); } - return when.reject(); + return Promise.reject(); } }); diff --git a/core/server/models/role.js b/core/server/models/role.js index 0981ebbedd..d1b5a96335 100644 --- a/core/server/models/role.js +++ b/core/server/models/role.js @@ -1,7 +1,7 @@ var _ = require('lodash'), errors = require('../errors'), ghostBookshelf = require('./base'), - when = require('when'), + Promise = require('bluebird'), Role, Roles; @@ -73,9 +73,10 @@ Role = ghostBookshelf.Model.extend({ } if (hasUserPermission && hasAppPermission) { - return when.resolve(); + return Promise.resolve(); } - return when.reject(); + + return Promise.reject(); } }); diff --git a/core/server/models/settings.js b/core/server/models/settings.js index 4033140fb9..4555f9959d 100644 --- a/core/server/models/settings.js +++ b/core/server/models/settings.js @@ -3,7 +3,7 @@ var Settings, uuid = require('node-uuid'), _ = require('lodash'), errors = require('../errors'), - when = require('when'), + Promise = require('bluebird'), validation = require('../data/validation'), internal = {context: {internal: true}}, @@ -64,7 +64,7 @@ Settings = ghostBookshelf.Model.extend({ var themeName = setting.value || ''; if (setting.key !== 'activeTheme') { - return when.resolve(); + return; } return validation.validateActiveTheme(themeName); @@ -87,7 +87,7 @@ Settings = ghostBookshelf.Model.extend({ if (!_.isObject(options)) { options = { key: options }; } - return when(ghostBookshelf.Model.findOne.call(this, options)); + return Promise.resolve(ghostBookshelf.Model.findOne.call(this, options)); }, edit: function (data, options) { @@ -98,11 +98,11 @@ Settings = ghostBookshelf.Model.extend({ data = [data]; } - return when.map(data, function (item) { + return Promise.map(data, function (item) { // Accept an array of models as input if (item.toJSON) { item = item.toJSON(); } if (!(_.isString(item.key) && item.key.length > 0)) { - return when.reject(new errors.ValidationError('Value in [settings.key] cannot be blank.')); + return Promise.reject(new errors.ValidationError('Value in [settings.key] cannot be blank.')); } item = self.filterData(item); @@ -113,7 +113,7 @@ Settings = ghostBookshelf.Model.extend({ return setting.save({value: item.value}, options); } - return when.reject(new errors.NotFoundError('Unable to find setting to update: ' + item.key)); + return Promise.reject(new errors.NotFoundError('Unable to find setting to update: ' + item.key)); }, errors.logAndThrowError); }); @@ -121,7 +121,7 @@ Settings = ghostBookshelf.Model.extend({ populateDefault: function (key) { if (!getDefaultSettings()[key]) { - return when.reject(new errors.NotFoundError('Unable to find default setting: ' + key)); + return Promise.reject(new errors.NotFoundError('Unable to find default setting: ' + key)); } return this.findOne({ key: key }).then(function (foundSetting) { @@ -153,7 +153,7 @@ Settings = ghostBookshelf.Model.extend({ } }); - return when.all(insertOperations); + return Promise.all(insertOperations); }); } diff --git a/core/server/models/user.js b/core/server/models/user.js index c7aa37e0f6..8ca9652547 100644 --- a/core/server/models/user.js +++ b/core/server/models/user.js @@ -1,7 +1,6 @@ var _ = require('lodash'), - when = require('when'), + Promise = require('bluebird'), errors = require('../errors'), - nodefn = require('when/node'), bcrypt = require('bcryptjs'), ghostBookshelf = require('./base'), http = require('http'), @@ -9,6 +8,10 @@ var _ = require('lodash'), validator = require('validator'), validation = require('../data/validation'), + bcryptGenSalt = Promise.promisify(bcrypt.genSalt), + bcryptHash = Promise.promisify(bcrypt.hash), + bcryptCompare = Promise.promisify(bcrypt.compare), + tokenSecurity = {}, activeStates = ['active', 'warn-1', 'warn-2', 'warn-3', 'warn-4', 'locked'], invitedStates = ['invited', 'invited-pending'], @@ -21,16 +24,16 @@ function validatePasswordLength(password) { throw new Error('Your password must be at least 8 characters long.'); } } catch (error) { - return when.reject(error); + return Promise.reject(error); } - return when.resolve(); + return Promise.resolve(); } function generatePasswordHash(password) { // Generate a new salt - return nodefn.call(bcrypt.genSalt).then(function (salt) { + return bcryptGenSalt().then(function (salt) { // Hash the provided password with bcrypt - return nodefn.call(bcrypt.hash, password, salt); + return bcryptHash(password, salt); }); } @@ -239,7 +242,7 @@ User = ghostBookshelf.Model.extend({ return false; } - return when(fetchRoleQuery()) + return Promise.resolve(fetchRoleQuery()) .then(function () { if (roleInstance) { @@ -394,7 +397,7 @@ User = ghostBookshelf.Model.extend({ roleId = parseInt(data.roles[0].id || data.roles[0], 10); if (data.roles.length > 1) { - return when.reject( + return Promise.reject( new errors.ValidationError('Only one role per user is supported at the moment.') ); } @@ -407,7 +410,7 @@ User = ghostBookshelf.Model.extend({ return ghostBookshelf.model('Role').findOne({id: roleId}); }).then(function (roleToAssign) { if (roleToAssign && roleToAssign.get('name') === 'Owner') { - return when.reject( + return Promise.reject( new errors.ValidationError('This method does not support assigning the owner role') ); } else { @@ -447,7 +450,7 @@ User = ghostBookshelf.Model.extend({ // check for too many roles if (roles.length > 1) { - return when.reject(new errors.ValidationError('Only one role per user is supported at the moment.')); + return Promise.reject(new errors.ValidationError('Only one role per user is supported at the moment.')); } // remove roles from the object delete data.roles; @@ -552,7 +555,7 @@ User = ghostBookshelf.Model.extend({ if (action === 'destroy') { // Owner cannot be deleted EVER if (userModel.hasRole('Owner')) { - return when.reject(); + return Promise.reject(); } // Users with the role 'Editor' have complex permissions when the action === 'destroy' @@ -566,10 +569,10 @@ User = ghostBookshelf.Model.extend({ } if (hasUserPermission && hasAppPermission) { - return when.resolve(); + return Promise.resolve(); } - return when.reject(); + return Promise.reject(); }, setWarning: function (user, options) { @@ -588,7 +591,7 @@ User = ghostBookshelf.Model.extend({ user.set('status', 'warn-' + level); } } - return when(user.save(options)).then(function () { + return Promise.resolve(user.save(options)).then(function () { return 5 - level; }); }, @@ -599,19 +602,19 @@ User = ghostBookshelf.Model.extend({ s; return this.getByEmail(object.email).then(function (user) { if (!user) { - return when.reject(new errors.NotFoundError('There is no user with that email address.')); + return Promise.reject(new errors.NotFoundError('There is no user with that email address.')); } if (user.get('status') === 'invited' || user.get('status') === 'invited-pending' || user.get('status') === 'inactive' ) { - return when.reject(new Error('The user with that email address is inactive.')); + return Promise.reject(new Error('The user with that email address is inactive.')); } if (user.get('status') !== 'locked') { - return nodefn.call(bcrypt.compare, object.password, user.get('password')).then(function (matched) { + return bcryptCompare(object.password, user.get('password')).then(function (matched) { if (!matched) { - return when(self.setWarning(user, {validate: false})).then(function (remaining) { + return Promise.resolve(self.setWarning(user, {validate: false})).then(function (remaining) { s = (remaining > 1) ? 's' : ''; - return when.reject(new errors.UnauthorizedError('Your password is incorrect.
' + + return Promise.reject(new errors.UnauthorizedError('Your password is incorrect.
' + remaining + ' attempt' + s + ' remaining!')); // Use comma structure, not .catch, because we don't want to catch incorrect passwords @@ -624,11 +627,11 @@ User = ghostBookshelf.Model.extend({ 'Error thrown from user update during login', 'Visit and save your profile after logging in to check for problems.' ); - return when.reject(new errors.UnauthorizedError('Your password is incorrect.')); + return Promise.reject(new errors.UnauthorizedError('Your password is incorrect.')); }); } - return when(user.set({status : 'active', last_login : new Date()}).save({validate: false})) + return Promise.resolve(user.set({status : 'active', last_login : new Date()}).save({validate: false})) .catch(function (error) { // If we get a validation or other error during this save, catch it and log it, but don't // cause a login error because of it. The user validation is not important here. @@ -641,16 +644,16 @@ User = ghostBookshelf.Model.extend({ }); }, errors.logAndThrowError); } - return when.reject(new errors.NoPermissionError('Your account is locked due to too many ' + + return Promise.reject(new errors.NoPermissionError('Your account is locked due to too many ' + 'login attempts. Please reset your password to log in again by clicking ' + 'the "Forgotten password?" link!')); }, function (error) { if (error.message === 'NotFound' || error.message === 'EmptyResponse') { - return when.reject(new errors.NotFoundError('There is no user with that email address.')); + return Promise.reject(new errors.NotFoundError('There is no user with that email address.')); } - return when.reject(error); + return Promise.reject(error); }); }, @@ -664,21 +667,21 @@ User = ghostBookshelf.Model.extend({ user = null; if (newPassword !== ne2Password) { - return when.reject(new Error('Your new passwords do not match')); + return Promise.reject(new Error('Your new passwords do not match')); } return validatePasswordLength(newPassword).then(function () { return self.forge({id: userid}).fetch({require: true}); }).then(function (_user) { user = _user; - return nodefn.call(bcrypt.compare, oldPassword, user.get('password')); + return bcryptCompare(oldPassword, user.get('password')); }).then(function (matched) { if (!matched) { - return when.reject(new Error('Your password is incorrect')); + return Promise.reject(new Error('Your password is incorrect')); } - return nodefn.call(bcrypt.genSalt); + return bcryptGenSalt(); }).then(function (salt) { - return nodefn.call(bcrypt.hash, newPassword, salt); + return bcryptHash(newPassword, salt); }).then(function (hash) { user.save({password: hash}); return user; @@ -688,7 +691,7 @@ User = ghostBookshelf.Model.extend({ generateResetToken: function (email, expires, dbHash) { return this.getByEmail(email).then(function (foundUser) { if (!foundUser) { - return when.reject(new errors.NotFoundError('There is no user with that email address.')); + return Promise.reject(new errors.NotFoundError('There is no user with that email address.')); } var hash = crypto.createHash('sha256'), @@ -720,25 +723,25 @@ User = ghostBookshelf.Model.extend({ // Check if invalid structure if (!parts || parts.length !== 3) { - return when.reject(new Error('Invalid token structure')); + return Promise.reject(new Error('Invalid token structure')); } expires = parseInt(parts[0], 10); email = parts[1]; if (isNaN(expires)) { - return when.reject(new Error('Invalid token expiration')); + return Promise.reject(new Error('Invalid token expiration')); } // Check if token is expired to prevent replay attacks if (expires < Date.now()) { - return when.reject(new Error('Expired token')); + return Promise.reject(new Error('Expired token')); } // to prevent brute force attempts to reset the password the combination of email+expires is only allowed for // 10 attempts if (tokenSecurity[email + '+' + expires] && tokenSecurity[email + '+' + expires].count >= 10) { - return when.reject(new Error('Token locked')); + return Promise.reject(new Error('Token locked')); } return this.generateResetToken(email, expires, dbHash).then(function (generatedToken) { @@ -756,14 +759,14 @@ User = ghostBookshelf.Model.extend({ } if (diff === 0) { - return when.resolve(email); + return email; } // increase the count for email+expires for each failed attempt tokenSecurity[email + '+' + expires] = { count: tokenSecurity[email + '+' + expires] ? tokenSecurity[email + '+' + expires].count + 1 : 1 }; - return when.reject(new Error('Invalid token')); + return Promise.reject(new Error('Invalid token')); }); }, @@ -771,7 +774,7 @@ User = ghostBookshelf.Model.extend({ var self = this; if (newPassword !== ne2Password) { - return when.reject(new Error('Your new passwords do not match')); + return Promise.reject(new Error('Your new passwords do not match')); } return validatePasswordLength(newPassword).then(function () { @@ -779,7 +782,7 @@ User = ghostBookshelf.Model.extend({ return self.validateToken(token, dbHash); }).then(function (email) { // Fetch the user by email, and hash the password at the same time. - return when.join( + return Promise.join( self.forge({email: email.toLocaleLowerCase()}).fetch({require: true}), generatePasswordHash(newPassword) ); @@ -809,7 +812,7 @@ User = ghostBookshelf.Model.extend({ // check if user has the owner role var currentRoles = ctxUser.toJSON().roles; if (!_.contains(currentRoles, ownerRole.id)) { - return when.reject(new errors.NoPermissionError('Only owners are able to transfer the owner role.')); + return Promise.reject(new errors.NoPermissionError('Only owners are able to transfer the owner role.')); } contextUser = ctxUser; return User.findOne({id: object.id}); @@ -817,7 +820,7 @@ User = ghostBookshelf.Model.extend({ var currentRoles = user.toJSON().roles; if (!_.contains(currentRoles, adminRole.id)) { - return when.reject(new errors.ValidationError('Only administrators can be assigned the owner role.')); + return Promise.reject(new errors.ValidationError('Only administrators can be assigned the owner role.')); } assignUser = user; @@ -838,20 +841,20 @@ User = ghostBookshelf.Model.extend({ gravatarLookup: function (userData) { var gravatarUrl = '//www.gravatar.com/avatar/' + crypto.createHash('md5').update(userData.email.toLowerCase().trim()).digest('hex') + - '?d=404&s=250', - checkPromise = when.defer(); + '?d=404&s=250'; - http.get('http:' + gravatarUrl, function (res) { - if (res.statusCode !== 404) { - userData.image = gravatarUrl; - } - checkPromise.resolve(userData); - }).on('error', function () { - //Error making request just continue. - checkPromise.resolve(userData); + return new Promise(function (resolve) { + http.get('http:' + gravatarUrl, function (res) { + if (res.statusCode !== 404) { + userData.image = gravatarUrl; + } + + resolve(userData); + }).on('error', function () { + //Error making request just continue. + resolve(userData); + }); }); - - return checkPromise.promise; }, // Get the user by email address, enforces case insensitivity rejects if the user is not found // When multi-user support is added, email addresses must be deduplicated with case insensitivity, so that @@ -869,7 +872,7 @@ User = ghostBookshelf.Model.extend({ return user.get('email').toLowerCase() === email.toLowerCase(); }); if (userWithEmail) { - return when.resolve(userWithEmail); + return userWithEmail; } }); } diff --git a/core/server/permissions/index.js b/core/server/permissions/index.js index 3cf8c3fe3d..0c7079365d 100644 --- a/core/server/permissions/index.js +++ b/core/server/permissions/index.js @@ -2,7 +2,7 @@ // canThis(someUser).edit.post(somePost|somePostId) var _ = require('lodash'), - when = require('when'), + Promise = require('bluebird'), Models = require('../models'), effectivePerms = require('./effective'), init, @@ -65,7 +65,7 @@ CanThisResult.prototype.buildObjectTypeHandlers = function (obj_types, act_type, // If it's an internal request, resolve immediately if (context.internal) { - return when.resolve(); + return Promise.resolve(); } if (_.isNumber(modelOrId) || _.isString(modelOrId)) { @@ -127,9 +127,10 @@ CanThisResult.prototype.buildObjectTypeHandlers = function (obj_types, act_type, } if (hasUserPermission && hasAppPermission) { - return when.resolve(); + return; } - return when.reject(); + + return Promise.reject(); }); }; @@ -155,7 +156,7 @@ CanThisResult.prototype.beginCheck = function (context) { userPermissionLoad = effectivePerms.user(context.user); } else { // Resolve null if no context.user to prevent db call - userPermissionLoad = when.resolve(null); + userPermissionLoad = Promise.resolve(null); } @@ -164,11 +165,11 @@ CanThisResult.prototype.beginCheck = function (context) { appPermissionLoad = effectivePerms.app(context.app); } else { // Resolve null if no context.app - appPermissionLoad = when.resolve(null); + appPermissionLoad = Promise.resolve(null); } // Wait for both user and app permissions to load - permissionsLoad = when.all([userPermissionLoad, appPermissionLoad]).then(function (result) { + permissionsLoad = Promise.all([userPermissionLoad, appPermissionLoad]).then(function (result) { return { user: result[0], app: result[1] @@ -232,7 +233,7 @@ init = refresh = function () { seenActions[action_type][object_type] = true; }); - return when(exported.actionsMap); + return exported.actionsMap; }); }; diff --git a/core/server/require-tree.js b/core/server/require-tree.js index c384a9a18a..956ae5c067 100644 --- a/core/server/require-tree.js +++ b/core/server/require-tree.js @@ -1,8 +1,10 @@ var _ = require('lodash'), fs = require('fs'), - keys = require('when/keys'), path = require('path'), - when = require('when'), + Promise = require('bluebird'), + readdirAsync = Promise.promisify(fs.readdir), + lstatAsync = Promise.promisify(fs.lstat), + parsePackageJson = function (path, messages) { // Default the messages if non were passed messages = messages || { @@ -10,42 +12,42 @@ var _ = require('lodash'), warns: [] }; - var packageDeferred = when.defer(), - packagePromise = packageDeferred.promise, - jsonContainer; + var jsonContainer; - fs.readFile(path, function (error, data) { - if (error) { - messages.errors.push({ - message: 'Could not read package.json file', - context: path - }); - packageDeferred.resolve(false); - return; - } - try { - jsonContainer = JSON.parse(data); - if (jsonContainer.hasOwnProperty('name') && jsonContainer.hasOwnProperty('version')) { - packageDeferred.resolve(jsonContainer); - } else { + return new Promise(function (resolve) { + fs.readFile(path, function (error, data) { + if (error) { messages.errors.push({ - message: '"name" or "version" is missing from theme package.json file.', + message: 'Could not read package.json file', + context: path + }); + resolve(false); + return; + } + try { + jsonContainer = JSON.parse(data); + if (jsonContainer.hasOwnProperty('name') && jsonContainer.hasOwnProperty('version')) { + resolve(jsonContainer); + } else { + messages.errors.push({ + message: '"name" or "version" is missing from theme package.json file.', + context: path, + help: 'This will be required in future. Please see http://docs.ghost.org/themes/' + }); + resolve(false); + } + } catch (e) { + messages.errors.push({ + message: 'Theme package.json file is malformed', context: path, help: 'This will be required in future. Please see http://docs.ghost.org/themes/' }); - packageDeferred.resolve(false); + resolve(false); } - } catch (e) { - messages.errors.push({ - message: 'Theme package.json file is malformed', - context: path, - help: 'This will be required in future. Please see http://docs.ghost.org/themes/' - }); - packageDeferred.resolve(false); - } + }); }); - return when(packagePromise); }, + readDir = function (dir, options, depth, messages) { depth = depth || 0; messages = messages || { @@ -58,44 +60,29 @@ var _ = require('lodash'), }, options); if (depth > 1) { - return null; + return Promise.resolve(null); } - var subtree = {}, - treeDeferred = when.defer(), - treePromise = treeDeferred.promise; - - fs.readdir(dir, function (error, files) { - if (error) { - return treeDeferred.reject(error); - } - + return readdirAsync(dir).then(function (files) { files = files || []; - files.forEach(function (file) { - var fileDeferred = when.defer(), - filePromise = fileDeferred.promise, - fpath = path.join(dir, file); - subtree[file] = filePromise; - fs.lstat(fpath, function (error, result) { - /*jslint unparam:true*/ + return Promise.reduce(files, function (results, file) { + var fpath = path.join(dir, file); + + return lstatAsync(fpath).then(function (result) { if (result.isDirectory()) { - fileDeferred.resolve(readDir(fpath, options, depth + 1, messages)); + return readDir(fpath, options, depth + 1, messages); } else if (depth === 1 && file === 'package.json') { - fileDeferred.resolve(parsePackageJson(fpath, messages)); + return parsePackageJson(fpath, messages); } else { - fileDeferred.resolve(fpath); + return fpath; } + }).then(function (result) { + results[file] = result; + + return results; }); - }); - - return keys.all(subtree).then(function (theFiles) { - return treeDeferred.resolve(theFiles); - }); - }); - - return when(treePromise).then(function (prom) { - return prom; + }, {}); }); }, readAll = function (dir, options, depth) { @@ -105,7 +92,7 @@ var _ = require('lodash'), warns: [] }; - return when(readDir(dir, options, depth, messages)).then(function (paths) { + return readDir(dir, options, depth, messages).then(function (paths) { // for all contents of the dir, I'm interested in the ones that are directories and within /theme/ if (typeof paths === 'object' && dir.indexOf('theme') !== -1) { _.each(paths, function (path, index) { @@ -118,9 +105,11 @@ var _ = require('lodash'), } }); } + paths._messages = messages; + return paths; - }).otherwise(function () { + }).catch(function () { return {'_messages': messages}; }); }; @@ -129,4 +118,4 @@ module.exports = { readAll: readAll, readDir: readDir, parsePackageJson: parsePackageJson -}; \ No newline at end of file +}; diff --git a/core/server/storage/base.js b/core/server/storage/base.js index d7cf63b625..1f224ca31a 100644 --- a/core/server/storage/base.js +++ b/core/server/storage/base.js @@ -1,6 +1,6 @@ var moment = require('moment'), path = require('path'), - when = require('when'), + Promise = require('bluebird'), baseStore; // TODO: would probably be better to put these on the prototype and have proper constructors etc @@ -34,18 +34,18 @@ baseStore = { self.generateUnique(store, dir, name, ext, i, done); }); } else { - done.resolve(filename); + done(filename); } }); }, 'getUniqueFileName': function (store, image, targetDir) { - var done = when.defer(), - ext = path.extname(image.name), - name = path.basename(image.name, ext).replace(/[\W]/gi, '-'); + var ext = path.extname(image.name), + name = path.basename(image.name, ext).replace(/[\W]/gi, '-'), + self = this; - this.generateUnique(store, targetDir, name, ext, 0, done); - - return done.promise; + return new Promise(function (resolve) { + self.generateUnique(store, targetDir, name, ext, 0, resolve); + }); } }; diff --git a/core/server/storage/localfilesystem.js b/core/server/storage/localfilesystem.js index 0abd1228f4..300bf0d75b 100644 --- a/core/server/storage/localfilesystem.js +++ b/core/server/storage/localfilesystem.js @@ -4,9 +4,8 @@ var _ = require('lodash'), express = require('express'), fs = require('fs-extra'), - nodefn = require('when/node'), path = require('path'), - when = require('when'), + Promise = require('bluebird'), errors = require('../errors'), config = require('../config'), utils = require('../utils'), @@ -20,37 +19,31 @@ localFileStore = _.extend(baseStore, { // - image is the express image object // - returns a promise which ultimately returns the full url to the uploaded image 'save': function (image) { - var saved = when.defer(), - targetDir = this.getTargetDir(config.paths.imagesPath), + var targetDir = this.getTargetDir(config.paths.imagesPath), targetFilename; - this.getUniqueFileName(this, image, targetDir).then(function (filename) { + return this.getUniqueFileName(this, image, targetDir).then(function (filename) { targetFilename = filename; - return nodefn.call(fs.mkdirs, targetDir); + return Promise.promisify(fs.mkdirs)(targetDir); }).then(function () { - return nodefn.call(fs.copy, image.path, targetFilename); + return Promise.promisify(fs.copy)(image.path, targetFilename); }).then(function () { // The src for the image must be in URI format, not a file system path, which in Windows uses \ // For local file system storage can use relative path so add a slash var fullUrl = (config.paths.subdir + '/' + config.paths.imagesRelPath + '/' + path.relative(config.paths.imagesPath, targetFilename)).replace(new RegExp('\\' + path.sep, 'g'), '/'); - return saved.resolve(fullUrl); - }).otherwise(function (e) { + return fullUrl; + }).catch(function (e) { errors.logError(e); - return saved.reject(e); + return Promise.reject(e); }); - - return saved.promise; }, 'exists': function (filename) { - // fs.exists does not play nicely with nodefn because the callback doesn't have an error argument - var done = when.defer(); - - fs.exists(filename, function (exists) { - done.resolve(exists); + return new Promise(function (resolve) { + fs.exists(filename, function (exists) { + resolve(exists); + }); }); - - return done.promise; }, // middleware for serving the files diff --git a/core/server/update-check.js b/core/server/update-check.js index d132698afa..1ac8605444 100644 --- a/core/server/update-check.js +++ b/core/server/update-check.js @@ -23,8 +23,7 @@ var crypto = require('crypto'), https = require('https'), moment = require('moment'), semver = require('semver'), - when = require('when'), - nodefn = require('when/node'), + Promise = require('bluebird'), _ = require('lodash'), url = require('url'), @@ -51,8 +50,8 @@ function updateCheckData() { ops = [], mailConfig = config.mail; - ops.push(api.settings.read(_.extend(internal, {key: 'dbHash'})).otherwise(errors.rejectError)); - ops.push(api.settings.read(_.extend(internal, {key: 'activeTheme'})).otherwise(errors.rejectError)); + ops.push(api.settings.read(_.extend(internal, {key: 'dbHash'})).catch(errors.rejectError)); + ops.push(api.settings.read(_.extend(internal, {key: 'activeTheme'})).catch(errors.rejectError)); ops.push(api.settings.read(_.extend(internal, {key: 'activeApps'})) .then(function (response) { var apps = response.settings[0]; @@ -63,10 +62,10 @@ function updateCheckData() { } return _.reduce(apps, function (memo, item) { return memo === '' ? memo + item : memo + ', ' + item; }, ''); - }).otherwise(errors.rejectError)); - ops.push(api.posts.browse().otherwise(errors.rejectError)); - ops.push(api.users.browse(internal).otherwise(errors.rejectError)); - ops.push(nodefn.call(exec, 'npm -v').otherwise(errors.rejectError)); + }).catch(errors.rejectError)); + ops.push(api.posts.browse().catch(errors.rejectError)); + ops.push(api.users.browse(internal).catch(errors.rejectError)); + ops.push(Promise.promisify(exec)('npm -v').catch(errors.rejectError)); data.ghost_version = currentVersion; data.node_version = process.versions.node; @@ -74,13 +73,13 @@ function updateCheckData() { data.database_type = config.database.client; data.email_transport = mailConfig && (mailConfig.options && mailConfig.options.service ? mailConfig.options.service : mailConfig.transport); - return when.settle(ops).then(function (descriptors) { - var hash = descriptors[0].value.settings[0], - theme = descriptors[1].value.settings[0], - apps = descriptors[2].value, - posts = descriptors[3].value, - users = descriptors[4].value, - npm = descriptors[5].value, + return Promise.settle(ops).then(function (descriptors) { + var hash = descriptors[0].value().settings[0], + theme = descriptors[1].value().settings[0], + apps = descriptors[2].value(), + posts = descriptors[3].value(), + users = descriptors[4].value(), + npm = descriptors[5].value(), blogUrl = url.parse(config.url), blogId = blogUrl.hostname + blogUrl.pathname.replace(/\//, '') + hash.value; @@ -93,13 +92,12 @@ function updateCheckData() { data.npm_version = _.isArray(npm) && npm[0] ? npm[0].toString().replace(/\n/, '') : ''; return data; - }).otherwise(updateCheckError); + }).catch(updateCheckError); } function updateCheckRequest() { return updateCheckData().then(function (reqData) { - var deferred = when.defer(), - resData = '', + var resData = '', headers, req; @@ -109,31 +107,31 @@ function updateCheckRequest() { 'Content-Length': reqData.length }; - req = https.request({ - hostname: checkEndpoint, - method: 'POST', - headers: headers - }, function (res) { - res.on('error', function (error) { deferred.reject(error); }); - res.on('data', function (chunk) { resData += chunk; }); - res.on('end', function () { - try { - resData = JSON.parse(resData); - deferred.resolve(resData); - } catch (e) { - deferred.reject('Unable to decode update response'); - } + return new Promise(function (resolve, reject) { + req = https.request({ + hostname: checkEndpoint, + method: 'POST', + headers: headers + }, function (res) { + res.on('error', function (error) { reject(error); }); + res.on('data', function (chunk) { resData += chunk; }); + res.on('end', function () { + try { + resData = JSON.parse(resData); + resolve(resData); + } catch (e) { + reject('Unable to decode update response'); + } + }); + }); + + req.write(reqData); + req.end(); + + req.on('error', function (error) { + reject(error); }); }); - - req.write(reqData); - req.end(); - - req.on('error', function (error) { - deferred.reject(error); - }); - - return deferred.promise; }); } @@ -149,53 +147,45 @@ function updateCheckResponse(response) { api.settings.edit( {settings: [{key: 'nextUpdateCheck', value: response.next_check}]}, internal - ) - .otherwise(errors.rejectError), + ).catch(errors.rejectError), api.settings.edit( {settings: [{key: 'displayUpdateNotification', value: response.version}]}, internal - ) - .otherwise(errors.rejectError) + ).catch(errors.rejectError) ); - return when.settle(ops).then(function (descriptors) { + return Promise.settle(ops).then(function (descriptors) { descriptors.forEach(function (d) { - if (d.state === 'rejected') { - errors.rejectError(d.reason); + if (d.isRejected()) { + errors.rejectError(d.reason()); } }); - return when.resolve(); }); } function updateCheck() { - var deferred = when.defer(); - // The check will not happen if: // 1. updateCheck is defined as false in config.js // 2. we've already done a check this session // 3. we're not in production or development mode if (config.updateCheck === false || _.indexOf(allowedCheckEnvironments, process.env.NODE_ENV) === -1) { // No update check - deferred.resolve(); + return Promise.resolve(); } else { - api.settings.read(_.extend(internal, {key: 'nextUpdateCheck'})).then(function (result) { + return api.settings.read(_.extend(internal, {key: 'nextUpdateCheck'})).then(function (result) { var nextUpdateCheck = result.settings[0]; if (nextUpdateCheck && nextUpdateCheck.value && nextUpdateCheck.value > moment().unix()) { // It's not time to check yet - deferred.resolve(); + return; } else { // We need to do a check return updateCheckRequest() .then(updateCheckResponse) - .otherwise(updateCheckError); + .catch(updateCheckError); } - }).otherwise(updateCheckError) - .then(deferred.resolve); + }).catch(updateCheckError); } - - return deferred.promise; } function showUpdateNotification() { @@ -210,9 +200,10 @@ function showUpdateNotification() { } if (display && display.value && currentVersion && semver.gt(display.value, currentVersion)) { - return when(display.value); + return display.value; } - return when(false); + + return false; }); } diff --git a/core/server/utils/pipeline.js b/core/server/utils/pipeline.js new file mode 100644 index 0000000000..513a4ea540 --- /dev/null +++ b/core/server/utils/pipeline.js @@ -0,0 +1,21 @@ +var Promise = require('bluebird'); + +function pipeline(tasks /* initial arguments */) { + var args = Array.prototype.slice.call(arguments, 1), + + runTask = function (task, args) { + runTask = function (task, arg) { + return task(arg); + }; + + return task.apply(null, args); + }; + + return Promise.all(tasks).reduce(function (arg, task) { + return Promise.resolve(runTask(task, arg)).then(function (result) { + return result; + }); + }, args); +} + +module.exports = pipeline; diff --git a/core/server/utils/sequence.js b/core/server/utils/sequence.js new file mode 100644 index 0000000000..6040b5dda2 --- /dev/null +++ b/core/server/utils/sequence.js @@ -0,0 +1,13 @@ +var Promise = require('bluebird'); + +function sequence(tasks) { + return Promise.reduce(tasks, function (results, task) { + return task().then(function (result) { + results.push(result); + + return results; + }); + }, []); +} + +module.exports = sequence; diff --git a/core/test/functional/routes/admin_test.js b/core/test/functional/routes/admin_test.js index 17ccde3bfb..b5a5d6345c 100644 --- a/core/test/functional/routes/admin_test.js +++ b/core/test/functional/routes/admin_test.js @@ -62,7 +62,7 @@ describe('Admin Routing', function () { }).then(function () { done(); }).catch(done); - }).otherwise(function (e) { + }).catch(function (e) { console.log('Ghost Error: ', e); console.log(e.stack); }); @@ -332,7 +332,7 @@ describe('Admin Routing', function () { // }); // }).catch(done); -// }).otherwise(function (e) { +// }).catch(function (e) { // console.log('Ghost Error: ', e); // console.log(e.stack); // }); diff --git a/core/test/functional/routes/api/users_test.js b/core/test/functional/routes/api/users_test.js index 6dbf9afb0f..7760465eff 100644 --- a/core/test/functional/routes/api/users_test.js +++ b/core/test/functional/routes/api/users_test.js @@ -334,7 +334,8 @@ describe('User API', function () { var jsonResponse = res.body, changedValue = 'joe-bloggs.ghost.org'; - jsonResponse.users[0].should.exist; + + should.exist(jsonResponse.users[0]); jsonResponse.users[0].website = changedValue; request.put(testUtils.API.getApiQuery('users/me/')) diff --git a/core/test/integration/api/api_authentication_spec.js b/core/test/integration/api/api_authentication_spec.js index 5d67a50987..9959a3d648 100644 --- a/core/test/integration/api/api_authentication_spec.js +++ b/core/test/integration/api/api_authentication_spec.js @@ -2,7 +2,7 @@ /*jshint expr:true*/ var testUtils = require('../../utils'), should = require('should'), - when = require('when'), + Promise = require('bluebird'), rewire = require('rewire'), // Stuff we are testing @@ -43,7 +43,7 @@ describe('Authentication API', function () { send = mail.__get__('mail.send'); mail.__set__('mail.send', function () { - return when.resolve(); + return Promise.resolve(); }); AuthAPI.setup({ setup: [setupData] }).then(function (result) { diff --git a/core/test/integration/api/api_themes_spec.js b/core/test/integration/api/api_themes_spec.js index 94d0435057..af8ac459b1 100644 --- a/core/test/integration/api/api_themes_spec.js +++ b/core/test/integration/api/api_themes_spec.js @@ -5,7 +5,7 @@ var _ = require('lodash'), rewire = require('rewire'), should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), // Stuff we are testing SettingsAPI = require('../../../server/api/settings'), @@ -30,11 +30,11 @@ describe('Themes API', function () { beforeEach(function () { // Override settings.read for activeTheme sandbox.stub(SettingsAPI, 'read', function () { - return when({ settings: [{value: 'casper'}] }); + return Promise.resolve({ settings: [{value: 'casper'}] }); }); sandbox.stub(SettingsAPI, 'edit', function () { - return when({ settings: [{value: 'rasper'}] }); + return Promise.resolve({ settings: [{value: 'rasper'}] }); }); configStub = { diff --git a/core/test/integration/api/api_upload_spec.js b/core/test/integration/api/api_upload_spec.js index 43986a735e..970564ec93 100644 --- a/core/test/integration/api/api_upload_spec.js +++ b/core/test/integration/api/api_upload_spec.js @@ -3,7 +3,7 @@ var fs = require('fs-extra'), should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), storage = require('../../../server/storage'), // Stuff we are testing @@ -23,9 +23,9 @@ describe('Upload API', function () { beforeEach(function () { store = sinon.stub(); - store.save = sinon.stub().returns(when('URL')); - store.exists = sinon.stub().returns(when(true)); - store.destroy = sinon.stub().returns(when()); + store.save = sinon.stub().returns(Promise.resolve('URL')); + store.exists = sinon.stub().returns(Promise.resolve(true)); + store.destroy = sinon.stub().returns(Promise.resolve()); sinon.stub(storage, 'get_storage').returns(store); sinon.stub(fs, 'unlink').yields(); }); diff --git a/core/test/integration/api/api_users_spec.js b/core/test/integration/api/api_users_spec.js index c0ab6ef43f..df51b5d60a 100644 --- a/core/test/integration/api/api_users_spec.js +++ b/core/test/integration/api/api_users_spec.js @@ -3,7 +3,7 @@ var testUtils = require('../../utils'), should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), _ = require('lodash'), // Stuff we are testing @@ -340,11 +340,11 @@ describe('Users API', function () { newUser = _.clone(testUtils.DataGenerator.forKnex.createUser(testUtils.DataGenerator.Content.users[4])); sandbox.stub(ModelUser.User, 'gravatarLookup', function (userData) { - return when.resolve(userData); + return Promise.resolve(userData); }); sandbox.stub(mail, 'send', function () { - return when.resolve(); + return Promise.resolve(); }); }); afterEach(function () { @@ -933,11 +933,11 @@ describe('Users API', function () { {name: newName, roles: [roleIdFor.admin]} ]}, _.extend({}, context.editor, {id: userIdFor.author}, {include: 'roles'}) ).then(function (response) { - done(new Error('Editor should not be able to upgrade the role of authors')); - }, function (error) { - error.type.should.eql('NoPermissionError'); - done(); - }).catch(done); + done(new Error('Editor should not be able to upgrade the role of authors')); + }).catch(function (error) { + error.type.should.eql('NoPermissionError'); + done(); + }).catch(done); }); }); diff --git a/core/test/integration/export_spec.js b/core/test/integration/export_spec.js index 480b8031bf..dfadb4b043 100644 --- a/core/test/integration/export_spec.js +++ b/core/test/integration/export_spec.js @@ -3,7 +3,7 @@ var testUtils = require('../utils/index'), should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), _ = require('lodash'), // Stuff we are testing @@ -25,7 +25,7 @@ describe('Exporter', function () { it('exports data', function (done) { // Stub migrations to return 000 as the current database version var versioningStub = sandbox.stub(versioning, 'getDatabaseVersion', function () { - return when.resolve('003'); + return Promise.resolve('003'); }); exporter().then(function (exportData) { diff --git a/core/test/integration/import_spec.js b/core/test/integration/import_spec.js index ae2fef52d2..3f3f5b8262 100644 --- a/core/test/integration/import_spec.js +++ b/core/test/integration/import_spec.js @@ -3,7 +3,7 @@ var testUtils = require('../utils/index'), should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), assert = require('assert'), _ = require('lodash'), rewire = require('rewire'), @@ -48,7 +48,7 @@ describe('Import', function () { it('resolves 000', function (done) { var importStub = sandbox.stub(Importer000, 'importData', function () { - return when.resolve(); + return Promise.resolve(); }), fakeData = { test: true }; @@ -63,7 +63,7 @@ describe('Import', function () { it('resolves 001', function (done) { var importStub = sandbox.stub(Importer001, 'importData', function () { - return when.resolve(); + return Promise.resolve(); }), fakeData = { test: true }; @@ -78,7 +78,7 @@ describe('Import', function () { it('resolves 002', function (done) { var importStub = sandbox.stub(Importer002, 'importData', function () { - return when.resolve(); + return Promise.resolve(); }), fakeData = { test: true }; @@ -93,7 +93,7 @@ describe('Import', function () { it('resolves 003', function (done) { var importStub = sandbox.stub(Importer003, 'importData', function () { - return when.resolve(); + return Promise.resolve(); }), fakeData = { test: true }; @@ -118,7 +118,7 @@ describe('Import', function () { it('imports data from 000', function (done) { var exportData, versioningStub = sandbox.stub(versioning, 'getDatabaseVersion', function () { - return when.resolve('000'); + return Promise.resolve('000'); }); testUtils.fixtures.loadExportFixture('export-000').then(function (exported) { @@ -127,7 +127,7 @@ describe('Import', function () { return importer('000', exportData); }).then(function () { // Grab the data from tables - return when.all([ + return Promise.all([ knex('users').select(), knex('posts').select(), knex('settings').select(), @@ -186,7 +186,7 @@ describe('Import', function () { return importer('001', exportData); }).then(function () { // Grab the data from tables - return when.all([ + return Promise.all([ knex('users').select(), knex('posts').select(), knex('settings').select(), @@ -261,7 +261,7 @@ describe('Import', function () { error[0].message.should.eql('Value in [posts.title] exceeds maximum length of 150 characters.'); error[0].type.should.eql('ValidationError'); - when.all([ + Promise.all([ knex('users').select(), knex('posts').select(), knex('settings').select(), @@ -310,7 +310,7 @@ describe('Import', function () { error[0].message.should.eql('Value in [settings.key] cannot be blank.'); error[0].type.should.eql('ValidationError'); - when.all([ + Promise.all([ knex('users').select(), knex('posts').select(), knex('settings').select(), @@ -367,7 +367,7 @@ describe('Import', function () { return importer('002', exportData); }).then(function () { // Grab the data from tables - return when.all([ + return Promise.all([ knex('users').select(), knex('posts').select(), knex('settings').select(), @@ -443,7 +443,7 @@ describe('Import', function () { error[0].message.should.eql('Value in [posts.title] exceeds maximum length of 150 characters.'); error[0].type.should.eql('ValidationError'); - when.all([ + Promise.all([ knex('users').select(), knex('posts').select(), knex('settings').select(), @@ -489,7 +489,7 @@ describe('Import', function () { error[0].message.should.eql('Value in [settings.key] cannot be blank.'); error[0].type.should.eql('ValidationError'); - when.all([ + Promise.all([ knex('users').select(), knex('posts').select(), knex('settings').select(), @@ -537,7 +537,7 @@ describe('Import', function () { return importer('003', exportData); }).then(function () { // Grab the data from tables - return when.all([ + return Promise.all([ knex('users').select(), knex('posts').select(), knex('settings').select(), @@ -704,7 +704,7 @@ describe('Import (new test structure)', function () { after(testUtils.teardown); it('gets the right data', function (done) { - var fetchImported = when.join( + var fetchImported = Promise.join( knex('posts').select(), knex('settings').select(), knex('tags').select() @@ -755,7 +755,7 @@ describe('Import (new test structure)', function () { }); it('imports users with correct roles and status', function (done) { - var fetchImported = when.join( + var fetchImported = Promise.join( knex('users').select(), knex('roles_users').select() ); @@ -829,7 +829,7 @@ describe('Import (new test structure)', function () { }); it('imports posts & tags with correct authors, owners etc', function (done) { - var fetchImported = when.join( + var fetchImported = Promise.join( knex('users').select(), knex('posts').select(), knex('tags').select() @@ -931,7 +931,7 @@ describe('Import (new test structure)', function () { after(testUtils.teardown); it('gets the right data', function (done) { - var fetchImported = when.join( + var fetchImported = Promise.join( knex('posts').select(), knex('settings').select(), knex('tags').select() @@ -982,7 +982,7 @@ describe('Import (new test structure)', function () { }); it('imports users with correct roles and status', function (done) { - var fetchImported = when.join( + var fetchImported = Promise.join( knex('users').select(), knex('roles_users').select() ); @@ -1056,7 +1056,7 @@ describe('Import (new test structure)', function () { }); it('imports posts & tags with correct authors, owners etc', function (done) { - var fetchImported = when.join( + var fetchImported = Promise.join( knex('users').select(), knex('posts').select(), knex('tags').select() @@ -1159,7 +1159,7 @@ describe('Import (new test structure)', function () { after(testUtils.teardown); it('gets the right data', function (done) { - var fetchImported = when.join( + var fetchImported = Promise.join( knex('posts').select(), knex('settings').select(), knex('tags').select() @@ -1221,7 +1221,7 @@ describe('Import (new test structure)', function () { }); it('imports users with correct roles and status', function (done) { - var fetchImported = when.join( + var fetchImported = Promise.join( knex('users').select(), knex('roles_users').select() ); @@ -1290,7 +1290,7 @@ describe('Import (new test structure)', function () { }); it('imports posts & tags with correct authors, owners etc', function (done) { - var fetchImported = when.join( + var fetchImported = Promise.join( knex('users').select(), knex('posts').select(), knex('tags').select() diff --git a/core/test/integration/model/model_apps_spec.js b/core/test/integration/model/model_apps_spec.js index 5fd6d2f241..eecf622ac7 100644 --- a/core/test/integration/model/model_apps_spec.js +++ b/core/test/integration/model/model_apps_spec.js @@ -2,7 +2,8 @@ /*jshint expr:true*/ var testUtils = require('../../utils'), should = require('should'), - sequence = require('when/sequence'), + Promise = require('bluebird'), + sequence = require('../../../server/utils/sequence'), _ = require('lodash'), // Stuff we are testing diff --git a/core/test/integration/model/model_posts_spec.js b/core/test/integration/model/model_posts_spec.js index 02c672d783..c6220a5df3 100644 --- a/core/test/integration/model/model_posts_spec.js +++ b/core/test/integration/model/model_posts_spec.js @@ -2,7 +2,8 @@ /*jshint expr:true*/ var testUtils = require('../../utils'), should = require('should'), - sequence = require('when/sequence'), + Promise = require('bluebird'), + sequence = require('../../../server/utils/sequence'), _ = require('lodash'), // Stuff we are testing @@ -541,6 +542,6 @@ describe('Post Model', function () { // }).then(function (saved) { // saved.get('title').should.eql("</title></head><body>[removed]alert('blogtitle');[removed]"); // done(); - // }).otherwise(done); + // }).catch(done); // }); }); diff --git a/core/test/integration/model/model_tags_spec.js b/core/test/integration/model/model_tags_spec.js index d5d9841da2..5cb2e336ce 100644 --- a/core/test/integration/model/model_tags_spec.js +++ b/core/test/integration/model/model_tags_spec.js @@ -2,7 +2,7 @@ /*jshint expr:true*/ var testUtils = require('../../utils'), should = require('should'), - when = require('when'), + Promise = require('bluebird'), _ = require('lodash'), // Stuff we are testing @@ -44,7 +44,7 @@ describe('Tag Model', function () { newTag = testUtils.DataGenerator.forModel.tags[0], createdPostID; - when.all([ + Promise.all([ PostModel.add(newPost, context), TagModel.add(newTag, context) ]).then(function (models) { @@ -71,7 +71,7 @@ describe('Tag Model', function () { createdTagID, createdPostID; - when.all([ + Promise.all([ PostModel.add(newPost, context), TagModel.add(newTag, context) ]).then(function (models) { @@ -106,7 +106,7 @@ describe('Tag Model', function () { var tagModels = tagNames.map(function (tagName) { return TagModel.add({name: tagName}, context); }); createOperations = createOperations.concat(tagModels); - return when.all(createOperations).then(function (models) { + return Promise.all(createOperations).then(function (models) { var postModel = models[0], attachOperations; @@ -115,7 +115,7 @@ describe('Tag Model', function () { attachOperations.push(postModel.tags().attach(models[i])); } - return when.all(attachOperations).then(function () { + return Promise.all(attachOperations).then(function () { return postModel; }); }).then(function (postModel) { diff --git a/core/test/integration/model/model_users_spec.js b/core/test/integration/model/model_users_spec.js index 2317feca89..5438181649 100644 --- a/core/test/integration/model/model_users_spec.js +++ b/core/test/integration/model/model_users_spec.js @@ -2,7 +2,7 @@ /*jshint expr:true*/ var testUtils = require('../../utils'), should = require('should'), - when = require('when'), + Promise = require('bluebird'), sinon = require('sinon'), uuid = require('node-uuid'), _ = require('lodash'), @@ -33,7 +33,7 @@ describe('User Model', function run() { var userData = testUtils.DataGenerator.forModel.users[0]; sandbox.stub(UserModel, 'gravatarLookup', function (userData) { - return when.resolve(userData); + return Promise.resolve(userData); }); UserModel.add(userData, context).then(function (createdUser) { @@ -50,7 +50,7 @@ describe('User Model', function run() { var userData = testUtils.DataGenerator.forModel.users[2]; sandbox.stub(UserModel, 'gravatarLookup', function (userData) { - return when.resolve(userData); + return Promise.resolve(userData); }); UserModel.add(userData, context).then(function (createdUser) { @@ -66,7 +66,7 @@ describe('User Model', function run() { sandbox.stub(UserModel, 'gravatarLookup', function (userData) { userData.image = 'http://www.gravatar.com/avatar/2fab21a4c4ed88e76add10650c73bae1?d=404'; - return when.resolve(userData); + return Promise.resolve(userData); }); UserModel.add(userData, context).then(function (createdUser) { @@ -83,7 +83,7 @@ describe('User Model', function run() { var userData = testUtils.DataGenerator.forModel.users[0]; sandbox.stub(UserModel, 'gravatarLookup', function (userData) { - return when.resolve(userData); + return Promise.resolve(userData); }); UserModel.add(userData, context).then(function (createdUser) { @@ -271,7 +271,7 @@ describe('User Model', function run() { var userData = testUtils.DataGenerator.forModel.users[4]; sandbox.stub(UserModel, 'gravatarLookup', function (userData) { - return when.resolve(userData); + return Promise.resolve(userData); }); RoleModel.findOne().then(function (role) { diff --git a/core/test/unit/apps_spec.js b/core/test/unit/apps_spec.js index b6ad4f5e57..a27c705b33 100644 --- a/core/test/unit/apps_spec.js +++ b/core/test/unit/apps_spec.js @@ -5,7 +5,7 @@ var path = require('path'), should = require('should'), sinon = require('sinon'), _ = require('lodash'), - when = require('when'), + Promise = require('bluebird'), helpers = require('../../server/helpers'), filters = require('../../server/filters'), @@ -432,7 +432,7 @@ describe('Apps', function () { var perms = new AppPermissions("test"); // No package.json in this directory - sandbox.stub(perms, "checkPackageContentsExists").returns(when.resolve(false)); + sandbox.stub(perms, "checkPackageContentsExists").returns(Promise.resolve(false)); perms.read().then(function (readPerms) { should.exist(readPerms); @@ -447,9 +447,9 @@ describe('Apps', function () { noGhostPackageJsonContents = JSON.stringify(noGhostPackageJson, null, 2); // package.json IS in this directory - sandbox.stub(perms, "checkPackageContentsExists").returns(when.resolve(true)); + sandbox.stub(perms, "checkPackageContentsExists").returns(Promise.resolve(true)); // no ghost property on package - sandbox.stub(perms, "getPackageContents").returns(when.resolve(noGhostPackageJsonContents)); + sandbox.stub(perms, "getPackageContents").returns(Promise.resolve(noGhostPackageJsonContents)); perms.read().then(function (readPerms) { should.exist(readPerms); @@ -463,9 +463,9 @@ describe('Apps', function () { var perms = new AppPermissions("test"); // package.json IS in this directory - sandbox.stub(perms, "checkPackageContentsExists").returns(when.resolve(true)); + sandbox.stub(perms, "checkPackageContentsExists").returns(Promise.resolve(true)); // malformed JSON on package - sandbox.stub(perms, "getPackageContents").returns(when.reject(new Error('package.json file is malformed'))); + sandbox.stub(perms, "getPackageContents").returns(Promise.reject(new Error('package.json file is malformed'))); perms.read().then(function (readPerms) { /*jshint unused:false*/ @@ -480,9 +480,9 @@ describe('Apps', function () { validGhostPackageJsonContents = validGhostPackageJson; // package.json IS in this directory - sandbox.stub(perms, "checkPackageContentsExists").returns(when.resolve(true)); + sandbox.stub(perms, "checkPackageContentsExists").returns(Promise.resolve(true)); // valid ghost property on package - sandbox.stub(perms, "getPackageContents").returns(when.resolve(validGhostPackageJsonContents)); + sandbox.stub(perms, "getPackageContents").returns(Promise.resolve(validGhostPackageJsonContents)); perms.read().then(function (readPerms) { should.exist(readPerms); diff --git a/core/test/unit/bootstrap_spec.js b/core/test/unit/bootstrap_spec.js index 97ad68b620..6e6906125e 100644 --- a/core/test/unit/bootstrap_spec.js +++ b/core/test/unit/bootstrap_spec.js @@ -2,7 +2,7 @@ /*jshint expr:true*/ var should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), path = require('path'), fs = require('fs'), _ = require('lodash'), @@ -62,13 +62,10 @@ describe('Bootstrap', function () { it('creates the config file if one does not exist', function (done) { - var deferred = when.defer(), // trick bootstrap into thinking that the config file doesn't exist yet - existsStub = sandbox.stub(fs, 'exists', function (file, cb) { return cb(false); }), + var existsStub = sandbox.stub(fs, 'exists', function (file, cb) { return cb(false); }), // create a method which will return a pre-resolved promise - resolvedPromise = sandbox.stub().returns(deferred.promise); - - deferred.resolve(); + resolvedPromise = sandbox.stub().returns(Promise.resolve()); // ensure that the file creation is a stub, the tests shouldn't really create a file bootstrap.__set__('writeConfigFile', resolvedPromise); diff --git a/core/test/unit/config_spec.js b/core/test/unit/config_spec.js index e0066cff64..3e3b84eb30 100644 --- a/core/test/unit/config_spec.js +++ b/core/test/unit/config_spec.js @@ -2,7 +2,7 @@ /*jshint expr:true*/ var should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), path = require('path'), _ = require('lodash'), rewire = require('rewire'), @@ -32,7 +32,7 @@ describe('Config', function () { settings = {'read': function read() {}}; settingsStub = sandbox.stub(settings, 'read', function () { - return when({ settings: [{value: 'casper'}] }); + return Promise.resolve({ settings: [{value: 'casper'}] }); }); theme.update(settings, 'http://my-ghost-blog.com') @@ -270,7 +270,7 @@ describe('Config', function () { it('should output correct url for post', function (done) { var settings = {'read': function read() {}}, settingsStub = sandbox.stub(settings, 'read', function () { - return when({ settings: [{value: '/:slug/'}] }); + return Promise.resolve({ settings: [{value: '/:slug/'}] }); }), /*jshint unused:false*/ testData = testUtils.DataGenerator.Content.posts[2], @@ -308,7 +308,7 @@ describe('Config', function () { it('should output correct url for post with date permalink', function (done) { var settings = {'read': function read() {}}, settingsStub = sandbox.stub(settings, 'read', function () { - return when({ settings: [{value: '/:year/:month/:day/:slug/'}] }); + return Promise.resolve({ settings: [{value: '/:year/:month/:day/:slug/'}] }); }), /*jshint unused:false*/ testData = testUtils.DataGenerator.Content.posts[2], @@ -349,7 +349,7 @@ describe('Config', function () { it('should output correct url for page with date permalink', function (done) { var settings = {'read': function read() {}}, settingsStub = sandbox.stub(settings, 'read', function () { - return when({ settings: [{value: '/:year/:month/:day/:slug/'}] }); + return Promise.resolve({ settings: [{value: '/:year/:month/:day/:slug/'}] }); }), /*jshint unused:false*/ testData = testUtils.DataGenerator.Content.posts[5], diff --git a/core/test/unit/errorHandling_spec.js b/core/test/unit/errorHandling_spec.js index 8e393712a3..840a0388b9 100644 --- a/core/test/unit/errorHandling_spec.js +++ b/core/test/unit/errorHandling_spec.js @@ -1,7 +1,7 @@ /*globals describe, after, before, beforeEach, afterEach, it*/ /*jshint expr:true*/ var should = require('should'), - when = require('when'), + Promise = require('bluebird'), sinon = require('sinon'), express = require('express'), rewire = require('rewire'), @@ -181,9 +181,7 @@ describe('Error handling', function () { }); it('logs promise errors and redirects', function (done) { - var def = when.defer(), - prom = def.promise, - req = null, + var req = null, res = { redirect: function () { return; @@ -192,11 +190,11 @@ describe('Error handling', function () { redirectStub = sinon.stub(res, 'redirect'); // give environment a value that will console log - prom.then(function () { + Promise.reject().then(function () { throw new Error('Ran success handler'); }, errors.logErrorWithRedirect('test1', null, null, '/testurl', req, res)); - prom.otherwise(function () { + Promise.reject().catch(function () { logStub.calledWith('\nERROR:'.red, 'test1'.red).should.equal(true); logStub.restore(); @@ -205,7 +203,6 @@ describe('Error handling', function () { done(); }); - def.reject(); }); }); diff --git a/core/test/unit/filters_spec.js b/core/test/unit/filters_spec.js index 2736ab8e5a..4be09dc003 100644 --- a/core/test/unit/filters_spec.js +++ b/core/test/unit/filters_spec.js @@ -2,7 +2,7 @@ /*jshint expr:true*/ var should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), _ = require('lodash'), // Stuff we are testing @@ -85,7 +85,7 @@ describe('Filters', function () { it('executes filters that return a promise', function (done) { var filterName = 'testprioritypromise', testFilterHandler1 = sinon.spy(function (args) { - return when.promise(function (resolve) { + return new Promise(function (resolve) { process.nextTick(function () { args.filter1 = true; @@ -99,7 +99,7 @@ describe('Filters', function () { return args; }), testFilterHandler3 = sinon.spy(function (args) { - return when.promise(function (resolve) { + return new Promise(function (resolve) { process.nextTick(function () { args.filter3 = true; diff --git a/core/test/unit/frontend_spec.js b/core/test/unit/frontend_spec.js index 91d7dfcf2a..e0e7dceb74 100644 --- a/core/test/unit/frontend_spec.js +++ b/core/test/unit/frontend_spec.js @@ -4,7 +4,7 @@ var assert = require('assert'), moment = require('moment'), should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), rewire = require('rewire'), _ = require('lodash'), @@ -53,11 +53,11 @@ describe('Frontend Controller', function () { }; sandbox.stub(api.posts, 'browse', function () { - return when({posts: {}, meta: {pagination: { pages: 3}}}); + return Promise.resolve({posts: {}, meta: {pagination: { pages: 3}}}); }); apiSettingsStub = sandbox.stub(api.settings, 'read'); - apiSettingsStub.withArgs('postsPerPage').returns(when({ + apiSettingsStub.withArgs('postsPerPage').returns(Promise.resolve({ settings: [{ 'key': 'postsPerPage', 'value': 5 @@ -156,7 +156,7 @@ describe('Frontend Controller', function () { beforeEach(function () { sandbox.stub(api.posts, 'browse', function () { - return when({ + return Promise.resolve({ posts: [], meta: { pagination: { @@ -169,14 +169,14 @@ describe('Frontend Controller', function () { apiSettingsStub = sandbox.stub(api.settings, 'read'); - apiSettingsStub.withArgs(sinon.match.has('key', 'activeTheme')).returns(when({ + apiSettingsStub.withArgs(sinon.match.has('key', 'activeTheme')).returns(Promise.resolve({ settings: [{ 'key': 'activeTheme', 'value': 'casper' }] })); - apiSettingsStub.withArgs('postsPerPage').returns(when({ + apiSettingsStub.withArgs('postsPerPage').returns(Promise.resolve({ settings: [{ 'key': 'postsPerPage', 'value': '10' @@ -306,7 +306,7 @@ describe('Frontend Controller', function () { beforeEach(function () { sandbox.stub(api.posts, 'browse', function () { - return when({ + return Promise.resolve({ posts: mockPosts, meta: { pagination: { @@ -322,14 +322,14 @@ describe('Frontend Controller', function () { apiSettingsStub = sandbox.stub(api.settings, 'read'); - apiSettingsStub.withArgs(sinon.match.has('key', 'activeTheme')).returns(when({ + apiSettingsStub.withArgs(sinon.match.has('key', 'activeTheme')).returns(Promise.resolve({ settings: [{ 'key': 'activeTheme', 'value': 'casper' }] })); - apiSettingsStub.withArgs('postsPerPage').returns(when({ + apiSettingsStub.withArgs('postsPerPage').returns(Promise.resolve({ settings: [{ 'key': 'postsPerPage', 'value': '10' @@ -355,7 +355,7 @@ describe('Frontend Controller', function () { describe('custom tag template', function () { beforeEach(function () { - apiSettingsStub.withArgs('permalinks').returns(when({ + apiSettingsStub.withArgs('permalinks').returns(Promise.resolve({ settings: [{ key: 'permalinks', value: '/tag/:slug/' @@ -392,11 +392,11 @@ describe('Frontend Controller', function () { }; sandbox.stub(api.posts, 'browse', function () { - return when({posts: {}, meta: {pagination: { pages: 3}}}); + return Promise.resolve({posts: {}, meta: {pagination: { pages: 3}}}); }); apiSettingsStub = sandbox.stub(api.settings, 'read'); - apiSettingsStub.withArgs('postsPerPage').returns(when({ + apiSettingsStub.withArgs('postsPerPage').returns(Promise.resolve({ settings: [{ 'key': 'postsPerPage', 'value': 5 @@ -542,14 +542,14 @@ describe('Frontend Controller', function () { beforeEach(function () { sandbox.stub(api.posts, 'read', function (args) { - return when(_.find(mockPosts, function(mock) { + return Promise.resolve(_.find(mockPosts, function(mock) { return mock.posts[0].slug === args.slug; })); }); apiSettingsStub = sandbox.stub(api.settings, 'read'); - apiSettingsStub.withArgs(sinon.match.has('key', 'activeTheme')).returns(when({ + apiSettingsStub.withArgs(sinon.match.has('key', 'activeTheme')).returns(Promise.resolve({ settings: [{ 'key': 'activeTheme', 'value': 'casper' @@ -577,7 +577,7 @@ describe('Frontend Controller', function () { describe('custom page templates', function () { beforeEach(function () { - apiSettingsStub.withArgs('permalinks').returns(when({ + apiSettingsStub.withArgs('permalinks').returns(Promise.resolve({ settings: [{ value: '/:slug/' }] @@ -602,7 +602,7 @@ describe('Frontend Controller', function () { describe('permalink set to slug', function () { beforeEach(function () { - apiSettingsStub.withArgs('permalinks').returns(when({ + apiSettingsStub.withArgs('permalinks').returns(Promise.resolve({ settings: [{ value: '/:slug/' }] @@ -674,7 +674,7 @@ describe('Frontend Controller', function () { describe('permalink set to date', function () { beforeEach(function () { - apiSettingsStub.withArgs('permalinks').returns(when({ + apiSettingsStub.withArgs('permalinks').returns(Promise.resolve({ settings: [{ value: '/:year/:month/:day/:slug/' }] @@ -747,7 +747,7 @@ describe('Frontend Controller', function () { describe('post', function () { describe('permalink set to slug', function () { beforeEach(function () { - apiSettingsStub.withArgs('permalinks').returns(when({ + apiSettingsStub.withArgs('permalinks').returns(Promise.resolve({ settings: [{ value: '/:slug' }] @@ -821,7 +821,7 @@ describe('Frontend Controller', function () { describe('permalink set to date', function () { beforeEach(function () { - apiSettingsStub.withArgs('permalinks').returns(when({ + apiSettingsStub.withArgs('permalinks').returns(Promise.resolve({ settings: [{ value: '/:year/:month/:day/:slug' }] @@ -912,7 +912,7 @@ describe('Frontend Controller', function () { describe('permalink set to custom format', function () { beforeEach(function () { - apiSettingsStub.withArgs('permalinks').returns(when({ + apiSettingsStub.withArgs('permalinks').returns(Promise.resolve({ settings: [{ value: '/:year/:slug' }] @@ -1035,25 +1035,25 @@ describe('Frontend Controller', function () { }; sandbox.stub(api.posts, 'browse', function () { - return when({posts: {}, meta: {pagination: { pages: 3}}}); + return Promise.resolve({posts: {}, meta: {pagination: { pages: 3}}}); }); - apiUsersStub = sandbox.stub(api.users, 'read').returns(when({})); + apiUsersStub = sandbox.stub(api.users, 'read').returns(Promise.resolve({})); apiSettingsStub = sandbox.stub(api.settings, 'read'); - apiSettingsStub.withArgs('title').returns(when({ + apiSettingsStub.withArgs('title').returns(Promise.resolve({ settings: [{ 'key': 'title', 'value': 'Test' }] })); - apiSettingsStub.withArgs('description').returns(when({ + apiSettingsStub.withArgs('description').returns(Promise.resolve({ settings: [{ 'key': 'description', 'value': 'Some Text' }] })); - apiSettingsStub.withArgs('permalinks').returns(when({ + apiSettingsStub.withArgs('permalinks').returns(Promise.resolve({ settings: [{ 'key': 'permalinks', 'value': '/:slug/' diff --git a/core/test/unit/mail_spec.js b/core/test/unit/mail_spec.js index c527fc3a42..890c93f55c 100644 --- a/core/test/unit/mail_spec.js +++ b/core/test/unit/mail_spec.js @@ -2,7 +2,7 @@ /*jshint expr:true*/ var should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), _ = require('lodash'), rewire = require('rewire'), @@ -61,7 +61,7 @@ describe('Mail', function () { }); sandbox.stub(mailer, 'detectSendmail', function () { - return when.resolve(fakeSendmail); + return Promise.resolve(fakeSendmail); }); }); @@ -119,7 +119,7 @@ describe('Mail', function () { it('should disable transport if config is empty & sendmail not found', function (done) { overrideConfig({mail: {}}); mailer.detectSendmail.restore(); - sandbox.stub(mailer, 'detectSendmail', when.reject); + sandbox.stub(mailer, 'detectSendmail', Promise.reject); mailer.init().then(function () { should.not.exist(mailer.transport); done(); @@ -141,7 +141,7 @@ describe('Mail', function () { it('should fail to send messages when no transport is set', function (done) { mailer.detectSendmail.restore(); - sandbox.stub(mailer, 'detectSendmail', when.reject); + sandbox.stub(mailer, 'detectSendmail', Promise.reject); mailer.init().then(function () { mailer.send().then(function () { should.fail(); @@ -154,15 +154,15 @@ describe('Mail', function () { }); it('should fail to send messages when given insufficient data', function (done) { - when.settle([ + Promise.settle([ mailer.send(), mailer.send({}), mailer.send({ subject: '123' }), mailer.send({ subject: '', html: '123' }) ]).then(function (descriptors) { descriptors.forEach(function (d) { - d.state.should.equal('rejected'); - d.reason.should.be.an.instanceOf(Error); + d.isRejected().should.be.true; + d.reason().should.be.an.instanceOf(Error); }); done(); }).catch(done); diff --git a/core/test/unit/permissions_spec.js b/core/test/unit/permissions_spec.js index 25429a15c8..9da5700619 100644 --- a/core/test/unit/permissions_spec.js +++ b/core/test/unit/permissions_spec.js @@ -3,7 +3,7 @@ var testUtils = require('../utils'), should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), _ = require('lodash'), // Stuff we are testing @@ -31,7 +31,7 @@ describe('Permissions', function () { }); sandbox.stub(Models.Permission, 'findAll', function () { - return when(Models.Permissions.forge(permissions)); + return Promise.resolve(Models.Permissions.forge(permissions)); }); }); @@ -112,7 +112,7 @@ describe('Permissions', function () { // it('can use permissible function on Model to allow something', function (done) { // var testUser, // permissibleStub = sandbox.stub(Models.Post, 'permissible', function () { -// return when.resolve(); +// return Promise.resolve(); // }); // // testUtils.insertAuthorUser() @@ -141,7 +141,7 @@ describe('Permissions', function () { // it('can use permissible function on Model to forbid something', function (done) { // var testUser, // permissibleStub = sandbox.stub(Models.Post, 'permissible', function () { -// return when.reject(); +// return Promise.reject(); // }); // // testUtils.insertAuthorUser() @@ -203,7 +203,7 @@ describe('Permissions', function () { // // return newPerm.save(null, context).then(function () { // return foundUser.permissions().attach(newPerm).then(function () { -// return when.all([updatedPost, foundUser]); +// return Promise.all([updatedPost, foundUser]); // }); // }); // }); diff --git a/core/test/unit/server_helpers_index_spec.js b/core/test/unit/server_helpers_index_spec.js index 36b66d0fc4..b7877ce7d6 100644 --- a/core/test/unit/server_helpers_index_spec.js +++ b/core/test/unit/server_helpers_index_spec.js @@ -2,7 +2,7 @@ /*jshint expr:true*/ var should = require('should'), sinon = require('sinon'), - when = require('when'), + Promise = require('bluebird'), _ = require('lodash'), rewire = require('rewire'), moment = require('moment'), @@ -30,7 +30,7 @@ describe('Core Helpers', function () { helpers = rewire('../../server/helpers'); sandbox = sinon.sandbox.create(); apiStub = sandbox.stub(api.settings, 'read', function () { - return when({ + return Promise.resolve({ settings: [{value: 'casper'}] }); }); @@ -393,7 +393,7 @@ describe('Core Helpers', function () { }); it('can render class string for context', function (done) { - when.all([ + Promise.all([ helpers.body_class.call({relativeUrl: '/'}), helpers.body_class.call({relativeUrl: '/a-post-title', post: {}}), helpers.body_class.call({relativeUrl: '/page/4'}), @@ -762,7 +762,7 @@ describe('Core Helpers', function () { beforeEach(function () { apiStub.restore(); apiStub = sandbox.stub(api.settings, 'read', function () { - return when({ settings: [{ value: '/:slug/' }] }); + return Promise.resolve({ settings: [{ value: '/:slug/' }] }); }); }); diff --git a/core/test/unit/xmlrpc_spec.js b/core/test/unit/xmlrpc_spec.js index 4abd3d2a49..6a2583657f 100644 --- a/core/test/unit/xmlrpc_spec.js +++ b/core/test/unit/xmlrpc_spec.js @@ -5,7 +5,7 @@ var nock = require('nock'), should = require('should'), sinon = require('sinon'), testUtils = require('../utils'), - when = require('when'), + Promise = require('bluebird'), xmlrpc = require('../../server/xmlrpc'), // storing current environment currentEnv = process.env.NODE_ENV; @@ -34,7 +34,7 @@ describe('XMLRPC', function () { ping2 = nock('http://rpc.pingomatic.com').post('/').reply(200), testPost = testUtils.DataGenerator.Content.posts[2], settingsStub = sandbox.stub(settings, 'read', function () { - return when({ settings: [{value: '/:slug/'}] }); + return Promise.resolve({ settings: [{value: '/:slug/'}] }); }); /*jshint unused:false */ diff --git a/core/test/utils/fork.js b/core/test/utils/fork.js index 6113fe477c..ace95b935e 100644 --- a/core/test/utils/fork.js +++ b/core/test/utils/fork.js @@ -3,33 +3,39 @@ var cp = require('child_process'), fs = require('fs'), url = require('url'), net = require('net'), - when = require('when'), + Promise = require('bluebird'), path = require('path'), config = require('../../server/config'); function findFreePort(port) { - var deferred = when.defer(); - - if (typeof port === 'string') port = parseInt(port); - if (typeof port !== 'number') port = 2368; - port = port + 1; - - var server = net.createServer(); - server.on('error', function(e) { - if (e.code === 'EADDRINUSE') { - when.chain(findFreePort(port), deferred); - } else { - deferred.reject(e); + return new Promise(function (resolve, reject) { + if (typeof port === 'string') { + port = parseInt(port); } - }); - server.listen(port, function() { - var listenPort = server.address().port; - server.close(function() { - deferred.resolve(listenPort); + + if (typeof port !== 'number') { + port = 2368; + } + + port = port + 1; + + var server = net.createServer(); + + server.on('error', function(e) { + if (e.code === 'EADDRINUSE') { + resolve(findFreePort(port)); + } else { + reject(e); + } + }); + + server.listen(port, function() { + var listenPort = server.address().port; + server.close(function() { + resolve(listenPort); + }); }); }); - - return deferred.promise; } // Get a copy of current config object from file, to be modified before @@ -43,85 +49,87 @@ function forkConfig() { // Creates a new fork of Ghost process with a given config // Useful for tests that want to verify certain config options function forkGhost(newConfig, envName) { - var deferred = when.defer(); envName = envName || 'forked'; - findFreePort(newConfig.server ? newConfig.server.port : undefined) + + return findFreePort(newConfig.server ? newConfig.server.port : undefined) .then(function(port) { newConfig.server = newConfig.server || {}; newConfig.server.port = port; newConfig.url = url.format(_.extend(url.parse(newConfig.url), {port: port, host: null})); - + var newConfigFile = path.join(config.paths.appRoot, 'config.test' + port + '.js'); - fs.writeFile(newConfigFile, 'module.exports = {' + envName + ': ' + JSON.stringify(newConfig) + '}', function(err) { - if (err) throw err; - - // setup process environment for the forked Ghost to use the new config file - var env = _.clone(process.env); - env['GHOST_CONFIG'] = newConfigFile; - env['NODE_ENV'] = envName; - var child = cp.fork(path.join(config.paths.appRoot, 'index.js'), {env: env}); - - var pingTries = 0; - var pingCheck; - var pingStop = function() { - if (pingCheck) { - clearInterval(pingCheck); - pingCheck = undefined; - return true; + + return new Promise(function (resolve, reject) { + fs.writeFile(newConfigFile, 'module.exports = {' + envName + ': ' + JSON.stringify(newConfig) + '}', function(err) { + if (err) { + return reject(err); } - return false; - }; - // periodic check until forked Ghost is running and is listening on the port - pingCheck = setInterval(function() { - var socket = net.connect(port); - socket.on('connect', function() { - socket.end(); - if (pingStop()) { - deferred.resolve(child); + + // setup process environment for the forked Ghost to use the new config file + var env = _.clone(process.env); + env['GHOST_CONFIG'] = newConfigFile; + env['NODE_ENV'] = envName; + var child = cp.fork(path.join(config.paths.appRoot, 'index.js'), {env: env}); + + var pingTries = 0; + var pingCheck; + var pingStop = function() { + if (pingCheck) { + clearInterval(pingCheck); + pingCheck = undefined; + return true; } - }); - socket.on('error', function(err) { - // continue checking - if (++pingTries >= 20 && pingStop()) { - deferred.reject(new Error("Timed out waiting for child process")); - } - }); - }, 200); - - child.on('exit', function(code, signal) { - child.exited = true; - if (pingStop()) { - deferred.reject(new Error("Child process exit code: " + code)); - } - // cleanup the temporary config file - fs.unlink(newConfigFile); - }); - - // override kill() to have an async callback - var baseKill = child.kill; - child.kill = function(signal, cb) { - if (typeof signal === 'function') { - cb = signal; - signal = undefined; - } - - if (cb) { - child.on('exit', function() { - cb(); + return false; + }; + // periodic check until forked Ghost is running and is listening on the port + pingCheck = setInterval(function() { + var socket = net.connect(port); + socket.on('connect', function() { + socket.end(); + if (pingStop()) { + resolve(child); + } }); - } + socket.on('error', function(err) { + // continue checking + if (++pingTries >= 20 && pingStop()) { + reject(new Error("Timed out waiting for child process")); + } + }); + }, 200); - if (child.exited) { - process.nextTick(cb); - } else { - baseKill.apply(child, [signal]); - } - }; + child.on('exit', function(code, signal) { + child.exited = true; + if (pingStop()) { + reject(new Error("Child process exit code: " + code)); + } + // cleanup the temporary config file + fs.unlink(newConfigFile); + }); + + // override kill() to have an async callback + var baseKill = child.kill; + child.kill = function(signal, cb) { + if (typeof signal === 'function') { + cb = signal; + signal = undefined; + } + + if (cb) { + child.on('exit', function() { + cb(); + }); + } + + if (child.exited) { + process.nextTick(cb); + } else { + baseKill.apply(child, [signal]); + } + }; + }); }); - }) - .otherwise(deferred.reject); - - return deferred.promise; + }); } module.exports.ghost = forkGhost; diff --git a/core/test/utils/index.js b/core/test/utils/index.js index f819b8adb7..24801709df 100644 --- a/core/test/utils/index.js +++ b/core/test/utils/index.js @@ -1,6 +1,5 @@ -var when = require('when'), - sequence = require('when/sequence'), - nodefn = require('when/node'), +var Promise = require('bluebird'), + sequence = require('../../server/utils/sequence'), _ = require('lodash'), fs = require('fs-extra'), path = require('path'), @@ -32,7 +31,7 @@ var when = require('when'), fixtures = { insertPosts: function insertPosts() { var knex = config.database.knex; - return when(knex('posts').insert(DataGenerator.forKnex.posts)).then(function () { + return Promise.resolve(knex('posts').insert(DataGenerator.forKnex.posts)).then(function () { return knex('tags').insert(DataGenerator.forKnex.tags); }).then(function () { return knex('posts_tags').insert(DataGenerator.forKnex.posts_tags); @@ -49,7 +48,7 @@ fixtures = { max = max || 50; // insert users of different roles - return when(fixtures.createUsersWithRoles()).then(function (results) { + return Promise.resolve(fixtures.createUsersWithRoles()).then(function (results) { // create the tags return knex('tags').insert(DataGenerator.forKnex.tags); }).then(function (results) { @@ -72,7 +71,7 @@ fixtures = { }; })); }).then(function () { - return when.all([ + return Promise.all([ // PostgreSQL can return results in any order knex('posts').orderBy('id', 'asc').select('id'), knex('tags').select('id') @@ -84,7 +83,7 @@ fixtures = { i; if (max > posts.length) { - throw new Error('Trying to add more posts_tags than the number of posts.'); + throw new Error('Trying to add more posts_tags than the number of posts. ' + max + ' ' + posts.length); } for (i = 0; i < max; i += 1) { @@ -133,7 +132,7 @@ fixtures = { var knex = config.database.knex; - return when.all([ + return Promise.all([ // PostgreSQL can return results in any order knex('posts').orderBy('id', 'asc').select('id'), knex('tags').select('id', 'name') @@ -268,16 +267,17 @@ fixtures = { }, loadExportFixture: function loadExportFixture(filename) { - var filepath = path.resolve(__dirname + '/fixtures/' + filename + '.json'); + var filepath = path.resolve(__dirname + '/fixtures/' + filename + '.json'), + readFile = Promise.promisify(fs.readFile); - return nodefn.call(fs.readFile, filepath).then(function (fileContents) { + return readFile(filepath).then(function (fileContents) { var data; // Parse the json data try { data = JSON.parse(fileContents); } catch (e) { - return when.reject(new Error('Failed to parse the file')); + return new Error('Failed to parse the file'); } return data; @@ -451,7 +451,6 @@ setup = function setup() { // TODO make this do the DB init as well doAuth = function doAuth() { var options = arguments, - deferred = when.defer(), request = arguments[0], user = DataGenerator.forModel.users[0], fixtureOps; @@ -466,19 +465,19 @@ doAuth = function doAuth() { fixtureOps = getFixtureOps(options); - sequence(fixtureOps).then(function () { - request.post('/ghost/api/v0.1/authentication/token/') - .send({ grant_type: 'password', username: user.email, password: user.password, client_id: 'ghost-admin'}) - .end(function (err, res) { - if (err) { - deferred.reject(err); - } + return new Promise(function (resolve, reject) { + return sequence(fixtureOps).then(function () { + request.post('/ghost/api/v0.1/authentication/token/') + .send({ grant_type: 'password', username: user.email, password: user.password, client_id: 'ghost-admin'}) + .end(function (err, res) { + if (err) { + return reject(err); + } - deferred.resolve(res.body.access_token); - }); + resolve(res.body.access_token); + }); + }); }); - - return deferred.promise; }; teardown = function teardown(done) { diff --git a/package.json b/package.json index 6a8f42ed32..bf2da2ccc2 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "engineStrict": true, "dependencies": { "bcryptjs": "0.7.10", + "bluebird": "2.3.0", "body-parser": "1.6.3", "bookshelf": "0.7.6", "busboy": "0.2.3", @@ -63,7 +64,6 @@ "static-favicon": "1.0.2", "unidecode": "0.1.3", "validator": "3.4.0", - "when": "3.2.3", "xml": "0.0.12" }, "optionalDependencies": {