@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.
@@ -1217,6 +1217,7 @@ describe('FaqKBRoute', () => {
1217
1217
  if (err) { console.error("err: ", err); }
1218
1218
  if (log) { console.log("res.body", res.body); }
1219
1219
 
1220
+ console.log("chatbot trashed: ", res.body);
1220
1221
  res.should.have.status(200);
1221
1222
  res.body.should.be.a('object');
1222
1223
  expect(res.body.trashed).to.equal(true);
package/test/kbRoute.js CHANGED
@@ -9,6 +9,7 @@ process.env.PINECONE_INDEX_HYBRID = "test-index-hybrid";
9
9
  process.env.PINECONE_TYPE_HYBRID = "serverless";
10
10
  process.env.ADMIN_EMAIL = "admin@tiledesk.com";
11
11
  process.env.KB_ENDPOINT_TRAIN = "http://kb-train.test";
12
+ process.env.ACTIVITY_HISTORY_ENABLED = true;
12
13
 
13
14
  var userService = require('../services/userService');
14
15
  var projectService = require('../services/projectService');
@@ -792,6 +793,64 @@ describe('KbRoute', () => {
792
793
  });
793
794
  }).timeout(10000)
794
795
 
796
+ it('delete-content', (done) => {
797
+
798
+ var email = "test-signup-" + Date.now() + "@email.com";
799
+ var pwd = "pwd";
800
+
801
+ userService.signup(email, pwd, "Test Firstname", "Test lastname").then(function (savedUser) {
802
+ projectService.create("test-faqkb-create", savedUser._id).then(function (savedProject) {
803
+
804
+ chai.request(server)
805
+ .get('/' + savedProject._id + '/kb/namespace/all')
806
+ .auth(email, pwd)
807
+ .end((err, res) => {
808
+ if (err) { console.error("err: ", err); }
809
+ if (log) { console.log("get namespaces res.body: ", res.body); }
810
+
811
+ res.should.have.status(200);
812
+
813
+ let namespace_id = res.body[0].id;
814
+
815
+ let kb = {
816
+ name: "example_text_delete",
817
+ type: "text",
818
+ source: "example_text_delete",
819
+ content: "Example text",
820
+ namespace: namespace_id
821
+ }
822
+
823
+ chai.request(server)
824
+ .post('/' + savedProject._id + '/kb')
825
+ .auth(email, pwd)
826
+ .send(kb)
827
+ .end((err, res) => {
828
+ if (err) { console.error("err: ", err); }
829
+ if (log) { console.log("create kb res.body: ", res.body); }
830
+
831
+ res.should.have.status(200);
832
+
833
+ let kb_id = res.body.data.value._id;
834
+
835
+ chai.request(server)
836
+ .delete('/' + savedProject._id + '/kb/' + kb_id)
837
+ .auth(email, pwd)
838
+ .end((err, res) => {
839
+ if (err) { console.error("err: ", err); }
840
+ if (log) { console.log("delete kb res.body: ", res.body); }
841
+
842
+ res.should.have.status(200);
843
+ expect(res.body.success).to.equal(true);
844
+ expect(res.body.message).to.equal("Content deleted successfully");
845
+
846
+ done();
847
+ })
848
+ })
849
+ })
850
+ })
851
+ })
852
+ })
853
+
795
854
  it('get-content-chunks', (done) => {
796
855
 
797
856
  var email = "test-signup-" + Date.now() + "@email.com";
@@ -0,0 +1,167 @@
1
+ 'use strict';
2
+
3
+ const User = require('../models/user');
4
+ const winston = require('../config/winston');
5
+ const projectUserUpdateContextUtil = require('./projectUserUpdateContextUtil');
6
+
7
+ function actorFromReq(req) {
8
+ if (!req || !req.user) {
9
+ return { type: 'system', id: 'system', name: 'System' };
10
+ }
11
+ return projectUserUpdateContextUtil.actorFromPrincipal(req.user);
12
+ }
13
+
14
+ function actorFromUserId(userId) {
15
+ if (!userId) {
16
+ return { type: 'system', id: 'system', name: 'System' };
17
+ }
18
+ const id = String(userId);
19
+ return { type: 'user', id: id, name: id };
20
+ }
21
+
22
+ function resolveId(value) {
23
+ if (!value) {
24
+ return null;
25
+ }
26
+ if (value._id) {
27
+ return String(value._id);
28
+ }
29
+ return String(value);
30
+ }
31
+
32
+ function resolveUserFromParticipatingAgents(agents, userId) {
33
+ if (!Array.isArray(agents) || !userId) {
34
+ return null;
35
+ }
36
+
37
+ for (const agent of agents) {
38
+ const id = agent._id || agent.id;
39
+ if (id && String(id) === String(userId)) {
40
+ return agent;
41
+ }
42
+ }
43
+
44
+ return null;
45
+ }
46
+
47
+ function resolveUserFromSnapshotAgents(agents, userId) {
48
+ if (!Array.isArray(agents) || !userId) {
49
+ return null;
50
+ }
51
+
52
+ for (const projectUser of agents) {
53
+ const user = projectUser.id_user || projectUser;
54
+ const id = (user && (user._id || user.id)) || projectUser._id;
55
+ if (id && String(id) === String(userId)) {
56
+ return user;
57
+ }
58
+ }
59
+
60
+ return null;
61
+ }
62
+
63
+ function isSystemClosedBy(closedBy) {
64
+ if (!closedBy) {
65
+ return true;
66
+ }
67
+ const value = String(closedBy).toLowerCase();
68
+ return value === 'system' || value === '_trigger';
69
+ }
70
+
71
+ function actorFromClosedBy(request) {
72
+ if (!request) {
73
+ return { type: 'system', id: 'system', name: 'System' };
74
+ }
75
+
76
+ const closedBy = request.closed_by;
77
+ if (isSystemClosedBy(closedBy)) {
78
+ return { type: 'system', id: 'system', name: 'System' };
79
+ }
80
+
81
+ const userId = String(closedBy);
82
+ let user = resolveUserFromParticipatingAgents(request.participatingAgents, userId);
83
+ if (!user && request.snapshot) {
84
+ user = resolveUserFromSnapshotAgents(request.snapshot.agents, userId);
85
+ }
86
+
87
+ const name = (user && projectUserUpdateContextUtil.userDisplayName(user)) ||
88
+ request.closed_by_name ||
89
+ userId;
90
+
91
+ return {
92
+ type: 'user',
93
+ id: userId,
94
+ name: name
95
+ };
96
+ }
97
+
98
+ function actorFromRequestCreate(request) {
99
+ const requesterId = request && request.requester_id;
100
+ const lead = request && request.lead;
101
+ const leadId = lead && (lead._id || lead.id);
102
+ const leadFullname = lead && (lead.fullname || lead.fullName);
103
+ const participantsBots = (request && request.participantsBots) || [];
104
+ const participatingBotIds = (request && request.participatingBots || []).map(function (bot) {
105
+ return String((bot && (bot._id || bot.id)) || bot);
106
+ });
107
+
108
+ winston.info('ActivityArchiver REQUEST_CREATE actor resolution', {
109
+ request_id: request && request.request_id,
110
+ request_mongo_id: request && request._id && String(request._id),
111
+ requester_id: requesterId != null ? String(requesterId) : null,
112
+ requester_name: request && request.requester_name,
113
+ createdBy: request && request.createdBy,
114
+ lead_id: leadId != null ? String(leadId) : null,
115
+ lead_fullname: leadFullname,
116
+ participantsBots: participantsBots,
117
+ participatingBotIds: participatingBotIds,
118
+ hasBot: request && request.hasBot,
119
+ note: 'REQUEST_CREATE actor is always type=user with id=requester_id (lead virtual). JWT/token is not used here.'
120
+ });
121
+
122
+ const actor = {
123
+ type: 'user',
124
+ id: requesterId != null ? String(requesterId) : undefined,
125
+ name: (request && request.requester_name) || leadFullname || undefined
126
+ };
127
+
128
+ if (participantsBots.length > 0 || participatingBotIds.length > 0) {
129
+ winston.info('ActivityArchiver REQUEST_CREATE bot participants present (not used as actor)', {
130
+ request_id: request && request.request_id,
131
+ actor: actor,
132
+ participantsBots: participantsBots,
133
+ participatingBotIds: participatingBotIds
134
+ });
135
+ }
136
+
137
+ return actor;
138
+ }
139
+
140
+ async function resolveActorFromClosedBy(request) {
141
+ const actor = actorFromClosedBy(request);
142
+
143
+ if (actor.type !== 'user' || actor.name !== actor.id) {
144
+ return actor;
145
+ }
146
+
147
+ try {
148
+ const user = await User.findById(actor.id).select('firstname lastname email').lean().exec();
149
+ const displayName = user && projectUserUpdateContextUtil.userDisplayName(user);
150
+ if (displayName) {
151
+ return Object.assign({}, actor, { name: displayName });
152
+ }
153
+ } catch (err) {
154
+ // keep id fallback
155
+ }
156
+
157
+ return actor;
158
+ }
159
+
160
+ module.exports = {
161
+ actorFromReq,
162
+ actorFromUserId,
163
+ actorFromRequestCreate,
164
+ actorFromClosedBy,
165
+ resolveActorFromClosedBy,
166
+ resolveId
167
+ };
@@ -0,0 +1,283 @@
1
+ 'use strict';
2
+
3
+ const projectUserUpdateContextUtil = require('./projectUserUpdateContextUtil');
4
+ const winston = require('../config/winston');
5
+
6
+ function systemActor() {
7
+ return { type: 'system', id: 'system', name: 'System' };
8
+ }
9
+
10
+ function actorFromUser(user) {
11
+ return projectUserUpdateContextUtil.actorFromPrincipal(user);
12
+ }
13
+
14
+ function assignmentActorContext(req, defaultSource) {
15
+ const source = defaultSource || 'api';
16
+
17
+ if (!req || !req.user) {
18
+ return { actor: systemActor(), source: 'system' };
19
+ }
20
+
21
+ if (projectUserUpdateContextUtil.isSubscriptionActor(req.user)) {
22
+ return { actor: systemActor(), source: 'subscription' };
23
+ }
24
+
25
+ const actor = actorFromUser(req.user);
26
+ const isBot = projectUserUpdateContextUtil.isBotActor(req.user);
27
+
28
+ if (isBot && actor.type !== 'bot') {
29
+ winston.warn('assignmentActorContext bot principal resolved as non-bot actor', {
30
+ actor: actor,
31
+ principal_id: projectUserUpdateContextUtil.resolveUserId(req.user),
32
+ principal_name: req.user && req.user.name,
33
+ principal_type: req.user && req.user.type,
34
+ principal_subtype: req.user && req.user.subtype,
35
+ principal_sub: req.user && req.user.sub,
36
+ principal_model: req.user && req.user.constructor && req.user.constructor.modelName,
37
+ source: source,
38
+ url: req.originalUrl
39
+ });
40
+ } else {
41
+ winston.info('assignmentActorContext actor resolution', {
42
+ actor: actor,
43
+ principal_id: projectUserUpdateContextUtil.resolveUserId(req.user),
44
+ principal_name: req.user && req.user.name,
45
+ principal_type: req.user && req.user.type,
46
+ principal_subtype: req.user && req.user.subtype,
47
+ principal_sub: req.user && req.user.sub,
48
+ principal_model: req.user && req.user.constructor && req.user.constructor.modelName,
49
+ isBotActor: isBot,
50
+ source: source,
51
+ url: req.originalUrl
52
+ });
53
+ }
54
+
55
+ return { actor: actor, source: source };
56
+ }
57
+
58
+ function reconcileAssignmentPayload(data) {
59
+ const source = (data && data.source) || 'system';
60
+ let actor = (data && data.actor) || systemActor();
61
+
62
+ if (source === 'subscription' || actor.type === 'system') {
63
+ return {
64
+ actor: systemActor(),
65
+ source: source === 'subscription' ? 'subscription' : source
66
+ };
67
+ }
68
+
69
+ return { actor: actor, source: source };
70
+ }
71
+
72
+ function isHumanParticipant(participantId) {
73
+ return participantId && !String(participantId).startsWith('bot_');
74
+ }
75
+
76
+ function filterHumanParticipants(participantIds) {
77
+ return (participantIds || []).filter(isHumanParticipant);
78
+ }
79
+
80
+ function isBotParticipant(participantId) {
81
+ return participantId && String(participantId).startsWith('bot_');
82
+ }
83
+
84
+ function filterBotParticipants(participantIds) {
85
+ return (participantIds || []).filter(isBotParticipant);
86
+ }
87
+
88
+ function botIdFromParticipant(participantId) {
89
+ return String(participantId).replace('bot_', '');
90
+ }
91
+
92
+ function resolveBotName(requestComplete, botParticipantOrId) {
93
+ const botId = isBotParticipant(botParticipantOrId)
94
+ ? botIdFromParticipant(botParticipantOrId)
95
+ : String(botParticipantOrId);
96
+ const bots = requestComplete && requestComplete.participatingBots;
97
+ if (Array.isArray(bots)) {
98
+ for (const bot of bots) {
99
+ const id = String(bot._id || bot.id);
100
+ if (id === botId) {
101
+ return bot.name || id;
102
+ }
103
+ }
104
+ }
105
+ return botId;
106
+ }
107
+
108
+ function resolveDepartmentName(requestComplete) {
109
+ const dep = requestComplete && requestComplete.department;
110
+ if (!dep) {
111
+ return undefined;
112
+ }
113
+ if (dep.name) {
114
+ return dep.name;
115
+ }
116
+ return dep._id ? String(dep._id) : undefined;
117
+ }
118
+
119
+ function deriveAssignmentType({ actor, assigneeIds, isUnassign }) {
120
+ if (isUnassign) {
121
+ return 'unassign';
122
+ }
123
+
124
+ const humanAssignees = filterHumanParticipants(assigneeIds);
125
+ if (humanAssignees.length === 0) {
126
+ return null;
127
+ }
128
+
129
+ if (!actor || actor.type === 'system') {
130
+ return 'auto';
131
+ }
132
+
133
+ const assigneeId = String(humanAssignees[0]);
134
+ if (String(actor.id) === assigneeId) {
135
+ return 'self_join';
136
+ }
137
+
138
+ return 'manual_reassign';
139
+ }
140
+
141
+ function verbFromAssignmentType(assignmentType) {
142
+ switch (assignmentType) {
143
+ case 'auto':
144
+ return 'REQUEST_ASSIGNED_AUTO';
145
+ case 'self_join':
146
+ return 'REQUEST_ASSIGNED_SELF';
147
+ case 'manual_reassign':
148
+ case 'manual_reassign_bot':
149
+ case 'manual_reassign_department':
150
+ return 'REQUEST_ASSIGNED_MANUAL';
151
+ case 'unassign':
152
+ return 'REQUEST_UNASSIGNED';
153
+ default:
154
+ return null;
155
+ }
156
+ }
157
+
158
+ function buildSetParticipantsOptions(req, participants) {
159
+ const { actor, source } = assignmentActorContext(req, 'api');
160
+
161
+ if (!participants || participants.length === 0) {
162
+ return {
163
+ actor,
164
+ source,
165
+ assignmentType: 'unassign'
166
+ };
167
+ }
168
+
169
+ const botParticipants = filterBotParticipants(participants);
170
+ const humanParticipants = filterHumanParticipants(participants);
171
+
172
+ if (botParticipants.length > 0 && humanParticipants.length === 0) {
173
+ return {
174
+ actor,
175
+ source,
176
+ assignmentType: 'manual_reassign_bot'
177
+ };
178
+ }
179
+
180
+ return {
181
+ actor,
182
+ source,
183
+ assignmentType: deriveAssignmentType({
184
+ actor,
185
+ assigneeIds: participants,
186
+ isUnassign: false
187
+ })
188
+ };
189
+ }
190
+
191
+ function buildAddParticipantOptions(req, member) {
192
+ const { actor, source } = assignmentActorContext(req, 'api');
193
+
194
+ return {
195
+ actor,
196
+ source,
197
+ assignmentType: deriveAssignmentType({
198
+ actor,
199
+ assigneeIds: [member],
200
+ isUnassign: false
201
+ })
202
+ };
203
+ }
204
+
205
+ function buildRemoveParticipantOptions(req, member) {
206
+ if (!isHumanParticipant(member)) {
207
+ return { trackLeave: false };
208
+ }
209
+
210
+ const { actor: reqActor, source } = assignmentActorContext(req, 'api');
211
+ const actor = req && req.user && String(reqActor.id) === String(member)
212
+ ? reqActor
213
+ : { type: 'user', id: String(member), name: String(member) };
214
+
215
+ return {
216
+ actor,
217
+ source,
218
+ member,
219
+ trackLeave: true
220
+ };
221
+ }
222
+
223
+ function buildLeaveParticipantOptions(source, member) {
224
+ if (!isHumanParticipant(member)) {
225
+ return { trackLeave: false };
226
+ }
227
+
228
+ return {
229
+ actor: { type: 'user', id: String(member), name: String(member) },
230
+ source: source || 'webhook',
231
+ member,
232
+ trackLeave: true
233
+ };
234
+ }
235
+
236
+ function buildAutoRouteOptions(req, source) {
237
+ const ctx = assignmentActorContext(req, source || 'api');
238
+ return {
239
+ actor: ctx.actor,
240
+ source: ctx.source,
241
+ assignmentType: 'auto'
242
+ };
243
+ }
244
+
245
+ function buildDepartmentRouteOptions(req, source) {
246
+ const ctx = assignmentActorContext(req, source || 'api');
247
+ return {
248
+ actor: ctx.actor,
249
+ source: ctx.source,
250
+ assignmentType: 'manual_reassign_department'
251
+ };
252
+ }
253
+
254
+ function buildInternalOptions(source, assignmentType) {
255
+ return {
256
+ actor: systemActor(),
257
+ source: source || 'system',
258
+ assignmentType: assignmentType || 'auto'
259
+ };
260
+ }
261
+
262
+ module.exports = {
263
+ systemActor,
264
+ actorFromUser,
265
+ assignmentActorContext,
266
+ reconcileAssignmentPayload,
267
+ isHumanParticipant,
268
+ filterHumanParticipants,
269
+ isBotParticipant,
270
+ filterBotParticipants,
271
+ botIdFromParticipant,
272
+ resolveBotName,
273
+ resolveDepartmentName,
274
+ deriveAssignmentType,
275
+ verbFromAssignmentType,
276
+ buildSetParticipantsOptions,
277
+ buildAddParticipantOptions,
278
+ buildRemoveParticipantOptions,
279
+ buildLeaveParticipantOptions,
280
+ buildAutoRouteOptions,
281
+ buildDepartmentRouteOptions,
282
+ buildInternalOptions
283
+ };
@@ -0,0 +1,12 @@
1
+ const BSON = require('bson');
2
+ const bson = new BSON();
3
+
4
+ function calculateBsonSize(document) {
5
+ if (document == null) return 0;
6
+ const plain = document.toObject ? document.toObject({ depopulate: true }) : document;
7
+ return bson.calculateObjectSize(plain);
8
+ }
9
+
10
+ module.exports = {
11
+ calculateBsonSize: calculateBsonSize,
12
+ };