@dhyasama/totem-merge 6.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -0
- package/cli.js +154 -0
- package/config.js +23 -0
- package/index.js +109 -0
- package/lib/changeBSON.js +22 -0
- package/lib/helpers.js +117 -0
- package/lib/organizations.js +408 -0
- package/lib/people.js +739 -0
- package/package.json +30 -0
- package/test/config.js +17 -0
- package/test/index.js +1159 -0
|
@@ -0,0 +1,408 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
let async = require('async');
|
|
4
|
+
let _ = require('underscore');
|
|
5
|
+
let helpers = require('./helpers');
|
|
6
|
+
let mongoose = require('mongoose');
|
|
7
|
+
let ObjectId = mongoose.Types.ObjectId;
|
|
8
|
+
let logger;
|
|
9
|
+
|
|
10
|
+
var merge = function merge(context, target, source, mergedBy, customerId, cb) {
|
|
11
|
+
|
|
12
|
+
logger = context.logger;
|
|
13
|
+
|
|
14
|
+
logger.writeToInfoLog('Merge organizations...');
|
|
15
|
+
|
|
16
|
+
// validate parameters
|
|
17
|
+
if (!context) return cb(new Error('context is required'), null);
|
|
18
|
+
if (!target) return cb(new Error('target is required'), null);
|
|
19
|
+
if (!source) return cb(new Error('source is required'), null);
|
|
20
|
+
if (!mergedBy) return cb(new Error('mergedBy is required'), null);
|
|
21
|
+
if (!cb) throw new Error('cb is required');
|
|
22
|
+
|
|
23
|
+
var saveOrganization = function(org, username, callback) {
|
|
24
|
+
|
|
25
|
+
logger.writeToInfoLog('Save target organization...');
|
|
26
|
+
logger.writeToInfoLog(' ID: ' + org._id.toString());
|
|
27
|
+
logger.writeToInfoLog(' LinkedIn: ' + org.social.linkedin);
|
|
28
|
+
logger.writeToInfoLog(' Twitter: ' + org.social.twitter);
|
|
29
|
+
logger.writeToInfoLog(' Domains: ' + org.websites);
|
|
30
|
+
|
|
31
|
+
context.Organization.upsert(org, username, callback);
|
|
32
|
+
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
if (context.flagOnly) {
|
|
36
|
+
|
|
37
|
+
var url = 'https://admin.ffvc.com/orgs/' + source.id;
|
|
38
|
+
var text = 'This org should be merged with ' + url;
|
|
39
|
+
|
|
40
|
+
context.Organization.addFlag(target._id, target._id, text, customerId, function(err, result) {
|
|
41
|
+
if (err) return cb(err, null);
|
|
42
|
+
return cb(null, result);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
}
|
|
46
|
+
else {
|
|
47
|
+
|
|
48
|
+
// mergedBy can be either an account or a username
|
|
49
|
+
var username = mergedBy.username ? mergedBy.username : mergedBy;
|
|
50
|
+
logger.writeToInfoLog('Username: ' + username);
|
|
51
|
+
|
|
52
|
+
async.series([
|
|
53
|
+
|
|
54
|
+
verifyOkToMerge.bind(null, target, source),
|
|
55
|
+
|
|
56
|
+
// Create a snapshot of the target
|
|
57
|
+
helpers.createSnapshot.bind(null, context, 'Organization', target, username),
|
|
58
|
+
|
|
59
|
+
// Create a snapshot of the source
|
|
60
|
+
helpers.createSnapshot.bind(null, context, 'Organization', source, username),
|
|
61
|
+
|
|
62
|
+
// Merge the source into the target
|
|
63
|
+
mergeSourceIntoTarget.bind(null, target, source),
|
|
64
|
+
|
|
65
|
+
// Delete the source
|
|
66
|
+
// Do this prior to saving merged target in order to avoid unique key issues
|
|
67
|
+
helpers.removeEntity.bind(null, context, source),
|
|
68
|
+
|
|
69
|
+
// Save the updated target
|
|
70
|
+
saveOrganization.bind(null, target, username),
|
|
71
|
+
|
|
72
|
+
// Change source references to target
|
|
73
|
+
updateReferences.bind(null, context, target, source, mergedBy, customerId)
|
|
74
|
+
|
|
75
|
+
], function(err, results) {
|
|
76
|
+
|
|
77
|
+
if (err) { return cb(err, null); }
|
|
78
|
+
|
|
79
|
+
// result from saveOrganization
|
|
80
|
+
var updatedTarget = results[5];
|
|
81
|
+
|
|
82
|
+
// result from updateOrganizations within updateReferences
|
|
83
|
+
var updatedReferencesOrgs = results[6][4];
|
|
84
|
+
|
|
85
|
+
// Check if target had reference(s) updated
|
|
86
|
+
var targetMatch = _.find(updatedReferencesOrgs, function(o) {
|
|
87
|
+
return o.id.toString() === target.id.toString();
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
// Return up to date target
|
|
91
|
+
if (targetMatch) { return cb(null, targetMatch); }
|
|
92
|
+
else { return cb(null, updatedTarget); }
|
|
93
|
+
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
var mergeSourceIntoTarget = function mergeEntities(target, source, cb) {
|
|
101
|
+
|
|
102
|
+
logger.writeToInfoLog('Merge source into target...');
|
|
103
|
+
|
|
104
|
+
var doNotMergeKeys = [
|
|
105
|
+
'merged',
|
|
106
|
+
'deleted',
|
|
107
|
+
'entered',
|
|
108
|
+
'__v'
|
|
109
|
+
];
|
|
110
|
+
|
|
111
|
+
helpers.mergeObjects(target, source, doNotMergeKeys);
|
|
112
|
+
|
|
113
|
+
// keep a list of copied orgs
|
|
114
|
+
target.merge(source.id);
|
|
115
|
+
|
|
116
|
+
// add copied org name as an alias if it's different from source name
|
|
117
|
+
if (target.name.toLowerCase() != source.name.toLowerCase()) {
|
|
118
|
+
target.aliases = target.aliases.concat([source.name]);
|
|
119
|
+
target.aliases = _.uniq(target.aliases);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Add source website as alias for target
|
|
123
|
+
target.websiteAliases = target.websiteAliases.concat(source.website);
|
|
124
|
+
target.websiteAliases = _.uniq(target.websiteAliases);
|
|
125
|
+
|
|
126
|
+
// mark as entered by system
|
|
127
|
+
target.entered = {
|
|
128
|
+
by: 'ff-merge',
|
|
129
|
+
on: new Date()
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
return cb(null, target);
|
|
133
|
+
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
var verifyOkToMerge = function verifyOkToMerge(target, source, cb) {
|
|
137
|
+
|
|
138
|
+
logger.writeToInfoLog('Verify ok to merge...');
|
|
139
|
+
|
|
140
|
+
// Cannot merge if target is a customer
|
|
141
|
+
if (target && target.customer && target.customer.totemUrl) { return cb(true, null); }
|
|
142
|
+
|
|
143
|
+
// Cannot merge if source is a customer
|
|
144
|
+
else if (source && source.customer && source.customer.totemUrl) { return cb(true, null); }
|
|
145
|
+
|
|
146
|
+
// Ok to merge
|
|
147
|
+
else { return cb(null, null); }
|
|
148
|
+
|
|
149
|
+
};
|
|
150
|
+
|
|
151
|
+
var updateReferences = function updateReferences(context, target, source, mergedBy, customerId, cb) {
|
|
152
|
+
|
|
153
|
+
logger.writeToInfoLog('Update references...');
|
|
154
|
+
|
|
155
|
+
// validate parameters
|
|
156
|
+
if (!context) return cb(new Error('context is required'), null);
|
|
157
|
+
if (!target) return cb(new Error('target is required'), null);
|
|
158
|
+
if (!source) return cb(new Error('source is required'), null);
|
|
159
|
+
if (!cb) throw new Error('cb is required');
|
|
160
|
+
|
|
161
|
+
var oldOrgId = ObjectId(source.id);
|
|
162
|
+
var newOrgId = ObjectId(target.id);
|
|
163
|
+
|
|
164
|
+
logger.writeToInfoLog(' Target: ' + newOrgId.toString());
|
|
165
|
+
logger.writeToInfoLog(' Source: ' + oldOrgId.toString());
|
|
166
|
+
|
|
167
|
+
// mergedBy can be either an account or a username
|
|
168
|
+
var username = mergedBy.username ? mergedBy.username : mergedBy;
|
|
169
|
+
|
|
170
|
+
// Do not update cap tables, financials or rounds as these are complicated
|
|
171
|
+
// and get cleaned and updated on each pull anyway
|
|
172
|
+
|
|
173
|
+
async.parallel([
|
|
174
|
+
updateDeals.bind(context, newOrgId, oldOrgId, mergedBy, customerId),
|
|
175
|
+
updateLists.bind(context, newOrgId, oldOrgId, username),
|
|
176
|
+
updateMessages.bind(context, newOrgId, oldOrgId, username),
|
|
177
|
+
updateNews.bind(context, newOrgId, oldOrgId, username),
|
|
178
|
+
updateOrganizations.bind(context, newOrgId, oldOrgId, username)
|
|
179
|
+
], cb)
|
|
180
|
+
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
var updateDeals = function updateDeals(newOrgId, oldOrgId, user, customerId, cb) {
|
|
184
|
+
|
|
185
|
+
logger.writeToInfoLog(' Updating deals...');
|
|
186
|
+
|
|
187
|
+
var self = this;
|
|
188
|
+
|
|
189
|
+
// Get all deals for this org
|
|
190
|
+
self.Deal.collection.find({ 'organization': oldOrgId }).toArray(function(err, items) {
|
|
191
|
+
|
|
192
|
+
if (err) return cb(err, null);
|
|
193
|
+
else if (!items || items.length === 0) return cb(null, []);
|
|
194
|
+
|
|
195
|
+
var tasks = [];
|
|
196
|
+
var updated = [];
|
|
197
|
+
|
|
198
|
+
_.each(items, function(item) {
|
|
199
|
+
tasks.push({
|
|
200
|
+
id: item._id,
|
|
201
|
+
update: { $set: { organization: newOrgId } }
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
logger.writeToInfoLog(' Found ' + tasks.length + ' deal references to update');
|
|
206
|
+
|
|
207
|
+
var queue = async.queue(function(task, callback) {
|
|
208
|
+
self.Deal.modifyById(task.id, task.update, { CUSTOMER_ID: customerId }, function(err, result) {
|
|
209
|
+
if (err) { return callback(err, null); }
|
|
210
|
+
else if (result) { updated.push(result); }
|
|
211
|
+
return callback(null, result);
|
|
212
|
+
});
|
|
213
|
+
}, 8);
|
|
214
|
+
|
|
215
|
+
queue.drain = function() { return cb(null, updated); };
|
|
216
|
+
|
|
217
|
+
queue.push(tasks);
|
|
218
|
+
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
};
|
|
222
|
+
|
|
223
|
+
var updateLists = function updateLists(newOrgId, oldOrgId, user, cb) {
|
|
224
|
+
|
|
225
|
+
logger.writeToInfoLog(' Updating lists...');
|
|
226
|
+
|
|
227
|
+
var self = this;
|
|
228
|
+
|
|
229
|
+
self.List.collection.find({ 'organizations': oldOrgId }).toArray(function(err, items) {
|
|
230
|
+
|
|
231
|
+
if (err) return cb(err, null);
|
|
232
|
+
else if (!items || items.length === 0) return cb(null, []);
|
|
233
|
+
|
|
234
|
+
var tasks = [];
|
|
235
|
+
var updated = [];
|
|
236
|
+
|
|
237
|
+
_.each(items, function(item) {
|
|
238
|
+
|
|
239
|
+
var orgs = _.map(item.organizations, function(o) {
|
|
240
|
+
if (o.toString() === oldOrgId.toString()) { o = newOrgId; }
|
|
241
|
+
return o;
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
tasks.push({
|
|
245
|
+
id: item._id,
|
|
246
|
+
update: { $set: { organizations: orgs } }
|
|
247
|
+
});
|
|
248
|
+
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
logger.writeToInfoLog(' Found ' + tasks.length + ' list references to update');
|
|
252
|
+
|
|
253
|
+
var queue = async.queue(function(task, callback) {
|
|
254
|
+
self.List.modifyById(task.id, task.update, function(err, result) {
|
|
255
|
+
if (err) { return callback(err, null); }
|
|
256
|
+
else if (result) { updated.push(result); }
|
|
257
|
+
return callback(null, result);
|
|
258
|
+
});
|
|
259
|
+
}, 8);
|
|
260
|
+
|
|
261
|
+
queue.drain = function() { return cb(null, updated); };
|
|
262
|
+
|
|
263
|
+
queue.push(tasks);
|
|
264
|
+
|
|
265
|
+
});
|
|
266
|
+
|
|
267
|
+
};
|
|
268
|
+
|
|
269
|
+
var updateMessages = function updateMessages(newOrgId, oldOrgId, user, cb) {
|
|
270
|
+
|
|
271
|
+
logger.writeToInfoLog(' Updating messages...');
|
|
272
|
+
|
|
273
|
+
var self = this;
|
|
274
|
+
|
|
275
|
+
self.Message.collection.find({ 'organization': oldOrgId }).toArray(function(err, items) {
|
|
276
|
+
|
|
277
|
+
if (err) return cb(err, null);
|
|
278
|
+
else if (!items || items.length === 0) return cb(null, []);
|
|
279
|
+
|
|
280
|
+
var tasks = [];
|
|
281
|
+
var updated = [];
|
|
282
|
+
|
|
283
|
+
_.each(items, function(item) {
|
|
284
|
+
tasks.push({
|
|
285
|
+
id: item._id,
|
|
286
|
+
update: { $set: { organization: newOrgId } }
|
|
287
|
+
});
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
logger.writeToInfoLog(' Found ' + tasks.length + ' message references to update');
|
|
291
|
+
|
|
292
|
+
var queue = async.queue(function(task, callback) {
|
|
293
|
+
self.Message.modifyById(task.id, task.update, function(err, result) {
|
|
294
|
+
if (err) { return callback(err, null); }
|
|
295
|
+
else if (result) { updated.push(result); }
|
|
296
|
+
return callback(null, result);
|
|
297
|
+
});
|
|
298
|
+
}, 8);
|
|
299
|
+
|
|
300
|
+
queue.drain = function() { return cb(null, updated); };
|
|
301
|
+
|
|
302
|
+
queue.push(tasks);
|
|
303
|
+
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
};
|
|
307
|
+
|
|
308
|
+
var updateNews = function updateNews(newOrgId, oldOrgId, user, cb) {
|
|
309
|
+
|
|
310
|
+
logger.writeToInfoLog(' Updating news...');
|
|
311
|
+
|
|
312
|
+
var self = this;
|
|
313
|
+
|
|
314
|
+
self.News.collection.find({ 'org': oldOrgId }).toArray(function(err, items) {
|
|
315
|
+
|
|
316
|
+
if (err) return cb(err, null);
|
|
317
|
+
else if (!items || items.length === 0) return cb(null, []);
|
|
318
|
+
|
|
319
|
+
var tasks = [];
|
|
320
|
+
var updated = [];
|
|
321
|
+
|
|
322
|
+
_.each(items, function(item) {
|
|
323
|
+
tasks.push({
|
|
324
|
+
id: item._id,
|
|
325
|
+
update: { $set: { org: newOrgId } }
|
|
326
|
+
});
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
logger.writeToInfoLog(' Found ' + tasks.length + ' news references to update');
|
|
330
|
+
|
|
331
|
+
var queue = async.queue(function(task, callback) {
|
|
332
|
+
self.News.modifyById(task.id, task.update, function(err, result) {
|
|
333
|
+
if (err) { return callback(err, null); }
|
|
334
|
+
else if (result) { updated.push(result); }
|
|
335
|
+
return callback(null, result);
|
|
336
|
+
});
|
|
337
|
+
}, 8);
|
|
338
|
+
|
|
339
|
+
queue.drain = function() { return cb(null, updated); };
|
|
340
|
+
|
|
341
|
+
queue.push(tasks);
|
|
342
|
+
|
|
343
|
+
});
|
|
344
|
+
|
|
345
|
+
};
|
|
346
|
+
|
|
347
|
+
var updateOrganizations = function updateOrganizations(newOrgId, oldOrgId, user, cb) {
|
|
348
|
+
|
|
349
|
+
logger.writeToInfoLog(' Updating orgs...');
|
|
350
|
+
|
|
351
|
+
var self = this;
|
|
352
|
+
|
|
353
|
+
self.Organization.collection.find({ $or: [{ 'merged': oldOrgId }, { 'operating.acquired.public.by': oldOrgId }, { 'operating.acquired.private.by': oldOrgId }, { 'related': oldOrgId }] }).toArray(function(err, items) {
|
|
354
|
+
|
|
355
|
+
if (err) return cb(err, null);
|
|
356
|
+
else if (!items || items.length === 0) return cb(null, []);
|
|
357
|
+
|
|
358
|
+
var tasks = [];
|
|
359
|
+
var updated = [];
|
|
360
|
+
|
|
361
|
+
_.each(items, function(item) {
|
|
362
|
+
|
|
363
|
+
var setObj = {};
|
|
364
|
+
|
|
365
|
+
setObj.merged = _.map(item.merged, function(o) {
|
|
366
|
+
if (o.toString() === oldOrgId) { o = newOrgId; }
|
|
367
|
+
return o;
|
|
368
|
+
});
|
|
369
|
+
|
|
370
|
+
setObj.related = _.map(item.related, function(o) {
|
|
371
|
+
if (o.toString() === oldOrgId) { o = newOrgId; }
|
|
372
|
+
return o;
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
var publicBy = item["operating.acquired.public.by"];
|
|
376
|
+
var privateBy = item["operating.acquired.private.by"];
|
|
377
|
+
|
|
378
|
+
if (publicBy && publicBy.toString() === oldOrgId) { setObj["operating.acquired.public.by"] = newOrgId; }
|
|
379
|
+
if (privateBy && privateBy.toString() === oldOrgId) { setObj["operating.acquired.private.by"] = newOrgId; }
|
|
380
|
+
|
|
381
|
+
tasks.push({
|
|
382
|
+
id: item._id,
|
|
383
|
+
update: {
|
|
384
|
+
$set: setObj
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
logger.writeToInfoLog(' Found ' + tasks.length + ' org references to update');
|
|
391
|
+
|
|
392
|
+
var queue = async.queue(function(task, callback) {
|
|
393
|
+
self.Organization.modifyById(task.id, task.update, function(err, result) {
|
|
394
|
+
if (err) { return callback(err, null); }
|
|
395
|
+
else if (result) { updated.push(result); }
|
|
396
|
+
return callback(null, result);
|
|
397
|
+
});
|
|
398
|
+
}, 8);
|
|
399
|
+
|
|
400
|
+
queue.drain = function() { return cb(null, updated); };
|
|
401
|
+
|
|
402
|
+
queue.push(tasks);
|
|
403
|
+
|
|
404
|
+
});
|
|
405
|
+
|
|
406
|
+
};
|
|
407
|
+
|
|
408
|
+
module.exports.merge = merge;
|