@tiledesk/tiledesk-server 2.10.14 → 2.10.16

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.
Files changed (50) hide show
  1. package/CHANGELOG.md +9 -0
  2. package/app.js +2 -1
  3. package/models/request.js +8 -0
  4. package/package.json +4 -3
  5. package/pubmodules/activities/test/activityRoute.js +6 -2
  6. package/pubmodules/events/test/eventRoute.js +7 -3
  7. package/pubmodules/pubModulesManager.js +24 -0
  8. package/pubmodules/voice-twilio/index.js +6 -0
  9. package/pubmodules/voice-twilio/listener.js +59 -0
  10. package/routes/campaigns.js +1 -1
  11. package/routes/files.js +6 -4
  12. package/routes/images.js +0 -2
  13. package/routes/kb.js +7 -1
  14. package/routes/request.js +10 -0
  15. package/routes/users.js +2 -2
  16. package/services/fileGridFsService.js +12 -10
  17. package/services/requestService.js +2 -1
  18. package/test/app-test.js +36 -1
  19. package/test/authentication.js +662 -796
  20. package/test/authenticationJwt.js +213 -315
  21. package/test/authorization.js +53 -72
  22. package/test/campaignsRoute.js +42 -47
  23. package/test/cannedRoute.js +30 -16
  24. package/test/departmentService.js +222 -274
  25. package/test/example.json +31 -1
  26. package/test/faqRoute.js +713 -622
  27. package/test/faqService.js +124 -159
  28. package/test/faqkbRoute.js +128 -100
  29. package/test/fileRoute.js +50 -46
  30. package/test/imageRoute.js +263 -254
  31. package/test/jwtRoute.js +128 -153
  32. package/test/kbRoute.js +40 -17
  33. package/test/kbsettingsRoute.js +78 -54
  34. package/test/keysRoute.js +6 -7
  35. package/test/labelRoute.js +591 -696
  36. package/test/labelService.js +40 -47
  37. package/test/leadService.js +100 -115
  38. package/test/logsRoute.js +13 -7
  39. package/test/messageRootRoute.js +112 -102
  40. package/test/messageRoute.js +1171 -1419
  41. package/test/messageService.js +41 -43
  42. package/test/openaiRoute.js +5 -1
  43. package/test/projectRoute.js +23 -4
  44. package/test/projectService.js +3 -1
  45. package/test/quoteManager.js +36 -13
  46. package/test/requestRoute.js +103 -72
  47. package/test/requestService.js +51 -51
  48. package/test/userRoute.js +37 -8
  49. package/test/userService.js +34 -31
  50. package/utils/promiseUtil.js +1 -1
package/CHANGELOG.md CHANGED
@@ -5,6 +5,15 @@
5
5
  🚀 IN PRODUCTION 🚀
6
6
  (https://www.npmjs.com/package/@tiledesk/tiledesk-server/v/2.3.77)
7
7
 
8
+ # 2.10.16
9
+ - changed bodyParser.urlencoded extended to TRUE
10
+ - updated tybot-connector to 0.2.130
11
+ - added twilio voice module
12
+ - updated messenger-connectorto 0.1.22
13
+
14
+ # 2.10.15
15
+ - Readded event on fully_abandoned request
16
+
8
17
  # 2.10.14
9
18
  - Updated tybot-connector to 0.2.127
10
19
  - Restored request.js file
package/app.js CHANGED
@@ -280,6 +280,7 @@ if (process.env.ENABLE_ALTERNATIVE_CORS_MIDDLEWARE === "true") {
280
280
 
281
281
  // https://stackoverflow.com/questions/18710225/node-js-get-raw-request-body-using-express
282
282
 
283
+
283
284
  const JSON_BODY_LIMIT = process.env.JSON_BODY_LIMIT || '500KB';
284
285
  winston.debug("JSON_BODY_LIMIT : " + JSON_BODY_LIMIT);
285
286
 
@@ -293,7 +294,7 @@ app.use(bodyParser.json({limit: JSON_BODY_LIMIT,
293
294
  }
294
295
  }));
295
296
 
296
- app.use(bodyParser.urlencoded({limit: JSON_BODY_LIMIT, extended: false }));
297
+ app.use(bodyParser.urlencoded({limit: JSON_BODY_LIMIT, extended: true }));
297
298
 
298
299
  app.use(cookieParser());
299
300
  app.use(express.static(path.join(__dirname, 'public')));
package/models/request.js CHANGED
@@ -492,6 +492,14 @@ RequestSchema.index({ id_project: 1, createdAt: 1, preflight: 1});
492
492
  //suggested by atlas profiler. Used by auto closing requests
493
493
  RequestSchema.index({ hasBot: 1, status: 1, createdAt: 1});
494
494
 
495
+ // Evaluate following indexes
496
+ RequestSchema.index({ "channel.name": 1, id_project: 1, preflight: 1, "snapshot.requester.uuid_user": 1, createdAt: - 1, status: 1 })
497
+ RequestSchema.index({ id_project: 1, preflight: 1, "snapshot.agents.id_user": 1, updatedAt: -1, draft: 1, status: 1 })
498
+ RequestSchema.index({ id_project: 1, participants: 1, preflight: 1, updatedAt: -1, draft: 1, status: 1 })
499
+ RequestSchema.index({ id_project: 1, preflight: 1, updatedAt: -1, draft: 1, status: 1 })
500
+ RequestSchema.index({ id_project: 1, preflight: 1, "snapshot.requester.uuid_user": 1, createdAt: -1, status: 1 })
501
+ RequestSchema.index({ department: 1, id_project: 1, participants: 1, preflight: 1, createdAt: -1, status: 1 })
502
+
495
503
 
496
504
  // cannot index parallel arrays [agents] [participants] {"driv
497
505
  // RequestSchema.index({ id_project: 1, status: 1, preflight:1, participants:1, "agents.id_user":1, updatedAt: -1 }); //NN LO APPLICA
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@tiledesk/tiledesk-server",
3
3
  "description": "The Tiledesk server module",
4
- "version": "2.10.14",
4
+ "version": "2.10.16",
5
5
  "scripts": {
6
6
  "start": "node ./bin/www",
7
7
  "pretest": "mongodb-runner start",
@@ -44,15 +44,16 @@
44
44
  "@tiledesk/tiledesk-dialogflow-connector": "^1.8.4",
45
45
  "@tiledesk/tiledesk-json-rules-engine": "^4.0.3",
46
46
  "@tiledesk/tiledesk-kaleyra-proxy": "^0.1.7",
47
- "@tiledesk/tiledesk-messenger-connector": "^0.1.21",
47
+ "@tiledesk/tiledesk-messenger-connector": "^0.1.22",
48
48
  "@tiledesk/tiledesk-rasa-connector": "^1.0.10",
49
49
  "@tiledesk/tiledesk-telegram-connector": "^0.1.14",
50
50
  "@tiledesk/tiledesk-train-jobworker": "^0.0.11",
51
- "@tiledesk/tiledesk-tybot-connector": "^0.2.127",
51
+ "@tiledesk/tiledesk-tybot-connector": "^0.2.130",
52
52
  "@tiledesk/tiledesk-whatsapp-connector": "^0.1.73",
53
53
  "@tiledesk/tiledesk-whatsapp-jobworker": "^0.0.8",
54
54
  "@tiledesk/tiledesk-sms-connector": "^0.1.10",
55
55
  "@tiledesk/tiledesk-vxml-connector": "^0.1.49",
56
+ "@tiledesk/tiledesk-voice-twilio-connector": "^0.1.7",
56
57
  "amqplib": "^0.5.5",
57
58
  "app-root-path": "^3.0.0",
58
59
  "bcrypt-nodejs": "0.0.3",
@@ -12,6 +12,8 @@ let server = require('../../../app');
12
12
  let should = chai.should();
13
13
  var winston = require('../../../config/winston');
14
14
 
15
+ let log = false;
16
+
15
17
  // chai.config.includeStack = true;
16
18
 
17
19
  var expect = chai.expect;
@@ -56,8 +58,10 @@ describe('ActivityRoute', () => {
56
58
  .get('/'+ savedProject._id + '/activities')
57
59
  .auth(email, pwd)
58
60
  .end((err, res) => {
59
- //console.log("res", res);
60
- console.log("res.body", res.body);
61
+
62
+ if (err) { console.error("err: ", err); }
63
+ if (log) { console.log("res.body", res.body); }
64
+
61
65
  res.should.have.status(200);
62
66
  res.body.activities.should.be.a('array');
63
67
  expect(res.body.count).to.equal(1);
@@ -13,6 +13,8 @@ let chaiHttp = require('chai-http');
13
13
  let server = require('../../../app');
14
14
  let should = chai.should();
15
15
 
16
+ let log = false;
17
+
16
18
  // chai.config.includeStack = true;
17
19
 
18
20
  var expect = chai.expect;
@@ -40,8 +42,10 @@ describe('EventRoute', () => {
40
42
  .auth(email, pwd)
41
43
  .send({"name":"event1", attributes: {"attr1":"val1"}})
42
44
  .end((err, res) => {
43
- //console.log("res", res);
44
- console.log("res.body", res.body);
45
+
46
+ if (err) { console.error("err: ", err); }
47
+ if (log) { console.log("res.body", res.body); }
48
+
45
49
  res.should.have.status(200);
46
50
  res.body.should.be.a('object');
47
51
  expect(res.body.name).to.equal("event1");
@@ -50,7 +54,7 @@ describe('EventRoute', () => {
50
54
  Project_user.findOne({ id_user: savedUser.id, status: "active"} )
51
55
  .populate('events')
52
56
  .exec(function (err, project_user) {
53
- console.log("project_user", project_user.toJSON());
57
+ if (log) { console.log("project_user", project_user.toJSON()); }
54
58
  expect(project_user.events.length).to.equal(1);
55
59
  done();
56
60
  });
@@ -39,6 +39,9 @@ class PubModulesManager {
39
39
  this.voice = undefined;
40
40
  this.voiceRoute = undefined;
41
41
 
42
+ this.voiceTwilio = undefined;
43
+ this.voiceTwilioRoute = undefined;
44
+
42
45
  this.mqttTest = undefined;
43
46
  this.mqttTestRoute = undefined;
44
47
 
@@ -105,6 +108,10 @@ class PubModulesManager {
105
108
  app.use('/modules/voice', this.voiceRoute);
106
109
  winston.info("PubModulesManager voiceRoute controller loaded");
107
110
  }
111
+ if (this.voiceTwilioRoute) {
112
+ app.use('/modules/voice-twilio', this.voiceTwilioRoute);
113
+ winston.info("PubModulesManager voiceTwilioRoute controller loaded");
114
+ }
108
115
  if (this.mqttTestRoute) {
109
116
  app.use('/modules/mqttTest', this.mqttTestRoute);
110
117
  winston.info("PubModulesManager mqttTestRoute controller loaded");
@@ -364,6 +371,23 @@ class PubModulesManager {
364
371
  }
365
372
  }
366
373
 
374
+ try {
375
+ this.voiceTwilio = require('./voice-twilio');
376
+ winston.info("this.voiceTwilio: " + this.voiceTwilio);
377
+ this.voiceTwilio.listener.listen(config);
378
+
379
+ this.voiceTwilioRoute = this.voice.voiceTwilioRoute;
380
+
381
+ winston.info("PubModulesManager initialized apps (voiceTwilio).")
382
+ } catch(err) {
383
+ console.log("\n Unable to start voiceTwilio connector: ", err);
384
+ if (err.code == 'MODULE_NOT_FOUND') {
385
+ winston.info("PubModulesManager init apps module not found ");
386
+ } else {
387
+ winston.info("PubModulesManager error initializing init apps module", err);
388
+ }
389
+ }
390
+
367
391
  try {
368
392
  this.sms = require('./sms');
369
393
  winston.info("this.sms: " + this.sms);
@@ -0,0 +1,6 @@
1
+ const listener = require("./listener");
2
+
3
+ const twilio_voice = require('@tiledesk/tiledesk-voice-twilio-connector');
4
+ const twilioVoiceRoute = twilio_voice.router;
5
+
6
+ module.exports = { listener: listener, twilioVoiceRoute: twilioVoiceRoute }
@@ -0,0 +1,59 @@
1
+ const voice_twilio = require('@tiledesk/tiledesk-voice-twilio-connector');
2
+ let winston = require('../../config/winston');
3
+ let configGlobal = require('../../config/global');
4
+ const mongoose = require('mongoose');
5
+
6
+ const apiUrl = process.env.API_URL || configGlobal.apiUrl;
7
+ winston.info("TwilioVoice apiUrl: " + apiUrl);
8
+
9
+ const dbConnection = mongoose.connection;
10
+
11
+ class Listener {
12
+
13
+ listen(config) {
14
+
15
+ winston.info("TwilioVoice Listener listen");
16
+ if (config.databaseUri) {
17
+ winston.debug("TwilioVoice config databaseUri: " + config.databaseUri);
18
+ }
19
+
20
+ var port = process.env.CACHE_REDIS_PORT || 6379;
21
+ winston.debug("Redis port: "+ port);
22
+
23
+ var host = process.env.CACHE_REDIS_HOST || "127.0.0.1"
24
+ winston.debug("Redis host: "+ host);
25
+
26
+ var password = process.env.CACHE_REDIS_PASSWORD;
27
+ winston.debug("Redis password: "+ password);
28
+
29
+ let brand_name = null;
30
+ if (process.env.BRAND_NAME) {
31
+ brand_name = process.env.BRAND_NAME
32
+ }
33
+
34
+ let log = process.env.VOICE_TWILIO_LOG || false
35
+ winston.debug("Voice log: "+ log);
36
+
37
+ voice_twilio.startApp({
38
+ MONGODB_URI: config.databaseUri,
39
+ dbconnection: dbConnection,
40
+ API_URL: apiUrl,
41
+ BASE_URL: apiUrl + "/modules/voice",
42
+ REDIS_HOST: host,
43
+ REDIS_PORT: port,
44
+ REDIS_PASSWORD: password,
45
+ BRAND_NAME: brand_name,
46
+ log: log
47
+ }, (err) => {
48
+ if (!err) {
49
+ winston.info("Tiledesk Twilio Voice Connector proxy server succesfully started.");
50
+ } else {
51
+ winston.info("unable to start Tiledesk Twilio Voice Connector. " + err);
52
+ }
53
+ })
54
+ }
55
+ }
56
+
57
+ let listener = new Listener();
58
+
59
+ module.exports = listener;
@@ -325,7 +325,7 @@ router.post('/direct', async function (req, res) {
325
325
  channel_type: MessageConstants.CHANNEL_TYPE.DIRECT,
326
326
  channel: req.body.channel
327
327
  };
328
-
328
+
329
329
  winston.info("message before", message);
330
330
 
331
331
  jobManager.publish(
package/routes/files.js CHANGED
@@ -30,11 +30,13 @@ curl -u andrea.leo@f21.it:123456 \
30
30
  */
31
31
 
32
32
  router.post('/users', [passport.authenticate(['basic', 'jwt'], { session: false }), validtoken], upload.single('file'), (req, res, next) => {
33
+
34
+ console.log("asdasd")
33
35
  winston.info("files/users")
34
- return res.status(201).json({
35
- message: 'File uploded successfully',
36
- filename: req.file.filename
37
- });
36
+ return res.status(201).json({
37
+ message: 'File uploded successfully',
38
+ filename: req.file.filename
39
+ });
38
40
 
39
41
  });
40
42
 
package/routes/images.js CHANGED
@@ -211,7 +211,6 @@ uploadAvatar.single('file'), async (req, res, next) => {
211
211
  winston.error("Error finding project user: ", err);
212
212
  return res.status(500).send({ success: false, error: "Unable to find project user for user " + userid + "in project " + id_project });
213
213
  })
214
-
215
214
  if (!puser) {
216
215
  winston.warn("User" + userid + "don't belongs the project " + id_project);
217
216
  return res.status(401).send({ success: false, error: "You don't belong the chatbot's project" })
@@ -225,7 +224,6 @@ uploadAvatar.single('file'), async (req, res, next) => {
225
224
  entity_id = bot_id;
226
225
  }
227
226
 
228
-
229
227
  var destinationFolder = 'uploads/users/' + entity_id + "/images/";
230
228
  winston.debug("destinationFolder:"+destinationFolder);
231
229
 
package/routes/kb.js CHANGED
@@ -514,6 +514,10 @@ router.get('/namespace/:id/chunks/:content_id', async (req, res) => {
514
514
  let engine = ns.engine || default_engine;
515
515
  delete engine._id;
516
516
 
517
+ if (process.env.NODE_ENV === 'test') {
518
+ return res.status(200).send({ success: true, message: "Get chunks skipped in test environment"});
519
+ }
520
+
517
521
  openaiService.getContentChunks(namespace_id, content_id, engine).then((resp) => {
518
522
  let chunks = resp.data;
519
523
  winston.debug("chunks for content " + content_id);
@@ -1119,7 +1123,9 @@ router.post('/multi', upload.single('uploadFile'), async (req, res) => {
1119
1123
  });
1120
1124
  winston.verbose("resources to be sent to worker: ", resources);
1121
1125
 
1122
- scheduleScrape(resources);
1126
+ if (!process.env.NODE_ENV) {
1127
+ scheduleScrape(resources);
1128
+ }
1123
1129
  res.status(200).send(result);
1124
1130
 
1125
1131
  }).catch((err) => {
package/routes/request.js CHANGED
@@ -30,6 +30,7 @@ csv.separator = ';';
30
30
 
31
31
  const { check, validationResult } = require('express-validator');
32
32
  const RoleConstants = require('../models/roleConstants');
33
+ const eventService = require('../pubmodules/events/eventService');
33
34
 
34
35
  // var messageService = require('../services/messageService');
35
36
 
@@ -451,6 +452,11 @@ router.delete('/:requestid/participants/:participantid', function (req, res) {
451
452
  router.put('/:requestid/assign', function (req, res) {
452
453
  winston.debug(req.body);
453
454
 
455
+ let user = req.user;
456
+ let pu;
457
+ if (req.projectuser) {
458
+ pu = req.projectuser._id
459
+ }
454
460
  // leggi la request se già assegnata o già chiusa (1000) esci
455
461
 
456
462
  //cacheinvalidation
@@ -475,6 +481,10 @@ router.put('/:requestid/assign', function (req, res) {
475
481
 
476
482
  winston.debug("department changed", updatedRequest);
477
483
 
484
+ if (updatedRequest.status === RequestConstants.ABANDONED) {
485
+ eventService.emit('request.fully_abandoned', updatedRequest, req.projectid, pu, user._id, undefined, user)
486
+ }
487
+
478
488
  return res.json(updatedRequest);
479
489
  }).catch(function (error) {
480
490
  winston.error('Error changing the department.', error)
package/routes/users.js CHANGED
@@ -215,11 +215,11 @@ router.post('/loginemail', function (req, res) {
215
215
  let namespace_id = req.body.namespace_id;
216
216
 
217
217
  if (!project_id) {
218
- res.status(500).send({ success: false, error: "missing 'id_project' field" });
218
+ return res.status(500).send({ success: false, error: "missing 'id_project' field" });
219
219
  }
220
220
 
221
221
  if (!chatbot_id && !namespace_id) {
222
- res.status(500).send({ success: false, error: "missing 'bot_id' or 'namespace_id' field" });
222
+ return res.status(500).send({ success: false, error: "missing 'bot_id' or 'namespace_id' field" });
223
223
  }
224
224
 
225
225
  User.findById(user_id, (err, user) => {
@@ -20,6 +20,9 @@ class FileGridFsService extends FileService {
20
20
  // DB
21
21
  this.mongoURI = process.env.DATABASE_URI || process.env.MONGODB_URI || config.database;
22
22
 
23
+ if (process.env.NODE_ENV === 'test') {
24
+ this.mongoURI = config.databasetest;
25
+ }
23
26
 
24
27
  // // connection
25
28
  this.conn = mongoose.createConnection(this.mongoURI, {
@@ -133,8 +136,8 @@ class FileGridFsService extends FileService {
133
136
  }
134
137
 
135
138
  getStorage(folderName) {
136
- const storageMongo = new GridFsStorage({
137
- url : this.mongoURI,
139
+ const storageMongo = new GridFsStorage({
140
+ url: this.mongoURI,
138
141
  options: { useNewUrlParser: true, useUnifiedTopology: true },
139
142
  file: (req, file) => {
140
143
  var folder = uuidv4();
@@ -154,32 +157,31 @@ class FileGridFsService extends FileService {
154
157
  // console.log("XXX file",file)
155
158
 
156
159
  // if (req.body.folder) {
157
-
160
+
158
161
  // folder = req.body.folder;
159
162
  // }
160
163
 
161
164
  var subfolder = "/public";
162
165
  if (req.user && req.user.id) {
163
- subfolder = "/users/"+req.user.id;
166
+ subfolder = "/users/" + req.user.id;
164
167
  }
165
- const path = 'uploads'+ subfolder + "/" + folderName + "/" + folder ;
168
+ const path = 'uploads' + subfolder + "/" + folderName + "/" + folder;
166
169
  req.folder = folder;
167
-
168
170
  // const match = ["image/png", "image/jpeg"];
169
-
171
+
170
172
  // if (match.indexOf(file.mimetype) === -1) {
171
173
  // const filename = `${Date.now()}-${file.originalname}`;
172
174
  // return filename;
173
175
  // }
174
-
176
+
175
177
  return {
176
178
  bucketName: folderName,
177
179
  filename: `${path}/${file.originalname}`
178
180
  };
179
181
  }
180
- });
182
+ });
181
183
 
182
- return storageMongo;
184
+ return storageMongo;
183
185
  }
184
186
 
185
187
 
@@ -774,6 +774,7 @@ class RequestService {
774
774
  })
775
775
  }
776
776
 
777
+ // DEPRECATED
777
778
  async _create(request) {
778
779
 
779
780
  var startDate = new Date();
@@ -1072,7 +1073,7 @@ class RequestService {
1072
1073
 
1073
1074
  }
1074
1075
 
1075
-
1076
+ // DEPRECATED
1076
1077
  async __create(request) {
1077
1078
 
1078
1079
  var startDate = new Date();
package/test/app-test.js CHANGED
@@ -1,4 +1,5 @@
1
1
  process.env.NODE_ENV = 'test';
2
+ process.env.LOG_LEVEL = 'critical'
2
3
 
3
4
  let chai = require('chai');
4
5
  let chaiHttp = require('chai-http');
@@ -18,7 +19,7 @@ describe('Root Test', () => {
18
19
 
19
20
  /**
20
21
  * Try to perform a request with an invalid id project.
21
- * A fake id or non existent mongo id (like user id) is passed.
22
+ * A fake id is passed.
22
23
  * This test must respond with status 400 and with an error.
23
24
  */
24
25
  it("Wrong request url", (done) => {
@@ -31,6 +32,39 @@ describe('Root Test', () => {
31
32
 
32
33
  let fake_id_project = "fake1234"
33
34
 
35
+ chai.request(server)
36
+ //.get('/' + savedUser._id + '/faq_kb')
37
+ .get('/' + fake_id_project + '/faq_kb')
38
+ .auth(email, pwd)
39
+ .end((err, res) => {
40
+ if (err) { console.error("err: ", err); }
41
+ if (log) { console.log("res.body", res.body); }
42
+
43
+ res.should.have.status(400);
44
+ assert.notEqual(res.body.error, null);
45
+ expect(res.body.error).to.equal("Invalid project id: " + fake_id_project)
46
+ done();
47
+ })
48
+ })
49
+ })
50
+ }).timeout(5000)
51
+
52
+ /**
53
+ * Try to perform a request with an invalid id project.
54
+ * A non existent mongo id (like user id) is passed.
55
+ * This test must respond with status 400 and with an error.
56
+ */
57
+ it("Wrong request url", (done) => {
58
+
59
+ var email = "test-signup-" + Date.now() + "@email.com";
60
+ var pwd = "pwd";
61
+
62
+ userService.signup(email, pwd, "Test Firstname", "Test Lastname").then((savedUser) => {
63
+ projectService.create("test-root-project", savedUser._id).then((savedProject) => {
64
+
65
+ // A real valid mongo id without a corresponding project
66
+ let fake_id_project = savedUser._id;
67
+
34
68
  chai.request(server)
35
69
  //.get('/' + savedUser._id + '/faq_kb')
36
70
  .get('/' + fake_id_project + '/faq_kb')
@@ -47,4 +81,5 @@ describe('Root Test', () => {
47
81
  })
48
82
  })
49
83
  }).timeout(5000)
84
+
50
85
  })