@dhyasama/totem-models 12.35.0 → 12.37.0

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/Document.js CHANGED
@@ -6,6 +6,7 @@ module.exports = function(mongoose, config) {
6
6
 
7
7
  Schema = mongoose.Schema,
8
8
  utilities = require('@dhyasama/totem-utilities'),
9
+ metricSources = require('./MetricSource')(mongoose),
9
10
  _ = require('underscore');
10
11
 
11
12
  var Document = new Schema({
@@ -148,11 +149,10 @@ module.exports = function(mongoose, config) {
148
149
  });
149
150
 
150
151
  const doc = await query;
151
- if (!doc) return null;
152
-
153
- // No population so no need to scrub
154
-
155
- await doc.deleteOne();
152
+ // Always attempt the unlink so retrying a partially failed deletion also
153
+ // repairs metric references after the source itself is already gone.
154
+ if (doc) await doc.deleteOne();
155
+ await metricSources.unlink('Document', id, options.CUSTOMER_ID);
156
156
  return doc;
157
157
  };
158
158
 
package/lib/Meeting.js CHANGED
@@ -4,7 +4,8 @@ module.exports = function(mongoose, config) {
4
4
 
5
5
  var
6
6
 
7
- Schema = mongoose.Schema;
7
+ Schema = mongoose.Schema,
8
+ metricSources = require('./MetricSource')(mongoose);
8
9
 
9
10
  let moment = require('moment');
10
11
 
@@ -159,8 +160,8 @@ module.exports = function(mongoose, config) {
159
160
 
160
161
  let query = this.findOne({ _id: meetingId, customer: options.CUSTOMER_ID });
161
162
  const meeting = await query;
162
- if (!meeting) { return null; }
163
- await meeting.deleteOne();
163
+ if (meeting) await meeting.deleteOne();
164
+ await metricSources.unlink('Meeting', meetingId, options.CUSTOMER_ID);
164
165
  return meeting;
165
166
  };
166
167
 
package/lib/Message.js CHANGED
@@ -10,6 +10,7 @@ module.exports = function(mongoose, config) {
10
10
  _ = require('underscore'),
11
11
  moment = require('moment'),
12
12
  helpers = require('../helpers'),
13
+ metricSources = require('./MetricSource')(mongoose),
13
14
  Document = mongoose.model('Document');
14
15
 
15
16
  let Message = new Schema({
@@ -66,6 +67,7 @@ module.exports = function(mongoose, config) {
66
67
  '_id': messageId,
67
68
  'customer': customerId
68
69
  });
70
+ await metricSources.unlink('Message', messageId, customerId);
69
71
  return result;
70
72
  };
71
73
 
@@ -0,0 +1,38 @@
1
+ 'use strict';
2
+
3
+ var SOURCE_MODELS = ['Document', 'Meeting', 'Message', 'News', 'Note'];
4
+
5
+ module.exports = function(mongoose) {
6
+ return {
7
+ unlink: async function(model, ref, customerId) {
8
+ if (SOURCE_MODELS.indexOf(model) === -1) {
9
+ throw new Error('model must be a supported metric source');
10
+ }
11
+ if (!ref) { throw new Error('ref is required'); }
12
+ if (!customerId && model !== 'News') { throw new Error('customerId is required'); }
13
+
14
+ var source = { model: model, ref: ref };
15
+ var Financials = mongoose.model('Financials');
16
+ var filter = {
17
+ $or: [
18
+ { 'snapshots.metrics.sources': { $elemMatch: source } },
19
+ { 'snapshots.metrics.previous.sources': { $elemMatch: source } }
20
+ ]
21
+ };
22
+
23
+ // News is global and has no customer field. All current source models are
24
+ // tenant-owned and must only affect the deleting customer.
25
+ if (customerId) filter.customer = customerId;
26
+
27
+ return Financials.updateMany(
28
+ filter,
29
+ {
30
+ $pull: {
31
+ 'snapshots.$[].metrics.$[].sources': source,
32
+ 'snapshots.$[].metrics.$[].previous.sources': source
33
+ }
34
+ }
35
+ );
36
+ }
37
+ };
38
+ };
package/lib/News.js CHANGED
@@ -7,6 +7,7 @@ module.exports = function(mongoose, config) {
7
7
 
8
8
  Schema = mongoose.Schema,
9
9
  helpers = require('../helpers'),
10
+ metricSources = require('./MetricSource')(mongoose),
10
11
  moment = require('moment'),
11
12
 
12
13
  News = new Schema({
@@ -15,6 +16,8 @@ module.exports = function(mongoose, config) {
15
16
 
16
17
  text: { type: String, required: false, default: null },
17
18
 
19
+ html: { type: String, required: false, default: null },
20
+
18
21
  summary: { type: String, required: false, default: null },
19
22
 
20
23
  pageUrl: { type: String, required: false, default: null },
@@ -27,6 +30,13 @@ module.exports = function(mongoose, config) {
27
30
 
28
31
  sentiment: { type: Number, required: false, default: null },
29
32
 
33
+ analysis: {
34
+ insights: [{
35
+ text: { type: String },
36
+ category: { type: String, enum: ['highlight', 'lowlight', 'goal', 'ask'] }
37
+ }]
38
+ },
39
+
30
40
  diffbotUri: { type: String, required: true, unique: true },
31
41
 
32
42
  org: {
@@ -112,6 +122,26 @@ module.exports = function(mongoose, config) {
112
122
 
113
123
  };
114
124
 
125
+ News.statics.delete = function(id, cb) {
126
+
127
+ const run = async () => {
128
+ if (!id) { throw new Error('id is required'); }
129
+ if (!mongoose.Types.ObjectId.isValid(id)) { throw new Error('id is not a valid ObjectId'); }
130
+
131
+ const news = await this.findById(id);
132
+ if (news) await news.deleteOne();
133
+
134
+ // News records are global rather than customer-owned, so deleting one
135
+ // removes its metric reference for every customer.
136
+ await metricSources.unlink('News', id);
137
+ return news;
138
+ };
139
+
140
+ if (typeof cb === 'function') { run().then(result => cb(null, result), err => cb(err)); return; }
141
+ else { return run(); }
142
+
143
+ };
144
+
115
145
  News.statics.upsert = function (news, cb) {
116
146
 
117
147
  const run = async () => {
package/lib/Note.js CHANGED
@@ -5,6 +5,7 @@ module.exports = function(mongoose, config) {
5
5
  let
6
6
 
7
7
  Schema = mongoose.Schema,
8
+ metricSources = require('./MetricSource')(mongoose),
8
9
  querystring = require('querystring');
9
10
 
10
11
  let Note = new Schema({
@@ -44,9 +45,8 @@ module.exports = function(mongoose, config) {
44
45
  });
45
46
 
46
47
  const note = await query;
47
- if (!note) return null;
48
-
49
- await note.deleteOne();
48
+ if (note) await note.deleteOne();
49
+ await metricSources.unlink('Note', noteId, customerId);
50
50
  return note;
51
51
  };
52
52
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dhyasama/totem-models",
3
- "version": "12.35.0",
3
+ "version": "12.37.0",
4
4
  "author": "Jason Reynolds",
5
5
  "license": "UNLICENSED",
6
6
  "description": "Models for Totem platform",
@@ -0,0 +1,61 @@
1
+ 'use strict';
2
+
3
+ var should = require('should');
4
+ var createMetricSources = require('../lib/MetricSource');
5
+
6
+ describe('MetricSource', function() {
7
+ it('unlinks a source from current and previous metric states for one customer', async function() {
8
+ var captured;
9
+ var mongoose = {
10
+ model: function(name) {
11
+ name.should.equal('Financials');
12
+ return {
13
+ updateMany: function(filter, update) {
14
+ captured = { filter: filter, update: update };
15
+ return Promise.resolve({ modifiedCount: 1 });
16
+ }
17
+ };
18
+ }
19
+ };
20
+
21
+ await createMetricSources(mongoose).unlink('Meeting', 'meeting-id', 'customer-id');
22
+
23
+ captured.filter.customer.should.equal('customer-id');
24
+ captured.filter.$or.should.have.length(2);
25
+ captured.update.$pull['snapshots.$[].metrics.$[].sources'].should.eql({
26
+ model: 'Meeting', ref: 'meeting-id'
27
+ });
28
+ captured.update.$pull['snapshots.$[].metrics.$[].previous.sources'].should.eql({
29
+ model: 'Meeting', ref: 'meeting-id'
30
+ });
31
+ });
32
+
33
+ it('rejects unsupported source models', async function() {
34
+ var metricSources = createMetricSources({});
35
+
36
+ await metricSources.unlink('Event', 'event-id', 'customer-id').should.be.rejectedWith(
37
+ 'model must be a supported metric source'
38
+ );
39
+ });
40
+
41
+ it('unlinks global News sources without a customer filter', async function() {
42
+ var capturedFilter;
43
+ var mongoose = {
44
+ model: function() {
45
+ return {
46
+ updateMany: function(filter) {
47
+ capturedFilter = filter;
48
+ return Promise.resolve({ modifiedCount: 1 });
49
+ }
50
+ };
51
+ }
52
+ };
53
+
54
+ await createMetricSources(mongoose).unlink('News', 'news-id');
55
+
56
+ should.not.exist(capturedFilter.customer);
57
+ capturedFilter.$or[0]['snapshots.metrics.sources'].$elemMatch.should.eql({
58
+ model: 'News', ref: 'news-id'
59
+ });
60
+ });
61
+ });
package/test/News.js CHANGED
@@ -25,12 +25,23 @@ describe('News', function() {
25
25
  news.createdBy = 'Jason';
26
26
  news.createdOn = Date.now();
27
27
  news.title = 'Google';
28
+ news.html = '<p>Google announced quarterly revenue.</p>';
29
+ news.analysis = {
30
+ insights: [{
31
+ text: 'Quarterly revenue reached a new high.',
32
+ category: 'highlight'
33
+ }]
34
+ };
28
35
  news.link = 'http://google.com';
29
36
  news.internal = false;
30
37
 
31
38
  News.upsert(news, function(err, item) {
32
39
  should.not.exist(err);
33
40
  should.exist(item);
41
+ item.html.should.equal('<p>Google announced quarterly revenue.</p>');
42
+ item.analysis.insights.should.have.length(1);
43
+ item.analysis.insights[0].text.should.equal('Quarterly revenue reached a new high.');
44
+ item.analysis.insights[0].category.should.equal('highlight');
34
45
  news = item;
35
46
  done();
36
47
  });