imports/server/exportimport/expImpUsers.js
const fs = require('fs');
const EJSON = require('mongodb-extended-json');
class ExpImpUsers {
static get FILENAME_POSTFIX() {
return '_users.json';
}
static get MAPNAME_POSTFIX() {
return '_userMap.json';
}
static searchUser(allUserDoc, searchID) {
for (let i=0; i < allUserDoc.length; i++) {
if (allUserDoc[i]._id === searchID) {
return allUserDoc[i];
}
}
throw Error('Could not find user with _id:'+searchID);
}
static doExport (db, msID, userIDsFlat) {
return new Promise((resolve, reject) => {
userIDsFlat = Object.keys(userIDsFlat);
let userIDsOuputMap = {};
db.collection('users')
.find({ _id : { $in : userIDsFlat } })
.toArray()
.then(allUsersDoc => {
if (allUsersDoc) {
// userIDsFlat may contain "free text" users that are not in DB
// We create a dict to look up which collected userIDs are really from DB
let userIDsFromDB = {};
allUsersDoc.map(usr => {
userIDsFromDB[usr._id] = 1;
});
const usrFile = msID + ExpImpUsers.FILENAME_POSTFIX;
fs.writeFileSync(usrFile, EJSON.stringify(allUsersDoc,null,2));
console.log('Saved: '+usrFile + ' with '+allUsersDoc.length+' users');
// Save mapping file old => new user ID
// But only with REAL DB users (skip free text users)
userIDsFlat.map(usrID => {
if (userIDsFromDB[usrID]) {
// default: newID === oldID
// This means, users are copied(!) from source DB to destination DB
// If newID is changed to an existing id from destination ID, this target user is used
let thisUser = ExpImpUsers.searchUser(allUsersDoc, usrID);
userIDsOuputMap[usrID] = {
'newID': usrID,
'hint': thisUser.username +' '+thisUser.profile.name
};
}
});
const mapFile = msID + ExpImpUsers.MAPNAME_POSTFIX;
fs.writeFileSync(mapFile, JSON.stringify(userIDsOuputMap,null,2));
console.log('Saved: '+mapFile);
console.log(' *** IMPORTANT!!! EDIT USER MAP FILE BEFORE IMPORT!!!');
resolve(db);
} else {
return reject ('Could not find users: ', userIDsFlat);
}
});
});
}
static preImportCheck (db, msID) {
return new Promise((resolve, reject) => {
const mapFile = msID + ExpImpUsers.MAPNAME_POSTFIX;
let usrMap = undefined;
try {
usrMap = JSON.parse(fs.readFileSync(mapFile, 'utf8'));
if (!usrMap) {
return reject('Could not read user map file '+mapFile);
}
} catch (e) {
return reject('Could not read user map file '+mapFile + '\n'+e);
}
let usrMapSimple = {}; // make flat map: oldID => newID
usrMap = Object.keys(usrMap).map(key => {
usrMapSimple[key] = usrMap[key].newID;
});
usrMap = usrMapSimple;
let usrMapCount = Object.keys(usrMap).length;
console.log('Found '+usrMapCount+' users in '+mapFile);
let usrMapTargetIDs = [];
let usrCopyIDs = [];
Object.keys(usrMap).map(key => {
if (key !== usrMap[key]) { // key/value different...
usrMapTargetIDs.push(usrMap[key]); // map to existing user
} else {
usrCopyIDs.push(key); // copy user from export DB => import DB
}
});
// Check#1: All "link targets" should exist
db.collection('users')
.find({ _id : { $in : usrMapTargetIDs } })
.toArray()
.then(doc => {
if (doc) {
console.log('Found '+doc.length + ' target users in current user DB.');
console.log('Will copy over '+usrCopyIDs.length + ' export users to current user DB.');
if (doc.length !== usrMapTargetIDs.length) {
return reject ('Not all to-be patched target users found in current user DB: '+usrMapTargetIDs);
}
// Check#2: All copy-users MUST NOT exist!
db.collection('users')
.find({ _id : { $in : usrCopyIDs } })
.toArray()
.then(shouldBeEmpty => {
if (shouldBeEmpty && shouldBeEmpty.length > 0) {
let errorUsers = shouldBeEmpty.map(usr => {return {_id: usr._id, username: usr.username};});
return reject (shouldBeEmpty.length+' to-be copied user(s) already exists:\n'+JSON.stringify(errorUsers));
} else {
resolve({db, usrMap});
}
});
} else {
return reject ('Could not find users: ', usrMapTargetIDs);
}
});
});
}
static doImport (db, msID, usrMap) {
return new Promise((resolve, reject) => {
const usrFile = msID + ExpImpUsers.FILENAME_POSTFIX;
let allUsersDoc = undefined;
try {
allUsersDoc = EJSON.parse(fs.readFileSync(usrFile, 'utf8'));
if (!allUsersDoc) {
return reject('Could not read user file '+usrFile);
}
} catch (e) {
return reject('Could not read user file '+usrFile+'\n'+e);
}
// We have some sequential DB inserts/updates from two cases now.
// We chain them in a Promise chain.
let promiseChain = [];
for(let u=0; u<allUsersDoc.length; u++) {
if (allUsersDoc[u]._id === usrMap[allUsersDoc[u]._id]) { // before/after ID are same in mapping file!
let roleValueForMS = allUsersDoc[u].roles[msID]; // Case#1: clone this user from source DB => target DB!
allUsersDoc[u].roles = {msID: roleValueForMS}; // Kill all other roles, just keep the one for this MS
promiseChain.push(
db.collection('users')
.insert(allUsersDoc[u]));
} else {
promiseChain.push( // Case#2: only update user role for existing user in target DB
db.collection('users')
.findOne({'_id': usrMap[allUsersDoc[u]._id]}) // find the user in target DB
.then(function (usr) {
let roleValueForMS = allUsersDoc[u].roles[msID];
if (roleValueForMS && roleValueForMS.length > 0) { // user needs role for import meeting series?
let roles = usr.roles ? usr.roles : {};
roles[msID] = roleValueForMS;
return db.collection('users') // upsert role field
.update({_id: usr._id}, {$set: {roles: roles}});
}
}));
}
}
// Now execute the chain.
Promise.all(promiseChain)
.then(function (res) {
if (res && res[0] && res[0].result && !res[0].result.ok) {
console.log('Promisechain result: ', res);
}
resolve(db);
})
.catch(function (err) {
reject(err);
});
});
}
}
module.exports = ExpImpUsers;