diff --git a/package.json b/package.json index e2dcb9481..ed2755689 100644 --- a/package.json +++ b/package.json @@ -9,16 +9,17 @@ "bunyan-cloudwatch": "2.0.0", "bunyan-loggly": "^1.0.0", "bunyan-prettystream": "^0.1.3", + "imap": "0.8.x", "lerna": "2.0.0-beta.23", "mysql": "^2.11.1", "newrelic": "^1.28.1", "pm2": "^1.1.3", + "promise.prototype.finally": "^1.0.1", "redis": "2.x.x", "rx": "4.x.x", "sequelize": "3.x.x", "underscore": "1.x.x", - "utf7": "https://github.com/truebit/utf7/archive/1f753bac59b99d93b17a5ef11681e232465e2558.tar.gz", - "imap": "0.8.x" + "utf7": "https://github.com/truebit/utf7/archive/1f753bac59b99d93b17a5ef11681e232465e2558.tar.gz" }, "devDependencies": { "babel-eslint": "6.x", diff --git a/packages/nylas-api/app.js b/packages/nylas-api/app.js index 8199a9687..2dc1e9d28 100644 --- a/packages/nylas-api/app.js +++ b/packages/nylas-api/app.js @@ -11,7 +11,6 @@ const fs = require('fs'); const path = require('path'); const {DatabaseConnector, SchedulerUtils, Logger} = require(`nylas-core`); -global.Promise = require('bluebird'); global.Logger = Logger.createLogger('nylas-k2-api') const onUnhandledError = (err) => global.Logger.fatal(err, 'Unhandled error') diff --git a/packages/nylas-core/imap-connection.js b/packages/nylas-core/imap-connection.js index 2b002e048..4132cf140 100644 --- a/packages/nylas-core/imap-connection.js +++ b/packages/nylas-core/imap-connection.js @@ -3,6 +3,7 @@ const _ = require('underscore'); const xoauth2 = require('xoauth2'); const EventEmitter = require('events'); +const PromiseUtils = require('./promise-utils') const IMAPBox = require('./imap-box'); const { convertImapError, @@ -87,7 +88,7 @@ class IMAPConnection extends EventEmitter { _buildUnderlyingConnection(settings) { return new Promise((resolve, reject) => { - this._imap = Promise.promisifyAll(new Imap(settings)); + this._imap = PromiseUtils.promisifyAll(new Imap(settings)); // Emitted when new mail arrives in the currently open mailbox. // Fix https://github.com/mscdex/node-imap/issues/445 diff --git a/packages/nylas-core/index.js b/packages/nylas-core/index.js index 431eed10a..b6348ddd4 100644 --- a/packages/nylas-core/index.js +++ b/packages/nylas-core/index.js @@ -1,5 +1,3 @@ -global.Promise = require('bluebird'); - module.exports = { Provider: { Gmail: 'gmail', @@ -13,4 +11,5 @@ module.exports = { MessageTypes: require('./message-types'), Logger: require('./logger'), Errors: require('./imap-errors'), + PromiseUtils: require('./promise-utils'), } diff --git a/packages/nylas-core/promise-utils.js b/packages/nylas-core/promise-utils.js new file mode 100644 index 000000000..d0114b34d --- /dev/null +++ b/packages/nylas-core/promise-utils.js @@ -0,0 +1,50 @@ +require('promise.prototype.finally') +const _ = require('underscore') + +function sleep(ms) { + return new Promise((resolve) => setTimeout(resolve, ms)) +} + +function each(iterable, iterator) { + return Promise.resolve(iterable) + .then((iter) => Array.from(iter)) + .then((array) => { + return new Promise((resolve, reject) => { + array.reduce((prevPromise, item, idx, len) => ( + prevPromise.then(() => Promise.resolve(iterator(item, idx, len))) + ), Promise.resolve()) + .then(() => resolve(iterable)) + .catch((err) => reject(err)) + }) + }) +} + +function promisify(nodeFn) { + return function wrapper(...fnArgs) { + return new Promise((resolve, reject) => { + nodeFn.call(this, ...fnArgs, (err, ...results) => { + if (err) { + reject(err) + return + } + resolve(...results) + }); + }) + } +} + +function promisifyAll(obj) { + for(const key in obj) { + if (!key.endsWith('Async') && _.isFunction(obj[key])) { + obj[`${key}Async`] = promisify(obj[key]) + } + } + return obj +} + +module.exports = { + each, + sleep, + promisify, + promisifyAll, +} diff --git a/packages/nylas-core/pubsub-connector.js b/packages/nylas-core/pubsub-connector.js index 36121688a..fc5ff8bba 100644 --- a/packages/nylas-core/pubsub-connector.js +++ b/packages/nylas-core/pubsub-connector.js @@ -1,9 +1,10 @@ const Rx = require('rx') const redis = require("redis"); +const PromiseUtils = require('./promise-utils') const log = global.Logger || console -Promise.promisifyAll(redis.RedisClient.prototype); -Promise.promisifyAll(redis.Multi.prototype); +PromiseUtils.promisifyAll(redis.RedisClient.prototype); +PromiseUtils.promisifyAll(redis.Multi.prototype); class PubsubConnector { diff --git a/packages/nylas-core/scheduler-utils.js b/packages/nylas-core/scheduler-utils.js index 9b719f0ba..41711ca71 100644 --- a/packages/nylas-core/scheduler-utils.js +++ b/packages/nylas-core/scheduler-utils.js @@ -8,12 +8,13 @@ const HEARTBEAT_EXPIRES = 30; // 2 min in prod? const CLAIM_DURATION = 10 * 60 * 1000; // 2 hours on prod? +const PromiseUtils = require('./promise-utils') const PubsubConnector = require('./pubsub-connector'); const MessageTypes = require('./message-types') const forEachAccountList = (forEachCallback) => { const client = PubsubConnector.broadcastClient(); - return Promise.each(client.keysAsync(`accounts:*`), (key) => { + return PromiseUtils.each(client.keysAsync(`accounts:*`), (key) => { const processId = key.replace('accounts:', ''); return client.lrangeAsync(key, 0, 20000).then((foundIds) => forEachCallback(processId, foundIds) diff --git a/packages/nylas-dashboard/app.js b/packages/nylas-dashboard/app.js index 0284429ae..95eddfb30 100644 --- a/packages/nylas-dashboard/app.js +++ b/packages/nylas-dashboard/app.js @@ -5,7 +5,6 @@ const Hapi = require('hapi'); const HapiWebSocket = require('hapi-plugin-websocket'); const {Logger} = require(`nylas-core`); -global.Promise = require('bluebird'); global.Logger = Logger.createLogger('nylas-k2-dashboard') const server = new Hapi.Server(); diff --git a/packages/nylas-message-processor/app.js b/packages/nylas-message-processor/app.js index 4ad7aee14..73e4950f5 100644 --- a/packages/nylas-message-processor/app.js +++ b/packages/nylas-message-processor/app.js @@ -1,7 +1,6 @@ const {PubsubConnector, DatabaseConnector, Logger} = require(`nylas-core`) const {processors} = require('./processors') -global.Promise = require('bluebird'); global.Logger = Logger.createLogger('nylas-k2-message-processor') // List of the attributes of Message that the processor should be allowed to change. diff --git a/packages/nylas-sync/app.js b/packages/nylas-sync/app.js index 16bf4509f..03b04d92d 100644 --- a/packages/nylas-sync/app.js +++ b/packages/nylas-sync/app.js @@ -1,5 +1,4 @@ // require('newrelic'); -global.Promise = require('bluebird'); const {DatabaseConnector, Logger} = require(`nylas-core`) const SyncProcessManager = require('./sync-process-manager'); diff --git a/packages/nylas-sync/imap/fetch-messages-in-folder.js b/packages/nylas-sync/imap/fetch-messages-in-folder.js index 038ae281b..2f3be4cbe 100644 --- a/packages/nylas-sync/imap/fetch-messages-in-folder.js +++ b/packages/nylas-sync/imap/fetch-messages-in-folder.js @@ -1,7 +1,7 @@ const _ = require('underscore'); const Imap = require('imap'); -const {IMAPConnection, PubsubConnector} = require('nylas-core'); +const {IMAPConnection, PubsubConnector, PromiseUtils} = require('nylas-core'); const {Capabilities} = IMAPConnection; const MessageFlagAttributes = ['id', 'folderImapUID', 'unread', 'starred', 'folderImapXGMLabels'] @@ -113,12 +113,12 @@ class FetchMessagesInFolder { const {Message} = this._db; const removedUIDs = localMessageAttributes - .filter(msg => !remoteUIDAttributes[msg.folderImapUID]) - .map(msg => msg.folderImapUID) + .filter(msg => !remoteUIDAttributes[msg.folderImapUID]) + .map(msg => msg.folderImapUID) - this._logger.info({ - removed_messages: removedUIDs.length, - }, `FetchMessagesInFolder: found messages no longer in the folder`) + this._logger.info({ + removed_messages: removedUIDs.length, + }, `FetchMessagesInFolder: found messages no longer in the folder`) if (removedUIDs.length === 0) { return Promise.resolve(); @@ -176,7 +176,7 @@ class FetchMessagesInFolder { uidsByPart[key].push(attributes.uid); }) .then(() => { - return Promise.each(Object.keys(uidsByPart), (key) => { + return PromiseUtils.each(Object.keys(uidsByPart), (key) => { const uids = uidsByPart[key]; const desiredParts = JSON.parse(key); const bodies = ['HEADER'].concat(desiredParts.map(p => p.id)); @@ -329,7 +329,7 @@ class FetchMessagesInFolder { } } - return Promise.each(desiredRanges, ({min, max}) => { + return PromiseUtils.each(desiredRanges, ({min, max}) => { this._logger.info({ range: `${min}:${max}`, }, `FetchMessagesInFolder: Fetching range`); @@ -343,7 +343,8 @@ class FetchMessagesInFolder { timeFetchedUnseen: Date.now(), }); }) - }).then(() => { + }) + .then(() => { this._logger.info(`FetchMessagesInFolder: Fetching messages finished`); }); } diff --git a/packages/nylas-sync/sync-process-manager.js b/packages/nylas-sync/sync-process-manager.js index d1ee98c9f..c7eb423cc 100644 --- a/packages/nylas-sync/sync-process-manager.js +++ b/packages/nylas-sync/sync-process-manager.js @@ -1,6 +1,6 @@ const os = require('os'); const SyncWorker = require('./sync-worker'); -const {DatabaseConnector, PubsubConnector, SchedulerUtils} = require(`nylas-core`) +const {DatabaseConnector, PubsubConnector, SchedulerUtils, PromiseUtils} = require(`nylas-core`) const IDENTITY = `${os.hostname()}-${process.pid}`; @@ -110,12 +110,13 @@ class SyncProcessManager { this._logger.info("ProcessManager: Starting unassignment for processes missing heartbeats.") - Promise.each(client.keysAsync(`${ACCOUNTS_CLAIMED_PREFIX}*`), (key) => { + PromiseUtils.each(client.keysAsync(`${ACCOUNTS_CLAIMED_PREFIX}*`), (key) => { const id = key.replace(ACCOUNTS_CLAIMED_PREFIX, ''); return client.existsAsync(HEARTBEAT_FOR(id)).then((exists) => (exists ? Promise.resolve() : this.unassignAccountsAssignedTo(id)) ) - }).finally(() => { + }) + .finally(() => { const delay = HEARTBEAT_EXPIRES * 1000; setTimeout(() => this.unassignAccountsMissingHeartbeats(), delay); }); @@ -165,7 +166,7 @@ class SyncProcessManager { // If we've added an account, wait a second before asking for another one. // Spacing them out is probably healthy. - return Promise.delay(2000); + return PromiseUtils.sleep(2000); }); }