@tiledesk/tiledesk-server 2.5.3 → 2.7.0

Sign up to get free protection for your applications and to get access to all the features.
package/routes/kb.js CHANGED
@@ -2,8 +2,26 @@ var express = require('express');
2
2
  var { KB } = require('../models/kb_setting');
3
3
  var router = express.Router();
4
4
  var winston = require('../config/winston');
5
+ var multer = require('multer')
6
+ var upload = multer()
5
7
  const openaiService = require('../services/openaiService');
8
+ const JobManager = require('../utils/jobs-worker-queue-manager/JobManagerV2');
9
+ const { Scheduler } = require('../services/Scheduler');
6
10
 
11
+ const Sitemapper = require('sitemapper');
12
+
13
+ const AMQP_MANAGER_URL = process.env.AMQP_MANAGER_URL;
14
+ const JOB_TOPIC_EXCHANGE = process.env.JOB_TOPIC_EXCHANGE_TRAIN || 'tiledesk-trainer';
15
+
16
+ let jobManager = new JobManager(AMQP_MANAGER_URL, {
17
+ debug: false,
18
+ topic: JOB_TOPIC_EXCHANGE,
19
+ exchange: JOB_TOPIC_EXCHANGE
20
+ })
21
+
22
+ jobManager.connectAndStartPublisher(() => {
23
+ winston.info("ConnectPublisher done");
24
+ })
7
25
 
8
26
  router.get('/', async (req, res) => {
9
27
 
@@ -27,7 +45,7 @@ router.get('/', async (req, res) => {
27
45
  limit = parseInt(req.query.limit);
28
46
  winston.debug("Get kb limit: " + limit)
29
47
  }
30
-
48
+
31
49
  if (req.query.page) {
32
50
  page = parseInt(req.query.page);
33
51
  winston.debug("Get kb page: " + page)
@@ -55,54 +73,54 @@ router.get('/', async (req, res) => {
55
73
  let sortQuery = {};
56
74
  sortQuery[sortField] = direction;
57
75
  winston.debug("Get kb sortQuery: " + sortQuery);
58
-
76
+
59
77
  KB.countDocuments(query, (err, kbs_count) => {
60
- if (err) {
61
- winston.error("Find all kbs error: ", err);
62
- }
63
- winston.debug("KBs count: ", kbs_count);
64
-
65
- KB.find(query)
66
- .skip(skip)
67
- .limit(limit)
68
- .sort(sortQuery)
69
- .exec((err, kbs) => {
70
- if (err) {
71
- winston.error("Find all kbs error: ", err);
72
- return res.status(500).send({ success: false, error: err });
73
- }
74
-
75
- winston.debug("KBs found: ", kbs);
76
-
77
- let response = {
78
- count: kbs_count,
79
- query: {},
80
- kbs: kbs
81
- }
82
- if (status) {
83
- response.query.status = status;
84
- }
85
- if (limit) {
86
- response.query.limit = limit;
87
- }
88
- if (status) {
89
- response.query.page = page;
90
- }
91
- if (sortField) {
92
- response.query.sortField = sortField;
93
- }
94
- if (direction) {
95
- response.query.direction = direction;
96
- }
97
- if (text) {
98
- response.query.search = text;
99
- }
100
-
101
-
102
- return res.status(200).send(response);
103
- })
78
+ if (err) {
79
+ winston.error("Find all kbs error: ", err);
80
+ }
81
+ winston.debug("KBs count: ", kbs_count);
104
82
 
105
- })
83
+ KB.find(query)
84
+ .skip(skip)
85
+ .limit(limit)
86
+ .sort(sortQuery)
87
+ .exec((err, kbs) => {
88
+ if (err) {
89
+ winston.error("Find all kbs error: ", err);
90
+ return res.status(500).send({ success: false, error: err });
91
+ }
92
+
93
+ winston.debug("KBs found: ", kbs);
94
+
95
+ let response = {
96
+ count: kbs_count,
97
+ query: {},
98
+ kbs: kbs
99
+ }
100
+ if (status) {
101
+ response.query.status = status;
102
+ }
103
+ if (limit) {
104
+ response.query.limit = limit;
105
+ }
106
+ if (status) {
107
+ response.query.page = page;
108
+ }
109
+ if (sortField) {
110
+ response.query.sortField = sortField;
111
+ }
112
+ if (direction) {
113
+ response.query.direction = direction;
114
+ }
115
+ if (text) {
116
+ response.query.search = text;
117
+ }
118
+
119
+
120
+ return res.status(200).send(response);
121
+ })
122
+
123
+ })
106
124
 
107
125
 
108
126
  })
@@ -127,6 +145,31 @@ router.post('/', async (req, res) => {
127
145
  let project_id = req.projectid;
128
146
  let body = req.body;
129
147
 
148
+ // add or override namespace value if it is passed for security reason
149
+ body.namespace = project_id;
150
+
151
+ // if (req.body.namespace) {
152
+ // if (req.body.namespace != req.projectid) {
153
+ // return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project."})
154
+ // }
155
+ // }
156
+
157
+ let quoteManager = req.app.get('quote_manager');
158
+ let limits = await quoteManager.getPlanLimits(req.project);
159
+ let kbs_limit = limits.kbs;
160
+ winston.verbose("Limit of kbs for current plan: " + kbs_limit);
161
+
162
+ let kbs_count = await KB.countDocuments({ id_project: project_id }).exec();
163
+ winston.verbose("Kbs count: " + kbs_count);
164
+
165
+ if (kbs_count >= kbs_limit) {
166
+ return res.status(403).send({ success: false, error: "Maximum number of resources reached for the current plan", plan_limit: kbs_limit })
167
+ }
168
+
169
+ let total_count = kbs_count + 1;
170
+ if (total_count > kbs_limit) {
171
+ return res.status(403).send({ success: false, error: "Cannot exceed the number of resources in the current plan", plan_limit: kbs_limit })
172
+ }
130
173
  let new_kb = {
131
174
  id_project: project_id,
132
175
  name: body.name,
@@ -141,7 +184,6 @@ router.post('/', async (req, res) => {
141
184
  }
142
185
  winston.debug("adding kb: ", new_kb);
143
186
 
144
-
145
187
  KB.findOneAndUpdate({ id_project: project_id, type: 'url', source: new_kb.source }, new_kb, { upsert: true, new: true, rawResult: true }, async (err, raw) => {
146
188
  if (err) {
147
189
  winston.error("findOneAndUpdate with upsert error: ", err);
@@ -163,17 +205,118 @@ router.post('/', async (req, res) => {
163
205
  json.content = raw.value.content;
164
206
  }
165
207
 
166
- startScrape(json).then((response) => {
167
- winston.verbose("startScrape response: ", response);
168
- }).catch((err) => {
169
- winston.error("startScrape err: ", err);
170
- })
208
+ let resources = [];
209
+
210
+ resources.push(json);
211
+ scheduleScrape(resources);
212
+
213
+ // startScrape(json).then((response) => {
214
+ // winston.verbose("startScrape response: ", response);
215
+ // }).catch((err) => {
216
+ // winston.error("startScrape err: ", err);
217
+ // })
218
+
219
+ }
220
+ })
221
+
222
+ })
223
+
224
+
225
+ router.post('/multi', upload.single('uploadFile'), async (req, res) => {
226
+
227
+ let list;
228
+ if (req.file) {
229
+ file_string = req.file.buffer.toString('utf-8');
230
+ list = file_string.trim().split('\n');
231
+ } else {
232
+ list = req.body.list;
233
+ }
234
+
235
+ let project_id = req.projectid;
236
+
237
+ let quoteManager = req.app.get('quote_manager');
238
+ let limits = await quoteManager.getPlanLimits(req.project);
239
+ let kbs_limit = limits.kbs;
240
+ winston.verbose("Limit of kbs for current plan: " + kbs_limit);
241
+
242
+ let kbs_count = await KB.countDocuments({ id_project: project_id }).exec();
243
+ winston.verbose("Kbs count: " + kbs_count);
244
+
245
+ if (kbs_count >= kbs_limit) {
246
+ return res.status(403).send({ success: false, error: "Maximum number of resources reached for the current plan", plan_limit: kbs_limit })
247
+ }
248
+
249
+ let total_count = kbs_count + list.length;
250
+ if (total_count > kbs_limit) {
251
+ return res.status(403).send({ success: false, error: "Cannot exceed the number of resources in the current plan", plan_limit: kbs_limit })
252
+ }
253
+
254
+ if (list.length > 300) {
255
+ winston.error("Too many urls. Can't index more than 300 urls at a time.");
256
+ return res.status(403).send({ success: false, error: "Too many urls. Can't index more than 300 urls at a time."})
257
+ }
171
258
 
259
+ let kbs = [];
260
+ list.forEach(url => {
261
+ kbs.push({
262
+ id_project: project_id,
263
+ name: url,
264
+ source: url,
265
+ type: 'url',
266
+ content: "",
267
+ namespace: project_id,
268
+ status: -1
269
+ })
270
+ })
271
+
272
+ let operations = kbs.map(doc => {
273
+ return {
274
+ updateOne: {
275
+ filter: { id_project: doc.id_project, type: 'url', source: doc.source },
276
+ update: doc,
277
+ upsert: true,
278
+ returnOriginal: false
279
+ }
172
280
  }
173
281
  })
174
282
 
283
+ saveBulk(operations, kbs, project_id).then((result) => {
284
+
285
+ let resources = result.map(({ name, status, __v, createdAt, updatedAt, id_project, ...keepAttrs }) => keepAttrs)
286
+ resources = resources.map(({ _id, ...rest }) => {
287
+ return { id: _id, ...rest };
288
+ });
289
+ winston.verbose("resources to be sent to worker: ", resources)
290
+ scheduleScrape(resources);
291
+ res.status(200).send(result);
292
+
293
+ }).catch((err) => {
294
+ winston.error("Unable to save kbs in bulk ", err)
295
+ res.status(500).send(err);
296
+ })
297
+
298
+ })
299
+
300
+ router.post('/sitemap', async (req, res) => {
301
+
302
+ let sitemap_url = req.body.sitemap;
303
+
304
+ const sitemap = new Sitemapper({
305
+ url: sitemap_url,
306
+ timeout: 15000
307
+ });
308
+
309
+ sitemap.fetch().then((data) => {
310
+ winston.debug("data: ", data);
311
+ res.status(200).send(data);
312
+ }).catch((err) => {
313
+ console.error("err ", err)
314
+ res.status(500).send({ success: false, error: err });
315
+ })
316
+
175
317
  })
176
318
 
319
+
177
320
  router.put('/:kb_id', async (req, res) => {
178
321
 
179
322
  let kb_id = req.params.kb_id;
@@ -230,10 +373,14 @@ router.post('/scrape/single', async (req, res) => {
230
373
  id: kb._id,
231
374
  type: kb.type,
232
375
  source: kb.source,
233
- content: kb.content,
376
+ content: "",
234
377
  namespace: kb.namespace
235
378
  }
236
379
 
380
+ if (kb.content) {
381
+ json.content = kb.content;
382
+ }
383
+
237
384
  startScrape(json).then((response) => {
238
385
  winston.verbose("startScrape response: ", response);
239
386
  res.status(200).send(response);
@@ -256,24 +403,27 @@ router.post('/scrape/status', async (req, res) => {
256
403
 
257
404
  if (req.query &&
258
405
  req.query.returnObject &&
259
- (req.query.returnObject === true || req.query.returnObject === true)) {
406
+ (req.query.returnObject === true || req.query.returnObject === 'true')) {
260
407
  returnObject = true;
261
408
  }
262
409
 
263
- openaiService.scrapeStatus(data).then((response) => {
410
+ openaiService.scrapeStatus(data).then( async (response) => {
264
411
 
265
412
  winston.debug("scrapeStatus response.data: ", response.data);
266
413
 
267
414
  let update = {};
268
415
 
269
416
  if (response.data.status_code) {
270
- update.status = response.data.status_code;
417
+ // update.status = response.data.status_code;
418
+ update.status = await statusConverter(response.data.status_code)
419
+
271
420
  }
272
421
 
273
422
  KB.findByIdAndUpdate(data.id, update, { new: true }, (err, savedKb) => {
274
423
 
275
424
  if (err) {
276
425
  winston.verbose("Status was successfully recovered, but the update on the db failed");
426
+ winston.error("find kb by id and updated error: ", err);
277
427
 
278
428
  if (returnObject) {
279
429
  return res.status(206).send({ warning: "Unable to udpate content on db", message: "The original reply was forwarded", data: response.data });
@@ -298,8 +448,14 @@ router.post('/scrape/status', async (req, res) => {
298
448
 
299
449
  router.post('/qa', async (req, res) => {
300
450
  let data = req.body;
451
+ // add or override namespace value if it is passed for security reason
452
+ data.namespace = req.projectid;
301
453
  winston.debug("/qa data: ", data);
302
454
 
455
+ // if (req.body.namespace != req.projectid) {
456
+ // return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project."})
457
+ // }
458
+
303
459
  if (!data.gptkey) {
304
460
  let gptkey = process.env.GPTKEY;
305
461
  if (!gptkey) {
@@ -310,11 +466,38 @@ router.post('/qa', async (req, res) => {
310
466
 
311
467
  openaiService.askNamespace(data).then((resp) => {
312
468
  winston.debug("qa resp: ", resp.data);
313
- res.status(200).send(resp.data);
469
+ let answer = resp.data;
470
+
471
+ let id = answer.id;
472
+ let index = id.indexOf("#");
473
+ if (index != -1) {
474
+ id = id.substring(index + 1);
475
+ }
476
+
477
+ KB.findById(id, (err, resource) => {
478
+
479
+ if (err) {
480
+ winston.error("Unable to find resource with id " + id + " in namespace " + answer.namespace + ". The standard answer is returned.")
481
+ return res.status(200).send(resp.data);
482
+ }
483
+
484
+ answer.source = resource.name;
485
+ return res.status(200).send(answer);
486
+ })
487
+
488
+
314
489
  }).catch((err) => {
315
490
  winston.error("qa err: ", err);
316
- let status = err.response.status;
317
- res.status(status).send({ success: false, statusText: err.response.statusText, error: err.response.data.detail });
491
+ console.log(err.response)
492
+ if (err.response
493
+ && err.response.status) {
494
+ let status = err.response.status;
495
+ res.status(status).send({ success: false, statusText: err.response.statusText, error: err.response.data.detail });
496
+ }
497
+ else {
498
+ res.status(500).send({ success: false, error: err });
499
+ }
500
+
318
501
  })
319
502
  })
320
503
 
@@ -375,7 +558,7 @@ router.delete('/:kb_id', async (req, res) => {
375
558
  })
376
559
 
377
560
  } else {
378
- winston.info("resp.data: ", resp.data);
561
+ winston.verbose("resp.data: ", resp.data);
379
562
 
380
563
  KB.findOneAndDelete({ _id: kb_id, status: { $in: [-1, 3, 4] } }, (err, deletedKb) => {
381
564
  if (err) {
@@ -398,6 +581,51 @@ router.delete('/:kb_id', async (req, res) => {
398
581
 
399
582
  })
400
583
 
584
+ async function saveBulk(operations, kbs, project_id) {
585
+
586
+ return new Promise((resolve, reject) => {
587
+ KB.bulkWrite(operations, { ordered: false }).then((result) => {
588
+ winston.verbose("bulkWrite operations result: ", result);
589
+
590
+ KB.find({ id_project: project_id, source: { $in: kbs.map(kb => kb.source) } }).lean().then((documents) => {
591
+ winston.debug("documents: ", documents);
592
+ resolve(documents)
593
+ }).catch((err) => {
594
+ winston.error("Error finding documents ", err)
595
+ reject(err);
596
+ })
597
+
598
+ }).catch((err) => {
599
+ reject(err);
600
+ })
601
+ })
602
+
603
+ }
604
+
605
+ async function statusConverter(status) {
606
+ return new Promise((resolve) => {
607
+
608
+ let td_status;
609
+ switch(status) {
610
+ case 0:
611
+ td_status = -1;
612
+ break;
613
+ case 2:
614
+ td_status = 200;
615
+ break;
616
+ case 3:
617
+ td_status = 300;
618
+ break;
619
+ case 4:
620
+ td_status = 400;
621
+ break;
622
+ default:
623
+ td_status = -1
624
+ }
625
+ resolve(td_status);
626
+ })
627
+ }
628
+
401
629
  async function updateStatus(id, status) {
402
630
  return new Promise((resolve) => {
403
631
 
@@ -412,6 +640,23 @@ async function updateStatus(id, status) {
412
640
  })
413
641
  }
414
642
 
643
+ async function scheduleScrape(resources) {
644
+
645
+ let data = {
646
+ resources: resources
647
+ }
648
+ winston.info("Schedule job with following data: ", data);
649
+ let scheduler = new Scheduler({ jobManager: jobManager });
650
+ scheduler.trainSchedule(data, (err, result) => {
651
+ if (err) {
652
+ winston.error("Scheduling error: ", err);
653
+ }
654
+ winston.info("Scheduling result: ", result);
655
+ });
656
+
657
+ return true;
658
+ }
659
+
415
660
  async function startScrape(data) {
416
661
 
417
662
  if (!data.gptkey) {
@@ -425,7 +670,7 @@ async function startScrape(data) {
425
670
  return new Promise((resolve, reject) => {
426
671
  openaiService.singleScrape(data).then(async (resp) => {
427
672
  winston.debug("singleScrape resp: ", resp.data);
428
- let status_updated = await updateStatus(data.id, 0);
673
+ let status_updated = await updateStatus(data.id, 100);
429
674
  winston.verbose("status of kb " + data.id + " updated: " + status_updated);
430
675
  resolve(resp.data);
431
676
  }).catch((err) => {
@@ -5,11 +5,11 @@ const messageEvent = require('../event/messageEvent');
5
5
  const emailEvent = require('../event/emailEvent');
6
6
 
7
7
  const PLANS_LIST = {
8
- FREE_TRIAL: { requests: 3000, messages: 0, tokens: 250000, email: 200 }, // same as PREMIUM
9
- SANDBOX: { requests: 200, messages: 0, tokens: 10000, email: 200 },
10
- BASIC: { requests: 800, messages: 0, tokens: 50000, email: 200 },
11
- PREMIUM: { requests: 3000, messages: 0, tokens: 250000, email: 200 },
12
- CUSTOM: { requests: 3000, messages: 0, tokens: 250000, email: 200 }
8
+ FREE_TRIAL: { requests: 3000, messages: 0, tokens: 250000, email: 200, kbs: 50 }, // same as PREMIUM
9
+ SANDBOX: { requests: 200, messages: 0, tokens: 10000, email: 200, kbs: 50 },
10
+ BASIC: { requests: 800, messages: 0, tokens: 50000, email: 200, kbs: 150},
11
+ PREMIUM: { requests: 3000, messages: 0, tokens: 250000, email: 200, kbs: 300},
12
+ CUSTOM: { requests: 3000, messages: 0, tokens: 250000, email: 200, kbs: 300}
13
13
  }
14
14
 
15
15
  const typesList = ['requests', 'messages', 'email', 'tokens']
@@ -58,7 +58,7 @@ class QuoteManager {
58
58
 
59
59
  this.project = project;
60
60
  let key = await this.generateKey(email, 'email');
61
- winston.info("[QuoteManager] incrementEmailCount key: " + key);
61
+ winston.verbose("[QuoteManager] incrementEmailCount key: " + key);
62
62
 
63
63
  await this.tdCache.incr(key)
64
64
  return key;
@@ -68,7 +68,7 @@ class QuoteManager {
68
68
 
69
69
  this.project = project;
70
70
  let key = await this.generateKey(data, 'tokens');
71
- winston.info("[QuoteManager] incrementTokenCount key: " + key);
71
+ winston.verbose("[QuoteManager] incrementTokenCount key: " + key);
72
72
 
73
73
  if (quotes_enabled === false) {
74
74
  winston.debug("QUOTES DISABLED - incrementTokenCount")
@@ -83,8 +83,8 @@ class QuoteManager {
83
83
 
84
84
  async generateKey(object, type) {
85
85
 
86
- winston.info("generateKey object ", object)
87
- winston.info("generateKey type " + type)
86
+ winston.debug("generateKey object ", object)
87
+ winston.debug("generateKey type " + type)
88
88
  let subscriptionDate;
89
89
  if (this.project.profile.subStart) {
90
90
  subscriptionDate = this.project.profile.subStart;
@@ -92,7 +92,7 @@ class QuoteManager {
92
92
  subscriptionDate = this.project.createdAt;
93
93
  }
94
94
  let objectDate = object.createdAt;
95
- winston.info("objectDate " + objectDate);
95
+ winston.debug("objectDate " + objectDate);
96
96
 
97
97
  // converts date in timestamps and transform from ms to s
98
98
  const objectDateTimestamp = ceil(objectDate.getTime() / 1000);
@@ -114,7 +114,7 @@ class QuoteManager {
114
114
 
115
115
  this.project = project;
116
116
  let key = await this.generateKey(object, type);
117
- winston.info("[QuoteManager] getCurrentQuote key: " + key);
117
+ winston.verbose("[QuoteManager] getCurrentQuote key: " + key);
118
118
 
119
119
  let quote = await this.tdCache.get(key);
120
120
  return Number(quote);
@@ -147,17 +147,17 @@ class QuoteManager {
147
147
  */
148
148
  async checkQuote(project, object, type) {
149
149
 
150
- winston.info("checkQuote type " + type);
150
+ winston.verbose("checkQuote type " + type);
151
151
  if (quotes_enabled === false) {
152
- winston.info("QUOTES DISABLED - checkQuote for type " + type);
152
+ winston.verbose("QUOTES DISABLED - checkQuote for type " + type);
153
153
  return true;
154
154
  }
155
155
 
156
156
  this.project = project;
157
157
  let limits = await this.getPlanLimits();
158
- winston.info("limits for current plan: ", limits)
158
+ winston.verbose("limits for current plan: ", limits)
159
159
  let quote = await this.getCurrentQuote(project, object, type);
160
- winston.info("getCurrentQuote resp: ", quote)
160
+ winston.verbose("getCurrentQuote resp: ", quote)
161
161
 
162
162
  if (quote == null) {
163
163
  return true;
@@ -171,7 +171,11 @@ class QuoteManager {
171
171
  }
172
172
 
173
173
 
174
- async getPlanLimits() {
174
+ async getPlanLimits(project) {
175
+
176
+ if (project) {
177
+ this.project = project
178
+ };
175
179
 
176
180
  let limits;
177
181
  if (this.project.profile.type === 'payment') {
@@ -222,6 +226,8 @@ class QuoteManager {
222
226
  }
223
227
  }
224
228
 
229
+ winston.info("QUOTES ENABLED ? ", quotes_enabled);
230
+
225
231
  // TODO - Try to generalize to avoid repetition
226
232
  let incrementEventHandler = (object) => { }
227
233
  let checkEventHandler = (object) => { }
@@ -244,7 +250,7 @@ class QuoteManager {
244
250
  let result = await this.incrementRequestsCount(payload.project, payload.request);
245
251
  return result;
246
252
  } else {
247
- winston.info("QUOTES DISABLED - request.create.quote event")
253
+ winston.verbose("QUOTES DISABLED - request.create.quote event")
248
254
  }
249
255
  })
250
256
  // REQUESTS EVENTS - END
@@ -267,7 +273,7 @@ class QuoteManager {
267
273
  let result = await this.incrementMessagesCount(payload.project, payload.message);
268
274
  return result;
269
275
  } else {
270
- winston.info("QUOTES DISABLED - message.create.quote event")
276
+ winston.verbose("QUOTES DISABLED - message.create.quote event")
271
277
  }
272
278
  })
273
279
  // MESSAGES EVENTS - END
@@ -286,11 +292,11 @@ class QuoteManager {
286
292
 
287
293
  emailEvent.on('email.send.quote', async (payload) => {
288
294
  if (quotes_enabled === true) {
289
- winston.info("email.send event catched");
295
+ winston.verbose("email.send event catched");
290
296
  let result = await this.incrementEmailCount(payload.project, payload.email);
291
297
  return result;
292
298
  } else {
293
- winston.info("QUOTES DISABLED - email.send event")
299
+ winston.verbose("QUOTES DISABLED - email.send event")
294
300
  }
295
301
  })
296
302
  // EMAIL EVENTS - END
@@ -0,0 +1,33 @@
1
+ let JobManager = require("jobs-worker-queued");
2
+ let winston = require('../config/winston');
3
+
4
+ let jobManager;
5
+
6
+ class Scheduler {
7
+
8
+ constructor(config) {
9
+
10
+ if (!config) {
11
+ throw new Error('(Scheduler) config is mandatory');
12
+ }
13
+
14
+ if (!config.jobManager) {
15
+ throw new Error('(Scheduler) config.jobManager is mandatory');
16
+ }
17
+
18
+ this.jobManager = config.jobManager;
19
+ }
20
+
21
+ trainSchedule(data, callback) {
22
+
23
+ winston.debug("(trainScheduler) data: ", data);
24
+ this.jobManager.publish(data, (err, ok) => {
25
+ let response_data = { success: true, message: "Scheduled" };
26
+ if (callback) {
27
+ callback(err, response_data);
28
+ }
29
+ });
30
+ }
31
+ }
32
+
33
+ module.exports = { Scheduler };