etherpad-lite/src/node/db/GroupManager.js
muxator 11453d544c prepare to async: stricter checks
This change is in preparation of the future async refactoring by Ray. It tries
to extract as many changes in boolean conditions as possible, in order to make
more evident identifying eventual logic bugs in the future work.

This proved already useful in at least one case.

BEWARE: this commit exposes an incoherency in the DB API, in which, depending
on the driver used, some functions can return null or undefined. This condition
will be externally fixed by the final commit in this series ("db/DB.js: prevent
DB layer from returning undefined"). Until that commit, the code base may have
some bugs.
2019-03-01 09:43:41 +01:00

322 lines
8.1 KiB
JavaScript

/**
* The Group Manager provides functions to manage groups in the database
*/
/*
* 2011 Peter 'Pita' Martischka (Primary Technology Ltd)
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS-IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var ERR = require("async-stacktrace");
var customError = require("../utils/customError");
var randomString = require('ep_etherpad-lite/static/js/pad_utils').randomString;
var db = require("./DB").db;
var async = require("async");
var padManager = require("./PadManager");
var sessionManager = require("./SessionManager");
exports.listAllGroups = function(callback) {
db.get("groups", function (err, groups) {
if (ERR(err, callback)) return;
if (groups == null) {
// there are no groups
callback(null, {groupIDs: []});
return;
}
var groupIDs = [];
for (var groupID in groups) {
groupIDs.push(groupID);
}
callback(null, {groupIDs: groupIDs});
});
}
exports.deleteGroup = function(groupID, callback)
{
var group;
async.series([
// ensure group exists
function (callback) {
// try to get the group entry
db.get("group:" + groupID, function (err, _group) {
if (ERR(err, callback)) return;
if (_group == null) {
// group does not exist
callback(new customError("groupID does not exist", "apierror"));
return;
}
// group exists, everything is fine
group = _group;
callback();
});
},
// iterate through all pads of this group and delete them
function(callback) {
// collect all padIDs in an array, that allows us to use async.forEach
var padIDs = [];
for(var i in group.pads) {
padIDs.push(i);
}
// loop through all pads and delete them
async.forEach(padIDs, function(padID, callback) {
padManager.getPad(padID, function(err, pad) {
if (ERR(err, callback)) return;
pad.remove(callback);
});
}, callback);
},
// iterate through group2sessions and delete all sessions
function(callback)
{
// try to get the group entry
db.get("group2sessions:" + groupID, function (err, group2sessions) {
if (ERR(err, callback)) return;
// skip if there is no group2sessions entry
if (group2sessions == null) { callback(); return }
// collect all sessions in an array, that allows us to use async.forEach
var sessions = [];
for (var i in group2sessions.sessionsIDs) {
sessions.push(i);
}
// loop through all sessions and delete them
async.forEach(sessions, function(session, callback) {
sessionManager.deleteSession(session, callback);
}, callback);
});
},
// remove group and group2sessions entry
function(callback) {
db.remove("group2sessions:" + groupID);
db.remove("group:" + groupID);
callback();
},
// unlist the group
function(callback) {
exports.listAllGroups(function(err, groups) {
if (ERR(err, callback)) return;
groups = groups? groups.groupIDs : [];
if (groups.indexOf(groupID) === -1) {
// it's not listed
callback();
return;
}
// remove from the list
groups.splice(groups.indexOf(groupID), 1);
// store empty group list
if (groups.length == 0) {
db.set("groups", {});
callback();
return;
}
// regenerate group list
var newGroups = {};
async.forEach(groups, function(group, cb) {
newGroups[group] = 1;
cb();
},
function() {
db.set("groups", newGroups);
callback();
});
});
}
],
function(err) {
if (ERR(err, callback)) return;
callback();
});
}
exports.doesGroupExist = function(groupID, callback)
{
// try to get the group entry
db.get("group:" + groupID, function (err, group) {
if (ERR(err, callback)) return;
callback(null, group != null);
});
}
exports.createGroup = function(callback)
{
// search for non existing groupID
var groupID = "g." + randomString(16);
// create the group
db.set("group:" + groupID, {pads: {}});
// list the group
exports.listAllGroups(function(err, groups) {
if (ERR(err, callback)) return;
groups = groups? groups.groupIDs : [];
groups.push(groupID);
// regenerate group list
var newGroups = {};
async.forEach(groups, function(group, cb) {
newGroups[group] = 1;
cb();
},
function() {
db.set("groups", newGroups);
callback(null, {groupID: groupID});
});
});
}
exports.createGroupIfNotExistsFor = function(groupMapper, callback)
{
// ensure mapper is optional
if (typeof groupMapper !== "string") {
callback(new customError("groupMapper is not a string", "apierror"));
return;
}
// try to get a group for this mapper
db.get("mapper2group:" + groupMapper, function(err, groupID) {
function createGroupForMapper(cb) {
exports.createGroup(function(err, responseObj) {
if (ERR(err, cb)) return;
// create the mapper entry for this group
db.set("mapper2group:" + groupMapper, responseObj.groupID);
cb(null, responseObj);
});
}
if (ERR(err, callback)) return;
if (groupID) {
// there is a group for this mapper
exports.doesGroupExist(groupID, function(err, exists) {
if (ERR(err, callback)) return;
if (exists) return callback(null, {groupID: groupID});
// hah, the returned group doesn't exist, let's create one
createGroupForMapper(callback)
});
return;
}
// there is no group for this mapper, let's create a group
createGroupForMapper(callback)
});
}
exports.createGroupPad = function(groupID, padName, text, callback)
{
// create the padID
var padID = groupID + "$" + padName;
async.series([
// ensure group exists
function (callback) {
exports.doesGroupExist(groupID, function(err, exists) {
if (ERR(err, callback)) return;
if (!exists) {
// group does not exist
callback(new customError("groupID does not exist", "apierror"));
return;
}
// group exists, everything is fine
callback();
});
},
// ensure pad doesn't exist already
function (callback) {
padManager.doesPadExists(padID, function(err, exists) {
if (ERR(err, callback)) return;
if (exists == true) {
// pad exists already
callback(new customError("padName does already exist", "apierror"));
return;
}
// pad does not exist, everything is fine
callback();
});
},
// create the pad
function (callback) {
padManager.getPad(padID, text, function(err) {
if (ERR(err, callback)) return;
callback();
});
},
// create an entry in the group for this pad
function (callback) {
db.setSub("group:" + groupID, ["pads", padID], 1);
callback();
}
],
function(err) {
if (ERR(err, callback)) return;
callback(null, {padID: padID});
});
}
exports.listPads = function(groupID, callback)
{
exports.doesGroupExist(groupID, function(err, exists) {
if (ERR(err, callback)) return;
// ensure the group exists
if (!exists) {
callback(new customError("groupID does not exist", "apierror"));
return;
}
// group exists, let's get the pads
db.getSub("group:" + groupID, ["pads"], function(err, result) {
if (ERR(err, callback)) return;
var pads = [];
for ( var padId in result ) {
pads.push(padId);
}
callback(null, {padIDs: pads});
});
});
}