From c893effcb9306e47d74fcd1c05558faabc730225 Mon Sep 17 00:00:00 2001 From: Julian Lam Date: Wed, 19 Mar 2014 20:33:39 -0400 Subject: [PATCH] WIP commit -- groups refactoring + upgrade scripts --- src/groups.js | 135 ++++++++++++++++++++++++-------------------- src/routes/debug.js | 9 ++- src/upgrade.js | 75 +++++++++++++++++++++++- src/user/delete.js | 10 +--- 4 files changed, 158 insertions(+), 71 deletions(-) diff --git a/src/groups.js b/src/groups.js index ddcea8801d..933c4d0846 100644 --- a/src/groups.js +++ b/src/groups.js @@ -2,29 +2,33 @@ (function(Groups) { + /* REMOVED + Groups.getGidFromName + Groups.joinByGroupName + Groups.leaveByGroupName + */ + var async = require('async'), winston = require('winston'), user = require('./user'), db = require('./database'); - Groups.getGroupIds = function (callback) { - db.getObjectValues('group:gid', callback); - }; - Groups.list = function(options, callback) { - db.getObjectValues('group:gid', function (err, gids) { - if (gids.length > 0) { - async.map(gids, function (gid, next) { - Groups.get(gid, options, next); + db.getSetMembers('groups', function (err, groupNames) { + if (groupNames.length > 0) { + async.map(groupNames, function (groupName, next) { + Groups.get(groupName, options, next); }, function (err, groups) { // Remove system, hidden, or deleted groups from this list - groups = groups.filter(function (group) { - if (group.deleted || (group.hidden && !group.system) || (!options.showSystemGroups && group.system)) { - return false; - } else { - return true; - } - }); + if (!options.showAllGroups) { + groups = groups.filter(function (group) { + if (group.deleted || (group.hidden && !group.system) || (!options.showSystemGroups && group.system)) { + return false; + } else { + return true; + } + }); + } callback(err, groups); }); @@ -34,16 +38,16 @@ }); }; - Groups.get = function(gid, options, callback) { + Groups.get = function(groupName, options, callback) { var truncated = false, numUsers; async.parallel({ base: function (next) { - db.getObject('gid:' + gid, next); + db.getObject('group:' + groupName, next); }, users: function (next) { - db.getSetMembers('gid:' + gid + ':members', function (err, uids) { + db.getSetMembers('group:' + groupName + ':members', function (err, uids) { if (err) { return next(err); } @@ -82,6 +86,7 @@ }); }; + // Not checked Groups.getByGroupName = function(groupName, options, callback) { Groups.getGidFromName(groupName, function(err, gid) { if (err || !gid) { @@ -92,6 +97,7 @@ }); }; + // Not checked Groups.getMemberships = function(uid, callback) { if (!uid) { return callback(new Error('no-uid-specified')); @@ -110,26 +116,18 @@ }); }; + // Not checked Groups.isDeleted = function(gid, callback) { db.getObjectField('gid:' + gid, 'deleted', function(err, deleted) { callback(err, parseInt(deleted, 10) === 1); }); }; - Groups.getGidFromName = function(name, callback) { - db.getObjectField('group:gid', name, callback); - }; - - Groups.isMember = function(uid, gid, callback) { - Groups.isDeleted(gid, function(err, deleted) { - if (!deleted) { - db.isSetMember('gid:' + gid + ':members', uid, callback); - } else { - callback(err, false); - } - }); + Groups.isMember = function(uid, groupName, callback) { + db.isSetMember('group:' + groupName + ':members', uid, callback); }; + // Not checked Groups.isMemberByGroupName = function(uid, groupName, callback) { Groups.getGidFromName(groupName, function(err, gid) { if (err || !gid) { @@ -142,6 +140,7 @@ }); }; + // Not checked Groups.isMemberOfGroupAny = function(uid, groupListKey, callback) { Groups.getGidFromName(groupListKey, function(err, gid) { if (err || !gid) { @@ -164,12 +163,14 @@ }); }; + // Not checked Groups.isEmpty = function(gid, callback) { db.setCount('gid:' + gid + ':members', function(err, numMembers) { callback(err, numMembers === 0); }); }; + // Not checked Groups.isEmptyByGroupName = function(groupName, callback) { Groups.getGidFromName(groupName, function(err, gid) { if (err || !gid) { @@ -180,6 +181,7 @@ }); }; + // Not checked Groups.exists = function(name, callback) { async.parallel({ exists: function(next) { @@ -195,6 +197,7 @@ }); }; + // Not checked Groups.create = function(name, description, callback) { if (name.length === 0) { return callback(new Error('name-too-short')); @@ -231,12 +234,14 @@ }); }; + // Not checked Groups.hide = function(gid, callback) { Groups.update(gid, { hidden: '1' }, callback); }; + // Not checked Groups.update = function(gid, values, callback) { db.exists('gid:' + gid, function (err, exists) { if (!err && exists) { @@ -268,47 +273,57 @@ }); }; - Groups.destroy = function(gid, callback) { - db.setObjectField('gid:' + gid, 'deleted', '1', callback); + Groups.destroy = function(groupName, callback) { + async.parallel([ + function(next) { + db.delete('group:' + groupName, next); + }, + function(next) { + db.setRemove('groups', groupName, next); + } + ], callback); }; - Groups.join = function(gid, uid, callback) { - db.setAdd('gid:' + gid + ':members', uid, callback); + Groups.join = function(groupName, uid, callback) { + db.setAdd('group:' + groupName + ':members', uid, callback); }; - Groups.joinByGroupName = function(groupName, uid, callback) { - Groups.getGidFromName(groupName, function(err, gid) { - if (err || !gid) { - Groups.create(groupName, '', function(err, groupObj) { - async.parallel([ - function(next) { - Groups.hide(groupObj.gid, next); - }, - function(next) { - Groups.join(groupObj.gid, uid, next); - } - ], callback); - }); - } else { - Groups.join(gid, uid, callback); + Groups.leave = function(groupName, uid, callback) { + db.setRemove('group:' + groupName + ':members', uid, function(err) { + if (err) { + return callback(err); } - }); - }; - Groups.leave = function(gid, uid, callback) { - db.setRemove('gid:' + gid + ':members', uid, callback); + // If this is a system group, and it is now empty, delete it + Groups.get(groupName, function(err, group) { + if (err) { + return callback(err); + } + + if (group.system && group.memberCount === 0) { + Groups.destroy(groupName, callback); + } else { + return callback(); + } + }); + }); }; - Groups.leaveByGroupName = function(groupName, uid, callback) { - Groups.getGidFromName(groupName, function(err, gid) { - if (err || !gid) { - callback(new Error('gid-not-found')); - } else { - Groups.leave(gid, uid, callback); - } + Groups.leaveAllGroups = function(uid, callback) { + db.getSetMembers('groups', function(err, groups) { + async.each(groups, function(groupName, next) { + Groups.isMember(uid, groupName, function(err, isMember) { + if (!err && isMember) { + Groups.leave(groupName, uid, next); + } else { + next(); + } + }); + }); }); }; + // Not checked Groups.prune = function(callback) { // Actually deletes groups (with the deleted flag) from the redis database db.getObjectValues('group:gid', function (err, gids) { diff --git a/src/routes/debug.js b/src/routes/debug.js index fd9e7232a8..0e9dc0920f 100644 --- a/src/routes/debug.js +++ b/src/routes/debug.js @@ -54,7 +54,14 @@ module.exports = function(app, middleware, controllers) { }); app.get('/test', function(req, res) { - res.send(200); + var groups = require('../groups'); + + groups.list({ + showAllGroups: true + }, function(err, groups) { + res.json(200, groups); + }); + // res.send(200); }); }); }; \ No newline at end of file diff --git a/src/upgrade.js b/src/upgrade.js index 680aa700b5..7045090689 100644 --- a/src/upgrade.js +++ b/src/upgrade.js @@ -19,7 +19,7 @@ var db = require('./database'), schemaDate, thisSchemaDate, // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema - latestSchema = Date.UTC(2014, 2, 19); + latestSchema = Date.UTC(2014, 2, 19, 20); Upgrade.check = function(callback) { db.get('schemaDate', function(err, value) { @@ -355,6 +355,79 @@ Upgrade.upgrade = function(callback) { winston.info('[2014/3/19] Setting "system" flag for system groups - skipped'); next(); } + }, + function(next) { + thisSchemaDate = Date.UTC(2014, 2, 19, 20); + + if (schemaDate < thisSchemaDate) { + db.getObject('group:gid', function(err, mapping) { + if (err) { + return next(err); + } + + if (!err && !mapping) { + // Done already, skip + return next(); + } + + var names = Object.keys(mapping); + async.each(names, function(name, next) { + async.series([ + function(next) { + // Remove the gid from the hash + db.deleteObjectField('gid:' + mapping[name], 'gid', next); + }, + function(next) { + db.rename('gid:' + mapping[name], 'group:' + name, next); + }, + function(next) { + db.exists('gid:' + mapping[name] + ':members', function(err, exists) { + if (err) { + return next(err); + } + + if (exists) { + db.rename('gid:' + mapping[name] + ':members', 'group:' + name + ':members', next); + } else { + // No members, do nothing + next(); + } + }); + }, + function(next) { + // Add groups to a directory (set) + db.setAdd('groups', name, next); + } + ], next); + }, function(err) { + // Delete the old mapping key + async.series([ + function(next) { + db.delete('group:gid', next); + }, + function(next) { + // Delete empty groups + Groups.list({ showAllGroups: true }, function(err, groups) { + async.each(groups, function(group, next) { + if (group.members.length === 0) { + // Delete the group + Groups.destroy(group.name, next); + } else { + next(); + } + }, next); + }); + } + ], function(err) { + console.log('so far so good'); + process.exit(); + }); + }); + }); + } else { + winston.info('[2014/3/19] Removing gids and pruning groups - skipped'); + next(); + } } // Add new schema updates here // IMPORTANT: REMEMBER TO UPDATE VALUE OF latestSchema IN LINE 22!!! diff --git a/src/user/delete.js b/src/user/delete.js index 1a8036e837..e73c47ae72 100644 --- a/src/user/delete.js +++ b/src/user/delete.js @@ -275,7 +275,7 @@ module.exports = function(User) { deleteUserFromFollowers(uid, next); }, function(next) { - deleteUserFromGroups(uid, next); + groups.leaveAllGroups(uid, next); } ], function(err) { if (err) { @@ -320,12 +320,4 @@ module.exports = function(User) { }, callback); }); } - - function deleteUserFromGroups(uid, callback) { - groups.getGroupIds(function(err, gids) { - async.each(gids, function(gid, next) { - groups.leave(gid, uid, next); - }, callback); - }); - } }; \ No newline at end of file