@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/lib/people.js ADDED
@@ -0,0 +1,739 @@
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
+
9
+ var merge = function merge(context, target, source, mergedBy, customerId, cb) {
10
+
11
+ console.log('Merging people...');
12
+
13
+ // validate parameters
14
+ if (!context) return cb(new Error('context is required'), null);
15
+ if (!target) return cb(new Error('target is required'), null);
16
+ if (!source) return cb(new Error('source is required'), null);
17
+ if (!mergedBy) return cb(new Error('mergedBy is required'), null);
18
+ if (!cb) throw new Error('cb is required');
19
+
20
+ var savePerson = function(person, username, callback) {
21
+
22
+ console.log('Saving target person...');
23
+ console.log(' ID:', person._id.toString());
24
+
25
+ context.Person.upsert(person, username, function(err, result) {
26
+ return callback(err, result);
27
+ });
28
+
29
+ };
30
+
31
+ if (context.flagOnly) {
32
+
33
+ var url = 'https://admin.totemvc.com/people/' + source.id;
34
+ var text = 'This person should be merged with ' + url;
35
+
36
+ context.Person.addFlag(target._id, target._id, text, customerId, function(err, result) {
37
+ if (err) return cb(err, null);
38
+ return cb(null, result);
39
+ });
40
+
41
+ }
42
+ else {
43
+
44
+ // mergedBy can be either an account or a username
45
+ var username = mergedBy.username ? mergedBy.username : mergedBy;
46
+
47
+ async.series([
48
+
49
+ // Create a snapshot of the target
50
+ helpers.createSnapshot.bind(null, context, 'Person', target, username),
51
+
52
+ // Create a snapshot of the source
53
+ helpers.createSnapshot.bind(null, context, 'Person', source, username),
54
+
55
+ // Merge the source into the target
56
+ mergeSourceIntoTarget.bind(null, target, source),
57
+
58
+ // Delete the source
59
+ // Do this prior to saving merged target in order to avoid unique key issues
60
+ helpers.removeEntity.bind(null, context, source),
61
+
62
+ // Save the updated target
63
+ savePerson.bind(null, target, username),
64
+
65
+ // Change source references to target
66
+ updateReferences.bind(null, context, target, source, mergedBy)
67
+
68
+ ], function(err, results) {
69
+
70
+ if (err) return cb(err, null);
71
+
72
+ // result from savePerson
73
+ var updatedTarget = results[4];
74
+
75
+ // return updated target
76
+ return cb(null, updatedTarget);
77
+
78
+ });
79
+
80
+ }
81
+
82
+ };
83
+
84
+ var mergeSourceIntoTarget = function mergeEntities(target, source, cb) {
85
+
86
+ console.log('Merging source into target...');
87
+
88
+ var doNotMergeKeys = [
89
+ 'merged',
90
+ 'deleted',
91
+ 'entered',
92
+ '__v',
93
+ 'status',
94
+ 'lps',
95
+ 'subscriptions',
96
+ 'account',
97
+ 'sources'
98
+ ];
99
+
100
+ var mergeSources = function mergeSources(target, source) {
101
+
102
+ // Note interaction counts are not combined here but will be updated on next calendar pull
103
+
104
+ var targetSources = target.sources || [];
105
+ var sourceSources = source.sources || [];
106
+
107
+ _.each(sourceSources, function(sourceSource) {
108
+
109
+ var match = _.find(targetSources, function(targetSource) {
110
+ return targetSource.customer.toString() === sourceSource.customer.toString() && targetSource.person.toString() === sourceSource.person.toString();
111
+ });
112
+
113
+ if (!match) { targetSources.push(sourceSource); }
114
+
115
+ });
116
+
117
+ return targetSources;
118
+
119
+ };
120
+
121
+ helpers.mergeObjects(target, source, doNotMergeKeys);
122
+
123
+ target.sources = mergeSources(target, source);
124
+
125
+ // keep a list of copied people
126
+ target.addMerged(source.id);
127
+
128
+ // add copied person name as an alias if it's different from source name
129
+ if (target.name.full.toLowerCase() != source.name.full.toLowerCase()) {
130
+ target.aliases = target.aliases.concat([source.name.full]);
131
+ target.aliases = _.uniq(target.aliases);
132
+ }
133
+
134
+ // mark as entered by system
135
+ target.entered = {
136
+ by: 'ff-merge',
137
+ on: new Date()
138
+ };
139
+
140
+ return cb(null, target);
141
+
142
+ };
143
+
144
+ // Flow control for updating references
145
+ // Takes us through updating each entity type
146
+ var updateReferences = function updateReferences(context, target, source, mergedBy, cb) {
147
+
148
+ console.log('Updating references...');
149
+
150
+ // validate parameters
151
+ if (!context) return cb(new Error('context is required'), null);
152
+ if (!target) return cb(new Error('target is required'), null);
153
+ if (!source) return cb(new Error('source is required'), null);
154
+ if (!cb) throw new Error('cb is required');
155
+
156
+ var oldPersonId = ObjectId(source.id);
157
+ var newPersonId = ObjectId(target.id);
158
+
159
+ console.log(' Target:', newPersonId.toString());
160
+ console.log(' Source:', oldPersonId.toString());
161
+
162
+ // mergedBy can be either an account or a username
163
+ var username = mergedBy.username ? mergedBy.username : mergedBy;
164
+
165
+ // Do not update cap tables or rounds as these are complicated
166
+ // and get cleaned and updated on each pull anyway
167
+
168
+ async.parallel([
169
+
170
+ updateDeals.bind(context, newPersonId, oldPersonId, mergedBy),
171
+
172
+ updateDocuments.bind(context, newPersonId, oldPersonId, null),
173
+
174
+ updateFlags.bind(context, newPersonId, oldPersonId, username),
175
+
176
+ updateInteractions.bind(context, newPersonId, oldPersonId, username),
177
+
178
+ updateLimitedPartners.bind(context, newPersonId, oldPersonId, mergedBy),
179
+
180
+ updateLists.bind(context, newPersonId, oldPersonId, null),
181
+
182
+ updateMessages.bind(context, newPersonId, oldPersonId, null),
183
+
184
+ updateNotes.bind(context, newPersonId, oldPersonId, username),
185
+
186
+ updateOrganizations.bind(context, newPersonId, oldPersonId, username),
187
+
188
+ updatePeople.bind(context, newPersonId, oldPersonId, username)
189
+
190
+ ], cb);
191
+
192
+ };
193
+
194
+ var updateDeals = function updateDeals(newPersonId, oldPersonId, user, cb) {
195
+
196
+ console.log(' Updating deals...');
197
+
198
+ var self = this;
199
+
200
+ // Get all deals for this person
201
+ self.Deal.collection.find({ $or: [{ 'source.person': oldPersonId }, { 'referrer.person': oldPersonId }]}).toArray(function(err, items) {
202
+
203
+ if (err) return cb(err, null);
204
+ else if (!items || items.length === 0) return cb(null, []);
205
+
206
+ var tasks = [];
207
+ var updated = [];
208
+
209
+ _.each(items, function(item) {
210
+
211
+ var setObj = {};
212
+
213
+ if (item.source && item.source.person && item.source.person.toString() === oldPersonId.toString()) { setObj.source = { person: newPersonId }; }
214
+ if (item.referrer && item.referrer.person && item.referrer.person.toString() === oldPersonId.toString()) { setObj.referrer = { person: newPersonId }; }
215
+
216
+ tasks.push({
217
+ id: item._id,
218
+ update: { $set: setObj }
219
+ });
220
+
221
+ });
222
+
223
+ console.log(' Found ' + tasks.length + ' deal references to update');
224
+
225
+ var queue = async.queue(function(task, callback) {
226
+ self.Deal.modifyById(task.id, task.update, function(err, result) {
227
+ if (err) { return callback(err, null); }
228
+ else if (result) { updated.push(result); }
229
+ return callback(null, result);
230
+ });
231
+ }, 8);
232
+
233
+ queue.drain = function() { return cb(null, updated); };
234
+
235
+ queue.push(tasks);
236
+
237
+ });
238
+
239
+ };
240
+
241
+ var updateDocuments = function updateDocuments(newPersonId, oldPersonId, user, cb) {
242
+
243
+ console.log(' Updating docs...');
244
+
245
+ var self = this;
246
+
247
+ // Get all documents for this person
248
+ self.Document.collection.find({ $or: [{ 'source.person': oldPersonId }, { 'referrer.person': oldPersonId }]}).toArray(function(err, items) {
249
+
250
+ if (err) return cb(err, null);
251
+ else if (!items || items.length === 0) return cb(null, []);
252
+
253
+ var tasks = [];
254
+ var updated = [];
255
+
256
+ _.each(items, function(item) {
257
+
258
+ var setObj = {};
259
+
260
+ if (item.source && item.source.person && item.source.person.toString() === oldPersonId.toString()) { setObj.source = { person: newPersonId }; }
261
+ if (item.referrer && item.referrer.person && item.referrer.person.toString() === oldPersonId.toString()) { setObj.referrer = { person: newPersonId }; }
262
+
263
+ tasks.push({
264
+ id: item._id,
265
+ update: { $set: setObj }
266
+ });
267
+
268
+ });
269
+
270
+ console.log(' Found ' + tasks.length + ' doc references to update');
271
+
272
+ var queue = async.queue(function(task, callback) {
273
+ self.Document.modifyById(task.id, task.update, function(err, result) {
274
+ if (err) { return callback(err, null); }
275
+ else if (result) { updated.push(result); }
276
+ return callback(null, result);
277
+ });
278
+ }, 8);
279
+
280
+ queue.drain = function() { return cb(null, updated); };
281
+
282
+ queue.push(tasks);
283
+
284
+ });
285
+
286
+ };
287
+
288
+ var updateFlags = function updateFlags(newPersonId, oldPersonId, user, cb) {
289
+
290
+ console.log(' Updating flags...');
291
+
292
+ var self = this;
293
+
294
+ // Get all flags for this person
295
+ self.Flag.collection.find({ 'createdBy': oldPersonId }).toArray(function(err, items) {
296
+
297
+ if (err) return cb(err, null);
298
+ else if (!items || items.length === 0) return cb(null, []);
299
+
300
+ var tasks = [];
301
+ var updated = [];
302
+
303
+ _.each(items, function(item) {
304
+
305
+ var setObj = {};
306
+
307
+ if (item.createdBy.toString() === oldPersonId.toString()) { setObj.createdBy = newPersonId; }
308
+
309
+ tasks.push({
310
+ id: item._id,
311
+ update: { $set: setObj }
312
+ });
313
+
314
+ });
315
+
316
+ console.log(' Found ' + tasks.length + ' flag references to update');
317
+
318
+ var queue = async.queue(function(task, callback) {
319
+ self.Flag.modifyById(task.id, task.update, function(err, result) {
320
+ if (err) { return callback(err, null); }
321
+ else if (result) { updated.push(result); }
322
+ return callback(null, result);
323
+ });
324
+ }, 8);
325
+
326
+ queue.drain = function() { return cb(null, updated); };
327
+
328
+ queue.push(tasks);
329
+
330
+ });
331
+
332
+ };
333
+
334
+ var updateInteractions = function updateInteractions(newPersonId, oldPersonId, user, cb) {
335
+
336
+ console.log(' Updating interactions...');
337
+
338
+ var self = this;
339
+
340
+ // Get all interactions for this person
341
+ self.Interaction.collection.find({ $or: [{ 'attendees.external': oldPersonId }, { 'attendees.internal': oldPersonId }] }).toArray(function(err, items) {
342
+
343
+ if (err) return cb(err, null);
344
+ else if (!items || items.length === 0) return cb(null, []);
345
+
346
+ var tasks = [];
347
+ var updated = [];
348
+
349
+ _.each(items, function(item) {
350
+
351
+ item.attendees.internal = _.map(item.attendees.internal, function(attendee) {
352
+ if (attendee.toString() === oldPersonId.toString()) { attendee = newPersonId; }
353
+ return attendee;
354
+ });
355
+
356
+ item.attendees.external = _.map(item.attendees.external, function(attendee) {
357
+ if (attendee.toString() === oldPersonId.toString()) { attendee = newPersonId; }
358
+ return attendee;
359
+ });
360
+
361
+ tasks.push({
362
+ id: item._id,
363
+ update: { $set: {
364
+ 'attendees.internal': item.attendees.internal,
365
+ 'attendees.external': item.attendees.external
366
+ }
367
+ }
368
+ });
369
+
370
+ });
371
+
372
+ console.log(' Found ' + tasks.length + ' interaction references to update');
373
+
374
+ var queue = async.queue(function(task, callback) {
375
+ self.Interaction.modifyById(task.id, task.update, function(err, result) {
376
+ if (err) { return callback(err, null); }
377
+ else if (result) { updated.push(result); }
378
+ return callback(null, result);
379
+ });
380
+ }, 8);
381
+
382
+ queue.drain = function() { return cb(null, updated); };
383
+
384
+ queue.push(tasks);
385
+
386
+ });
387
+
388
+ };
389
+
390
+ var updateLimitedPartners = function updateLimitedPartners(newPersonId, oldPersonId, user, cb) {
391
+
392
+ console.log(' Updating lps...');
393
+
394
+ var self = this;
395
+
396
+ // Get all lps for this person
397
+ self.LimitedPartner.collection.find({ 'people': oldPersonId }).toArray(function(err, items) {
398
+
399
+ if (err) return cb(err, null);
400
+ else if (!items || items.length === 0) return cb(null, []);
401
+
402
+ var tasks = [];
403
+ var updated = [];
404
+
405
+ _.each(items, function(item) {
406
+
407
+ item.people = _.map(item.people, function(person) {
408
+ if (person.toString() === oldPersonId.toString()) { person = newPersonId; }
409
+ return person;
410
+ });
411
+
412
+ tasks.push({
413
+ id: item._id,
414
+ update: { $set: {
415
+ 'people': item.people
416
+ }
417
+ }
418
+ });
419
+
420
+ });
421
+
422
+ console.log(' Found ' + tasks.length + ' lp references to update');
423
+
424
+ var queue = async.queue(function(task, callback) {
425
+ self.LimitedPartner.modifyById(task.id, task.update, function(err, result) {
426
+ if (err) { return callback(err, null); }
427
+ else if (result) { updated.push(result); }
428
+ return callback(null, result);
429
+ });
430
+ }, 8);
431
+
432
+ queue.drain = function() { return cb(null, updated); };
433
+
434
+ queue.push(tasks);
435
+
436
+ });
437
+
438
+ };
439
+
440
+ var updateLists = function updateLists(newPersonId, oldPersonId, user, cb) {
441
+
442
+ console.log(' Updating lists...');
443
+
444
+ var self = this;
445
+
446
+ // Get all lists for this person
447
+ self.List.collection.find({ $or: [{ 'people': oldPersonId }, { 'createdBy': oldPersonId }] }).toArray(function(err, items) {
448
+
449
+ if (err) return cb(err, null);
450
+ else if (!items || items.length === 0) return cb(null, []);
451
+
452
+ var tasks = [];
453
+ var updated = [];
454
+
455
+ _.each(items, function(item) {
456
+
457
+ var setObj = {};
458
+
459
+ if (item.createdBy.toString() === oldPersonId.toString()) { setObj.createdBy = newPersonId; }
460
+
461
+ setObj.people = _.map(item.people, function(person) {
462
+ if (person.toString() === oldPersonId.toString()) { person = newPersonId; }
463
+ return person;
464
+ });
465
+
466
+ tasks.push({
467
+ id: item._id,
468
+ update: { $set: setObj }
469
+ });
470
+
471
+ });
472
+
473
+ console.log(' Found ' + tasks.length + ' list references to update');
474
+
475
+ var queue = async.queue(function(task, callback) {
476
+ self.List.modifyById(task.id, task.update, function(err, result) {
477
+ if (err) { return callback(err, null); }
478
+ else if (result) { updated.push(result); }
479
+ return callback(null, result);
480
+ });
481
+ }, 8);
482
+
483
+ queue.drain = function() { return cb(null, updated); };
484
+
485
+ queue.push(tasks);
486
+
487
+ });
488
+
489
+ };
490
+
491
+ var updateMessages = function updateMessages(newPersonId, oldPersonId, user, cb) {
492
+
493
+ console.log(' Updating messages...');
494
+
495
+ var self = this;
496
+
497
+ // Get all messages for this person
498
+ self.Message.collection.find({ $or: [{ 'recipients.external': oldPersonId }, { 'recipients.internal': oldPersonId }] }).toArray(function(err, items) {
499
+
500
+ if (err) return cb(err, null);
501
+ else if (!items || items.length === 0) return cb(null, []);
502
+
503
+ var tasks = [];
504
+ var updated = [];
505
+
506
+ _.each(items, function(item) {
507
+
508
+ item.recipients.internal = _.map(item.recipients.internal, function(recipient) {
509
+ if (recipient.toString() === oldPersonId.toString()) { recipient = newPersonId; }
510
+ return recipient;
511
+ });
512
+
513
+ item.recipients.external = _.map(item.recipients.external, function(recipient) {
514
+ if (recipient.toString() === oldPersonId.toString()) { recipient = newPersonId; }
515
+ return recipient;
516
+ });
517
+
518
+ tasks.push({
519
+ id: item._id,
520
+ update: { $set: {
521
+ 'recipients.internal': item.recipients.internal,
522
+ 'recipients.external': item.recipients.external
523
+ }
524
+ }
525
+ });
526
+
527
+ });
528
+
529
+ console.log(' Found ' + tasks.length + ' message references to update');
530
+
531
+ var queue = async.queue(function(task, callback) {
532
+ self.Message.modifyById(task.id, task.update, function(err, result) {
533
+ if (err) { return callback(err, null); }
534
+ else if (result) { updated.push(result); }
535
+ return callback(null, result);
536
+ });
537
+ }, 8);
538
+
539
+ queue.drain = function() { return cb(null, updated); };
540
+
541
+ queue.push(tasks);
542
+
543
+ });
544
+
545
+ };
546
+
547
+ var updateNotes = function updateNotes(newPersonId, oldPersonId, user, cb) {
548
+
549
+ console.log(' Updating notes...');
550
+
551
+ var self = this;
552
+
553
+ // Get all notes for this person
554
+ self.Note.collection.find({ 'createdBy': oldPersonId }).toArray(function(err, items) {
555
+
556
+ if (err) return cb(err, null);
557
+ else if (!items || items.length === 0) return cb(null, []);
558
+
559
+ var tasks = [];
560
+ var updated = [];
561
+
562
+ _.each(items, function(item) {
563
+
564
+ var setObj = {};
565
+
566
+ if (item.createdBy.toString() === oldPersonId.toString()) { setObj.createdBy = newPersonId; }
567
+
568
+ tasks.push({
569
+ id: item._id,
570
+ update: { $set: setObj }
571
+ });
572
+
573
+ });
574
+
575
+ console.log(' Found ' + tasks.length + ' note references to update');
576
+
577
+ var queue = async.queue(function(task, callback) {
578
+ self.Note.modifyById(task.id, task.update, function(err, result) {
579
+ if (err) { return callback(err, null); }
580
+ else if (result) { updated.push(result); }
581
+ return callback(null, result);
582
+ });
583
+ }, 8);
584
+
585
+ queue.drain = function() { return cb(null, updated); };
586
+
587
+ queue.push(tasks);
588
+
589
+ });
590
+
591
+ };
592
+
593
+ var updateOrganizations = function updateOrganizations(newPersonId, oldPersonId, user, cb) {
594
+
595
+ console.log(' Updating orgs...');
596
+
597
+ var self = this;
598
+
599
+ // Get all orgs for this person
600
+ self.Organization.collection.find({ $or: [{ 'people.person': oldPersonId }, { 'chair.first': oldPersonId }, { 'chair.second': oldPersonId }] }).toArray(function(err, orgs) {
601
+
602
+ if (err) return cb(err, null);
603
+ else if (!orgs || orgs.length === 0) return cb(null, []);
604
+
605
+ var tasks = [];
606
+ var updated = [];
607
+
608
+ var checkIfBothPeopleAtOrg = function checkIfBothPeopleAtOrg(org) {
609
+
610
+ // if we blindly mapped the old person to the new person when they are both at the same org, then we'd end up
611
+ // with something like this:
612
+ //
613
+ // [ {
614
+ // person: 5cacc6238d38a6f68bf53c95,
615
+ // board: 'none',
616
+ // public: true,
617
+ // leader: false,
618
+ // current: true,
619
+ // title: 'CEO'
620
+ // },
621
+ // {
622
+ // person: 5cacc6238d38a6f68bf53c95,
623
+ // board: 'none',
624
+ // public: true,
625
+ // leader: false,
626
+ // current: true,
627
+ // title: 'CTO'
628
+ // }
629
+ // ]
630
+ //
631
+ // and be unable to determine which data to keep. We still can't know for sure, but let's default to keeping the
632
+ // target/new person's data. We do that by removing the source/old person here and then map the target/new person id.
633
+
634
+ var oldPerson = _.find(org.people, function(person) { return person.person.toString() === oldPersonId.toString(); });
635
+ var newPerson = _.find(org.people, function(person) { return person.person.toString() === newPersonId.toString(); });
636
+
637
+ // Both people are at this org
638
+ if (oldPerson && newPerson) {
639
+ org.people = _.reject(org.people, function(person) {
640
+ return person.person.toString() === oldPersonId.toString();
641
+ });
642
+ }
643
+
644
+ };
645
+
646
+ _.each(orgs, function(org) {
647
+
648
+ var setObj = {};
649
+
650
+ // keep the target info and remove the source if at the same org
651
+ // note this must be done before mapping the old id to the new id
652
+ checkIfBothPeopleAtOrg(org);
653
+
654
+ setObj.people = _.map(org.people, function(person) {
655
+ if (person.person.toString() === oldPersonId.toString()) { person.person = newPersonId; }
656
+ return person;
657
+ });
658
+
659
+ setObj.chairs = _.map(org.chairs, function(chair) {
660
+ if (chair.first && chair.first.toString() === oldPersonId.toString()) { chair.first = newPersonId; }
661
+ if (chair.second && chair.second.toString() === oldPersonId.toString()) { chair.second = newPersonId; }
662
+ return chair;
663
+ });
664
+
665
+ tasks.push({
666
+ id: org._id,
667
+ update: { $set: setObj }
668
+ });
669
+
670
+ });
671
+
672
+ console.log(' Found ' + tasks.length + ' org references to update');
673
+
674
+ var queue = async.queue(function(task, callback) {
675
+ self.Organization.modifyById(task.id, task.update, function(err, result) {
676
+ if (err) { return callback(err, null); }
677
+ else if (result) { updated.push(result); }
678
+ return callback(null, result);
679
+ });
680
+ }, 8);
681
+
682
+ queue.drain = function() { return cb(null, updated); };
683
+
684
+ queue.push(tasks);
685
+
686
+ });
687
+
688
+ };
689
+
690
+ var updatePeople = function updatePeople(newPersonId, oldPersonId, user, cb) {
691
+
692
+ console.log(' Updating people...');
693
+
694
+ var self = this;
695
+
696
+ // Get all orgs for this person
697
+ self.Organization.collection.find({ 'related': oldPersonId }).toArray(function(err, items) {
698
+
699
+ if (err) return cb(err, null);
700
+ else if (!items || items.length === 0) return cb(null, []);
701
+
702
+ var tasks = [];
703
+ var updated = [];
704
+
705
+ _.each(items, function(item) {
706
+
707
+ var setObj = {};
708
+
709
+ setObj.related = _.map(item.related, function(person) {
710
+ if (person.toString() === oldPersonId.toString()) { person = newPersonId; }
711
+ return person;
712
+ });
713
+
714
+ tasks.push({
715
+ id: item._id,
716
+ update: { $set: setObj }
717
+ });
718
+
719
+ });
720
+
721
+ console.log(' Found ' + tasks.length + ' person references to update');
722
+
723
+ var queue = async.queue(function(task, callback) {
724
+ self.Organization.modifyById(task.id, task.update, function(err, result) {
725
+ if (err) { return callback(err, null); }
726
+ else if (result) { updated.push(result); }
727
+ return callback(null, result);
728
+ });
729
+ }, 8);
730
+
731
+ queue.drain = function() { return cb(null, updated); };
732
+
733
+ queue.push(tasks);
734
+
735
+ });
736
+
737
+ };
738
+
739
+ module.exports.merge = merge;