UNPKG

cloudcms-server

Version:
381 lines (289 loc) 10.2 kB
var AbstractProvider = require("./abstract"); var async = require("async"); /** * Abstract class for an Awareness Provider that uses Asynchronous callbacks. * * This provides implementation methods for the AbstractProvider that call into smaller, abstract methods that are * easy to extend for a variety of cases. This is the foundation class for the Memory and Redis Awareness Providers. */ class AbstractAsyncProvider extends AbstractProvider { constructor(config) { super(config); } _lock(lockKey, workFunction) { process.locks.lock(lockKey, workFunction); } readOrCreateChannel(channelId, callback) { var self = this; self.readChannel(channelId, function(err, channel) { if (err) { return callback(err); } if (channel) { return callback(null, channel); } var channel = {}; self.writeChannel(channelId, channel, function(err) { if (err) { return callback(err); } callback(null, channel); }); }); } // IMPLEMENT ABSTRACT INTERFACE METHODS register(channelId, user, callback) { var self = this; self.doRegister(channelId, user, function(err) { callback(err); }); } /** * Workhorse method. * * @param channelId * @param user * @param callback * @private */ doRegister(channelId, user, callback) { var self = this; self.readOrCreateChannel(channelId, function (err, channel) { if (err) { return callback(err); } if (!channel.users) { channel.users = {}; } channel.users[user.id] = { "user": user, "time": Date.now() }; self.writeChannel(channelId, channel, function (err) { callback(err); }); }); } discover(channelId, callback) { var self = this; self.readChannel(channelId, function (err, channel) { if (err) { return callback(err); } if (!channel) { return callback(null, []); } var array = []; if (channel.users) { for (var userId in channel.users) { var entry = channel.users[userId]; if (entry && entry.user) { array.push(entry.user); } } } callback(null, array); }); } checkRegistered(channelId, userId, callback) { var self = this; self.readChannel(channelId, function(err, channel) { if (err) { return callback(err); } if (!channel) { return callback(null, false); } if (!channel.users) { return callback(null, false); } if (channel.users[userId]) { return callback(null, true); } callback(null, false); }); } expire(beforeMs, callback) { var self = this; self.listChannelIds(function(err, channelIds) { if (err) { return callback(err); } if (!channelIds || channelIds.length === 0) { return callback(null, [], {}); } // a list of channel IDs whose memberships were updated var updatedMembershipChannelIds = []; var expiredUserIdsByChannelId = {}; var fns = []; for (var i = 0; i < channelIds.length; i++) { var channelId = channelIds[i]; var fn = function (channelId, updatedMembershipChannelIds, expiredUserIdsByChannelId, beforeMs) { return function (done) { self.readChannel(channelId, function(err, channel) { if (err) { return done(err); } if (!channel) { return done(); } if (!channel.users || Object.keys(channel.users).length === 0) { return done(); } // populate all of the user IDs that need to be removed var userIdsToRemove = []; for (var userId in channel.users) { var entry = channel.users[userId]; if (entry.time < beforeMs) { updatedMembershipChannelIds.push(channelId); userIdsToRemove.push(userId); var expiredUserIds = expiredUserIdsByChannelId[channelId] if (!expiredUserIds) { expiredUserIds = expiredUserIdsByChannelId[channelId] = []; } expiredUserIds.push(userId); } } // remove the user IDs for (var i = 0; i < userIdsToRemove.length; i++) { delete channel.users[userIdsToRemove[i]]; } self.writeChannel(channelId, channel, function() { done(); }); }); }; }(channelId, updatedMembershipChannelIds, expiredUserIdsByChannelId, beforeMs); fns.push(fn); } async.parallel(fns, function(err) { if (err) { return callback(err); } callback(null, updatedMembershipChannelIds, expiredUserIdsByChannelId); }); }); }; acquireLock(channelId, user, callback) { var self = this; self.readLock(channelId, function(err, lock) { if (err) { return callback(err); } if (lock) { return callback({ "message": "The channel: " + channelId + " is already locked by: " + lock.user.id }); } lock = { "user": user, "lockTime": Date.now() }; self.writeLock(channelId, lock, function(err) { if (err) { return callback(err); } // callback with true to indicate success callback(null, true); }); }); } releaseLock(channelId, userId, callback) { var self = this; self.readLock(channelId, function(err, lock) { if (err) { return callback(err); } if (!lock) { return callback({ "message": "A lock does not currently exist for channel: " + channelId }); } if (lock.user.id !== userId) { return callback({ "message": "The channel: " + channelId + " has a lock but it is not owned by: " + userId }); } self.deleteLock(channelId, function(err) { if (err) { return callback(err); } // callback with true to indicate success callback(null, true); }); }); } lockInfo(channelId, callback) { var self = this; self.readLock(channelId, function(err, lock) { if (err) { return callback(err); } callback(null, lock); }); } ////////////////////////////////////////////////////////////////////////////////////////////////////////// // // ABSTRACT METHODS // ////////////////////////////////////////////////////////////////////////////////////////////////////////// // ABSTRACT readChannel(channelId, callback) { throw new Error("readChannel() method is not implemented"); } // ABSTRACT writeChannel(channelId, channel, callback) { throw new Error("writeChannel() method is not implemented"); } // ABSTRACT listChannelIds(callback) { throw new Error("listChannelIds() method is not implemented"); } // ABSTRACT readLock(lockId, callback) { throw new Error("readLock() method is not implemented"); } // ABSTRACT writeLock(lockId, lock, callback) { throw new Error("writeLock() method is not implemented"); } // ABSTRACT deleteLock(lockId, callback) { throw new Error("deleteLock() method is not implemented"); } // ABSTRACT listLockIds(callback) { throw new Error("listLockIds() method is not implemented"); } } module.exports = AbstractAsyncProvider;