@tiledesk/tiledesk-server 2.19.12 → 2.19.14
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/CHANGELOG.md +7 -0
- package/app.js +3 -0
- package/channels/chat21/chat21WebHook.js +13 -2
- package/docs/activities-client.md +864 -0
- package/event/requestEvent.js +14 -0
- package/jobs.js +1 -0
- package/jobsManager.js +2 -0
- package/models/dataTable.js +64 -0
- package/package.json +2 -2
- package/pubmodules/activities/activityArchiver.js +699 -377
- package/pubmodules/activities/activityMessageUtil.js +336 -0
- package/pubmodules/activities/index.js +1 -1
- package/pubmodules/activities/models/activity.js +99 -2
- package/pubmodules/activities/routes/activity.js +114 -144
- package/pubmodules/messageActions/messageActionsInterceptor.js +15 -2
- package/pubmodules/queue/reconnect.js +307 -2
- package/pubmodules/rules/conciergeBot.js +7 -1
- package/pubmodules/trigger/rulesTrigger.js +33 -5
- package/routes/dataTable.js +280 -0
- package/routes/faq_kb.js +24 -1
- package/routes/kb.js +90 -2
- package/routes/project_user.js +66 -17
- package/routes/request.js +20 -8
- package/services/chatbotService.js +6 -3
- package/services/dataTableService.js +545 -0
- package/services/requestService.js +180 -27
- package/test/dataTablesRoute.test.js +844 -0
- package/test/faqkbRoute.js +1 -0
- package/test/kbRoute.js +59 -0
- package/utils/activityActorUtil.js +167 -0
- package/utils/assignmentContextUtil.js +283 -0
- package/utils/dataTableBsonUtils.js +12 -0
- package/utils/dataTableUtils.js +353 -0
- package/utils/projectUserUpdateContextUtil.js +325 -0
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
function targetUserLabel(activity) {
|
|
4
|
+
const user = activity &&
|
|
5
|
+
activity.target &&
|
|
6
|
+
activity.target.object &&
|
|
7
|
+
activity.target.object.id_user;
|
|
8
|
+
|
|
9
|
+
if (!user) {
|
|
10
|
+
return 'unknown user';
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const firstname = user.firstname || '';
|
|
14
|
+
const lastname = user.lastname || '';
|
|
15
|
+
const fullname = (firstname + ' ' + lastname).trim();
|
|
16
|
+
if (fullname) {
|
|
17
|
+
return fullname;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return String(user._id || user.id || 'unknown user');
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function actorLabel(activity) {
|
|
24
|
+
const actor = activity && activity.actor;
|
|
25
|
+
if (!actor) {
|
|
26
|
+
return 'Someone';
|
|
27
|
+
}
|
|
28
|
+
if (actor.type === 'system') {
|
|
29
|
+
return 'System';
|
|
30
|
+
}
|
|
31
|
+
return actor.name || actor.id || 'Someone';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function requestLabel(activity) {
|
|
35
|
+
const request = activity && activity.target && activity.target.object;
|
|
36
|
+
if (request && request.request_id) {
|
|
37
|
+
return request.request_id;
|
|
38
|
+
}
|
|
39
|
+
if (activity && activity.target && activity.target.id) {
|
|
40
|
+
return String(activity.target.id);
|
|
41
|
+
}
|
|
42
|
+
return 'conversation';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function resolveParticipantLabel(activity, participantId) {
|
|
46
|
+
if (!participantId) {
|
|
47
|
+
return 'unknown agent';
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
const agents = activity &&
|
|
51
|
+
activity.target &&
|
|
52
|
+
activity.target.object &&
|
|
53
|
+
activity.target.object.participatingAgents;
|
|
54
|
+
|
|
55
|
+
if (Array.isArray(agents)) {
|
|
56
|
+
for (const agent of agents) {
|
|
57
|
+
const user = agent.id_user || agent;
|
|
58
|
+
const userId = user._id || user.id || user;
|
|
59
|
+
if (String(userId) === String(participantId)) {
|
|
60
|
+
const firstname = user.firstname || '';
|
|
61
|
+
const lastname = user.lastname || '';
|
|
62
|
+
const fullname = (firstname + ' ' + lastname).trim();
|
|
63
|
+
if (fullname) {
|
|
64
|
+
return fullname;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
return String(participantId);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function inviteTargetLabel(activity) {
|
|
74
|
+
const target = activity && activity.target;
|
|
75
|
+
if (!target) {
|
|
76
|
+
return 'unknown user';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
if (target.type === 'pendinginvitation') {
|
|
80
|
+
const pending = target.object || {};
|
|
81
|
+
return pending.email || (activity.actionObj && activity.actionObj.email) || 'unknown user';
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
const user = target.object && target.object.id_user;
|
|
85
|
+
if (user) {
|
|
86
|
+
const firstname = user.firstname || '';
|
|
87
|
+
const lastname = user.lastname || '';
|
|
88
|
+
const fullname = (firstname + ' ' + lastname).trim();
|
|
89
|
+
if (fullname) {
|
|
90
|
+
return fullname;
|
|
91
|
+
}
|
|
92
|
+
return user.email || 'unknown user';
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return (activity.actionObj && activity.actionObj.email) || 'unknown user';
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function inviteEmailLabel(activity) {
|
|
99
|
+
const actionObj = activity && activity.actionObj || {};
|
|
100
|
+
if (actionObj.email) {
|
|
101
|
+
return actionObj.email;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const target = activity && activity.target;
|
|
105
|
+
if (target && target.type === 'pendinginvitation' && target.object && target.object.email) {
|
|
106
|
+
return target.object.email;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
const user = target && target.object && target.object.id_user;
|
|
110
|
+
if (user && user.email) {
|
|
111
|
+
return user.email;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return '';
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function resolveAssigneeLabel(activity) {
|
|
118
|
+
const actionObj = activity && activity.actionObj || {};
|
|
119
|
+
if (actionObj.assigneeName) {
|
|
120
|
+
return actionObj.assigneeName;
|
|
121
|
+
}
|
|
122
|
+
if (actionObj.assigneeType === 'department') {
|
|
123
|
+
return actionObj.assigneeId || 'department';
|
|
124
|
+
}
|
|
125
|
+
if (actionObj.assigneeType === 'bot') {
|
|
126
|
+
return actionObj.assigneeId || 'chatbot';
|
|
127
|
+
}
|
|
128
|
+
return resolveParticipantLabel(activity, actionObj.assigneeId);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function namespaceLabel(activity) {
|
|
132
|
+
const object = activity && activity.target && activity.target.object;
|
|
133
|
+
if (object && object.name) {
|
|
134
|
+
return object.name;
|
|
135
|
+
}
|
|
136
|
+
if (activity && activity.actionObj && activity.actionObj.namespaceName) {
|
|
137
|
+
return activity.actionObj.namespaceName;
|
|
138
|
+
}
|
|
139
|
+
if (activity && activity.target && activity.target.id) {
|
|
140
|
+
return String(activity.target.id);
|
|
141
|
+
}
|
|
142
|
+
return 'namespace';
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function chatbotLabel(activity) {
|
|
146
|
+
const object = activity && activity.target && activity.target.object;
|
|
147
|
+
if (object && object.name) {
|
|
148
|
+
return object.name;
|
|
149
|
+
}
|
|
150
|
+
if (activity && activity.actionObj && activity.actionObj.name) {
|
|
151
|
+
return activity.actionObj.name;
|
|
152
|
+
}
|
|
153
|
+
return 'chatbot';
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function contentAddTypeLabel(contentAddType) {
|
|
157
|
+
switch (contentAddType) {
|
|
158
|
+
case 'content':
|
|
159
|
+
return 'content';
|
|
160
|
+
case 'url_list':
|
|
161
|
+
return 'URL list';
|
|
162
|
+
case 'csv':
|
|
163
|
+
return 'CSV file';
|
|
164
|
+
case 'sitemap':
|
|
165
|
+
return 'sitemap';
|
|
166
|
+
default:
|
|
167
|
+
return contentAddType || 'content';
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function kbContentLabel(activity) {
|
|
172
|
+
const actionObj = (activity && activity.actionObj) || {};
|
|
173
|
+
const object = activity && activity.target && activity.target.object;
|
|
174
|
+
if (actionObj.source) {
|
|
175
|
+
return actionObj.source;
|
|
176
|
+
}
|
|
177
|
+
if (actionObj.name) {
|
|
178
|
+
return actionObj.name;
|
|
179
|
+
}
|
|
180
|
+
if (object && object.source) {
|
|
181
|
+
return object.source;
|
|
182
|
+
}
|
|
183
|
+
if (object && object.name) {
|
|
184
|
+
return object.name;
|
|
185
|
+
}
|
|
186
|
+
if (activity && activity.target && activity.target.id) {
|
|
187
|
+
return String(activity.target.id);
|
|
188
|
+
}
|
|
189
|
+
return 'content';
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
function buildDefaultActivityMessage(activity) {
|
|
193
|
+
if (!activity || !activity.verb) {
|
|
194
|
+
return null;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const actionObj = activity.actionObj || {};
|
|
198
|
+
const actor = actorLabel(activity);
|
|
199
|
+
const conversation = requestLabel(activity);
|
|
200
|
+
const assignee = resolveAssigneeLabel(activity);
|
|
201
|
+
const source = actionObj.source || 'unknown';
|
|
202
|
+
|
|
203
|
+
switch (activity.verb) {
|
|
204
|
+
case 'REQUEST_ASSIGNED_AUTO':
|
|
205
|
+
if (activity.actor && activity.actor.type === 'system') {
|
|
206
|
+
return 'System automatically assigned conversation ' + conversation +
|
|
207
|
+
' to ' + assignee + ' (source: ' + source + ')';
|
|
208
|
+
}
|
|
209
|
+
return actor + ' triggered automatic assignment of conversation ' + conversation +
|
|
210
|
+
' to ' + assignee + ' (source: ' + source + ')';
|
|
211
|
+
|
|
212
|
+
case 'REQUEST_ASSIGNED_SELF':
|
|
213
|
+
return actor + ' joined conversation ' + conversation + ' (source: ' + source + ')';
|
|
214
|
+
|
|
215
|
+
case 'REQUEST_ASSIGNED_MANUAL': {
|
|
216
|
+
const assigneeType = actionObj.assigneeType || 'user';
|
|
217
|
+
if (assigneeType === 'bot') {
|
|
218
|
+
let message = actor + ' reassigned conversation ' + conversation +
|
|
219
|
+
' to chatbot ' + assignee + ' (source: ' + source + ')';
|
|
220
|
+
if (actionObj.previousAssigneeId) {
|
|
221
|
+
const previous = resolveParticipantLabel(activity, actionObj.previousAssigneeId);
|
|
222
|
+
message += ' (replaced ' + previous + ')';
|
|
223
|
+
}
|
|
224
|
+
return message;
|
|
225
|
+
}
|
|
226
|
+
if (assigneeType === 'department') {
|
|
227
|
+
let message = actor + ' reassigned conversation ' + conversation +
|
|
228
|
+
' to department ' + assignee + ' (source: ' + source + ')';
|
|
229
|
+
if (actionObj.previousAssigneeId) {
|
|
230
|
+
const previous = resolveParticipantLabel(activity, actionObj.previousAssigneeId);
|
|
231
|
+
message += ' (replaced ' + previous + ')';
|
|
232
|
+
}
|
|
233
|
+
return message;
|
|
234
|
+
}
|
|
235
|
+
let message = actor + ' manually assigned conversation ' + conversation +
|
|
236
|
+
' to ' + assignee + ' (source: ' + source + ')';
|
|
237
|
+
if (actionObj.previousAssigneeId) {
|
|
238
|
+
const previous = resolveParticipantLabel(activity, actionObj.previousAssigneeId);
|
|
239
|
+
message += ' (replaced ' + previous + ')';
|
|
240
|
+
}
|
|
241
|
+
return message;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
case 'REQUEST_UNASSIGNED':
|
|
245
|
+
if (actionObj.source === 'subscription') {
|
|
246
|
+
return 'System unassigned ' + assignee + ' from conversation ' + conversation +
|
|
247
|
+
' (source: subscription)';
|
|
248
|
+
}
|
|
249
|
+
return actor + ' unassigned ' + assignee + ' from conversation ' + conversation +
|
|
250
|
+
' (source: ' + source + ')';
|
|
251
|
+
|
|
252
|
+
case 'LEAVE_CONVERSATION':
|
|
253
|
+
return actor + ' left conversation ' + conversation + ' (source: ' + source + ')';
|
|
254
|
+
|
|
255
|
+
case 'PROJECT_USER_AVAILABILITY_SELF': {
|
|
256
|
+
const targetUser = targetUserLabel(activity);
|
|
257
|
+
const newStatus = actionObj.newStatus || 'unknown';
|
|
258
|
+
return targetUser + ' changed availability status to ' + newStatus;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
case 'PROJECT_USER_AVAILABILITY_SYSTEM': {
|
|
262
|
+
const targetUser = targetUserLabel(activity);
|
|
263
|
+
const newStatus = actionObj.newStatus || 'unknown';
|
|
264
|
+
return targetUser + ' availability status was changed to ' + newStatus + ' by the system';
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
case 'PROJECT_USER_INVITE': {
|
|
268
|
+
const target = inviteTargetLabel(activity);
|
|
269
|
+
const email = inviteEmailLabel(activity);
|
|
270
|
+
const role = actionObj.role || 'agent';
|
|
271
|
+
const emailPart = email && target !== email ? ' (' + email + ')' : '';
|
|
272
|
+
return actor + ' invited ' + target + emailPart + ' to take on the role of ' + role;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
case 'PROJECT_USER_DELETE':
|
|
276
|
+
return actor + ' removed ' + targetUserLabel(activity) + ' from the project';
|
|
277
|
+
|
|
278
|
+
case 'FAQ_KB_CREATE':
|
|
279
|
+
return actor + ' created chatbot ' + chatbotLabel(activity);
|
|
280
|
+
|
|
281
|
+
case 'FAQ_KB_DELETE':
|
|
282
|
+
return actor + ' deleted chatbot ' + chatbotLabel(activity);
|
|
283
|
+
|
|
284
|
+
case 'FAQ_KB_PUBLISH':
|
|
285
|
+
return actor + ' published chatbot ' + chatbotLabel(activity);
|
|
286
|
+
|
|
287
|
+
case 'KB_NAMESPACE_CREATE':
|
|
288
|
+
return actor + ' created namespace ' + namespaceLabel(activity);
|
|
289
|
+
|
|
290
|
+
case 'KB_NAMESPACE_DELETE':
|
|
291
|
+
return actor + ' deleted namespace ' + namespaceLabel(activity);
|
|
292
|
+
|
|
293
|
+
case 'KB_CONTENTS_ADD': {
|
|
294
|
+
const namespace = namespaceLabel(activity);
|
|
295
|
+
const addType = contentAddTypeLabel(actionObj.contentAddType);
|
|
296
|
+
const count = actionObj.count;
|
|
297
|
+
if (count && count > 1) {
|
|
298
|
+
return actor + ' added ' + count + ' items (' + addType + ') to namespace ' + namespace;
|
|
299
|
+
}
|
|
300
|
+
if (actionObj.source) {
|
|
301
|
+
return actor + ' added ' + addType + ' to namespace ' + namespace + ' (' + actionObj.source + ')';
|
|
302
|
+
}
|
|
303
|
+
return actor + ' added ' + addType + ' to namespace ' + namespace;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
case 'KB_CONTENTS_DELETE':
|
|
307
|
+
return actor + ' deleted all contents from namespace ' + namespaceLabel(activity);
|
|
308
|
+
|
|
309
|
+
case 'KB_CONTENT_DELETE':
|
|
310
|
+
return actor + ' deleted content ' + kbContentLabel(activity) +
|
|
311
|
+
' from namespace ' + namespaceLabel(activity);
|
|
312
|
+
|
|
313
|
+
default:
|
|
314
|
+
return null;
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function enrichActivityWithMessage(activity) {
|
|
319
|
+
const message = buildDefaultActivityMessage(activity);
|
|
320
|
+
if (!message) {
|
|
321
|
+
return activity;
|
|
322
|
+
}
|
|
323
|
+
return Object.assign({}, activity, { message: message });
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
module.exports = {
|
|
327
|
+
actorLabel,
|
|
328
|
+
targetUserLabel,
|
|
329
|
+
requestLabel,
|
|
330
|
+
namespaceLabel,
|
|
331
|
+
chatbotLabel,
|
|
332
|
+
kbContentLabel,
|
|
333
|
+
resolveParticipantLabel,
|
|
334
|
+
buildDefaultActivityMessage,
|
|
335
|
+
enrichActivityWithMessage
|
|
336
|
+
};
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
var mongoose = require('mongoose');
|
|
2
2
|
var Schema = mongoose.Schema;
|
|
3
3
|
|
|
4
|
+
const DEFAULT_ACTIVITY_TTL_SEC = 30 * 24 * 60 * 60; // 30 days
|
|
5
|
+
|
|
6
|
+
function ttlSecondsFromEnv(raw, fallbackSec) {
|
|
7
|
+
if (raw == null || String(raw).trim() === '') return fallbackSec;
|
|
8
|
+
const n = Number(raw);
|
|
9
|
+
return Number.isFinite(n) && n >= 0 ? n : fallbackSec;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
let expireAfterSeconds = ttlSecondsFromEnv(
|
|
13
|
+
process.env.ACTIVITY_EXPIRATION_TIME,
|
|
14
|
+
DEFAULT_ACTIVITY_TTL_SEC
|
|
15
|
+
);
|
|
16
|
+
|
|
4
17
|
// https://getstream.io/blog/designing-activity-stream-newsfeed-w3c-spec/
|
|
5
18
|
// {
|
|
6
19
|
// "@context": "http://www.w3.org/ns/activitystreams",
|
|
@@ -47,6 +60,65 @@ var ActorActivitySchema = new Schema({
|
|
|
47
60
|
},
|
|
48
61
|
});
|
|
49
62
|
|
|
63
|
+
// Secondary entity involved in / affected by the activity (a "third party" beyond actor and target).
|
|
64
|
+
// Example: "user1 assigned conversation xyz to user2" -> actor=user1, target=conversation, related=user2.
|
|
65
|
+
// `role` is kept as a free String (no enum) on purpose, to avoid save failures when new roles appear.
|
|
66
|
+
// Suggested role values: 'assignee', 'unassigned_user', 'previous_assignee', 'removed_participant'.
|
|
67
|
+
var RelatedActivitySchema = new Schema({
|
|
68
|
+
role: {
|
|
69
|
+
type: String,
|
|
70
|
+
required: false,
|
|
71
|
+
index: true
|
|
72
|
+
},
|
|
73
|
+
type: {
|
|
74
|
+
type: String,
|
|
75
|
+
required: false
|
|
76
|
+
},
|
|
77
|
+
id: {
|
|
78
|
+
type: String,
|
|
79
|
+
required: false,
|
|
80
|
+
index: true
|
|
81
|
+
},
|
|
82
|
+
name: {
|
|
83
|
+
type: String,
|
|
84
|
+
required: false
|
|
85
|
+
},
|
|
86
|
+
object: {
|
|
87
|
+
type: Schema.Types.Mixed,
|
|
88
|
+
required: false
|
|
89
|
+
}
|
|
90
|
+
}, { _id: false });
|
|
91
|
+
|
|
92
|
+
function userIdFromEntity(entity) {
|
|
93
|
+
if (!entity) {
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
if (entity.object && entity.object.id_user) {
|
|
97
|
+
var u = entity.object.id_user;
|
|
98
|
+
return String((u && (u._id || u.id)) || u);
|
|
99
|
+
}
|
|
100
|
+
if ((entity.type === 'user') && entity.id) {
|
|
101
|
+
return String(entity.id);
|
|
102
|
+
}
|
|
103
|
+
return null;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function computeInvolvedUserIds(doc) {
|
|
107
|
+
var ids = new Set();
|
|
108
|
+
if (doc.actor && doc.actor.type === 'user' && doc.actor.id) {
|
|
109
|
+
ids.add(String(doc.actor.id));
|
|
110
|
+
}
|
|
111
|
+
var targetUserId = userIdFromEntity(doc.target);
|
|
112
|
+
if (targetUserId) {
|
|
113
|
+
ids.add(targetUserId);
|
|
114
|
+
}
|
|
115
|
+
var relatedUserId = userIdFromEntity(doc.related);
|
|
116
|
+
if (relatedUserId) {
|
|
117
|
+
ids.add(relatedUserId);
|
|
118
|
+
}
|
|
119
|
+
return Array.from(ids);
|
|
120
|
+
}
|
|
121
|
+
|
|
50
122
|
var ActivitySchema = new Schema({
|
|
51
123
|
|
|
52
124
|
actor: {
|
|
@@ -71,6 +143,18 @@ var ActivitySchema = new Schema({
|
|
|
71
143
|
required: true,
|
|
72
144
|
// index: true
|
|
73
145
|
},
|
|
146
|
+
related: {
|
|
147
|
+
type: RelatedActivitySchema,
|
|
148
|
+
required: false
|
|
149
|
+
},
|
|
150
|
+
// Denormalized list of user ids involved in the activity (actor, target user, related user).
|
|
151
|
+
// Auto-filled by the pre-save hook below. Indexed for efficient "activities of a user" queries.
|
|
152
|
+
involvedUserIds: {
|
|
153
|
+
type: [String],
|
|
154
|
+
required: false,
|
|
155
|
+
index: true,
|
|
156
|
+
default: undefined
|
|
157
|
+
},
|
|
74
158
|
// summary A natural language summarization of the object encoded as HTML. Multiple language tagged summaries may be provided.
|
|
75
159
|
// summaryMap https://www.w3.org/TR/activitystreams-vocabulary/#dfn-summary
|
|
76
160
|
id_project: {
|
|
@@ -83,8 +167,21 @@ var ActivitySchema = new Schema({
|
|
|
83
167
|
}
|
|
84
168
|
);
|
|
85
169
|
|
|
86
|
-
ActivitySchema.
|
|
170
|
+
ActivitySchema.pre('save', function (next) {
|
|
171
|
+
try {
|
|
172
|
+
var ids = computeInvolvedUserIds(this);
|
|
173
|
+
this.involvedUserIds = ids.length > 0 ? ids : undefined;
|
|
174
|
+
} catch (e) {
|
|
175
|
+
// never block saving an activity because of the denormalized field
|
|
176
|
+
}
|
|
177
|
+
next();
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
ActivitySchema.index({ id_project: 1, createdAt: -1 });
|
|
181
|
+
ActivitySchema.index({ id_project: 1, 'actor.id': 1, createdAt: -1 });
|
|
182
|
+
ActivitySchema.index({ id_project: 1, 'target.object.id_user._id': 1, createdAt: -1 });
|
|
183
|
+
ActivitySchema.index({ id_project: 1, involvedUserIds: 1, createdAt: -1 });
|
|
184
|
+
ActivitySchema.index({ createdAt: 1 }, { expireAfterSeconds: expireAfterSeconds });
|
|
87
185
|
|
|
88
|
-
// TODO metti indice per query è lentina
|
|
89
186
|
|
|
90
187
|
module.exports = mongoose.model('activity', ActivitySchema);
|