@tiledesk/tiledesk-server 2.10.92 → 2.10.93

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 (28) hide show
  1. package/CHANGELOG.md +3 -0
  2. package/models/kbConstants.js +12 -0
  3. package/models/kb_setting.js +1 -0
  4. package/package.json +1 -1
  5. package/routes/admin.js +1 -2
  6. package/routes/kb.js +242 -3
  7. package/test/faqRoute.js +3 -3
  8. package/test/faqkbRoute.js +6 -6
  9. package/test/fileRoute.js +2 -2
  10. package/test/fixtures/exported_namespace.json +62 -0
  11. package/test/imageRoute.js +11 -11
  12. package/test/kbRoute.js +601 -562
  13. package/test/quoteManager.js +1 -1
  14. package/test/webhookRoute.js +1 -1
  15. /package/test/{TooManykbUrlsList.txt → fixtures/TooManykbUrlsList.txt} +0 -0
  16. /package/test/{example-faqs.csv → fixtures/example-faqs.csv} +0 -0
  17. /package/test/{example-json-intents.txt → fixtures/example-json-intents.txt} +0 -0
  18. /package/test/{example-json-multiple-operation-mock.js → fixtures/example-json-multiple-operation-mock.js} +0 -0
  19. /package/test/{example-json-rules.txt → fixtures/example-json-rules.txt} +0 -0
  20. /package/test/{example-json.txt → fixtures/example-json.txt} +0 -0
  21. /package/test/{example-kb-faqs.csv → fixtures/example-kb-faqs.csv} +0 -0
  22. /package/test/{example-webhook-json.txt → fixtures/example-webhook-json.txt} +0 -0
  23. /package/test/{example.json → fixtures/example.json} +0 -0
  24. /package/test/{kbUrlsList.txt → fixtures/kbUrlsList.txt} +0 -0
  25. /package/test/{sample.pdf → fixtures/sample.pdf} +0 -0
  26. /package/test/{test-image.png → fixtures/test-image.png} +0 -0
  27. /package/test/{chatbot-mock.js → mock/chatbotMock.js} +0 -0
  28. /package/test/mock/{MockTdCache.js → tdCacheMock.js} +0 -0
package/CHANGELOG.md CHANGED
@@ -5,6 +5,9 @@
5
5
  🚀 IN PRODUCTION 🚀
6
6
  (https://www.npmjs.com/package/@tiledesk/tiledesk-server/v/2.3.77)
7
7
 
8
+ # 2.10.93
9
+ - Added: endpoints to import/export namespaces
10
+
8
11
  # 2.10.92
9
12
  - Updated voice-twilio-connector to 0.1.22
10
13
  - Bug fix: tags scheduling
@@ -0,0 +1,12 @@
1
+ const kbTypes = {
2
+ URL: 'url',
3
+ TEXT: 'text',
4
+ FAQ: 'faq',
5
+ PDF: 'pdf',
6
+ DOCX: 'docx'
7
+ };
8
+
9
+
10
+ module.exports = {
11
+ kbTypes
12
+ };
@@ -150,6 +150,7 @@ var KBSettingSchema = new Schema({
150
150
 
151
151
  KBSchema.index({ createdAt: -1, updatedAt: -1 })
152
152
  KBSchema.index({ id_project: 1, namespace: 1, updatedAt: -1 })
153
+ KBSchema.index({ namespace: 1, type: 1 })
153
154
 
154
155
 
155
156
  // DEPRECATED
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.92",
4
+ "version": "2.10.93",
5
5
  "scripts": {
6
6
  "start": "node ./bin/www",
7
7
  "pretest": "mongodb-runner start",
package/routes/admin.js CHANGED
@@ -38,8 +38,7 @@ router.get('/saved', function (req, res) {
38
38
  });
39
39
 
40
40
  router.post('/', function (req, res) {
41
-
42
- console.log(req.body.FIREBASE_PRIVATE_KEY);
41
+
43
42
  var env = `
44
43
  FIREBASE_PRIVATE_KEY=${req.body.FIREBASE_PRIVATE_KEY}
45
44
  FIREBASE_CLIENT_EMAIL=${req.body.FIREBASE_CLIENT_EMAIL}
package/routes/kb.js CHANGED
@@ -18,6 +18,7 @@ let Integration = require('../models/integrations');
18
18
  var parsecsv = require("fast-csv");
19
19
 
20
20
  const { MODELS_MULTIPLIER } = require('../utils/aiUtils');
21
+ const { kbTypes } = require('../models/kbConstants');
21
22
 
22
23
  const AMQP_MANAGER_URL = process.env.AMQP_MANAGER_URL;
23
24
  const JOB_TOPIC_EXCHANGE = process.env.JOB_TOPIC_EXCHANGE_TRAIN || 'tiledesk-trainer';
@@ -503,7 +504,7 @@ router.get('/namespace/:id/chunks/:content_id', async (req, res) => {
503
504
  })
504
505
 
505
506
  if(!content) {
506
- return res.status(403).send({ success: false, error: "Not allowed. The conten does not belong to the current namespace." })
507
+ return res.status(403).send({ success: false, error: "Not allowed. The content does not belong to the current namespace." })
507
508
  }
508
509
 
509
510
  let ns = namespaces.find(n => n.id === namespace_id);
@@ -580,6 +581,56 @@ router.get('/namespace/:id/chatbots', async (req, res) => {
580
581
  res.status(200).send(chatbotsArray);
581
582
  })
582
583
 
584
+ router.get('/namespace/export/:id', async (req, res) => {
585
+
586
+ let id_project = req.projectid;
587
+ let namespace_id = req.params.id;
588
+
589
+ let query = {};
590
+ query.id_project = id_project;
591
+ query.namespace = namespace_id;
592
+
593
+ if (req.query.status) {
594
+ query.status = parseInt(req.query.status)
595
+ }
596
+
597
+ query.type = { $in: [ kbTypes.URL, kbTypes.TEXT, kbTypes.FAQ ] };
598
+
599
+ let namespace = await Namespace.findOne({ id: namespace_id}).catch((err) => {
600
+ winston.error("Error getting namepsace for export ", err);
601
+ return res.status(500).send({ success: false, error: "Unable to get namespace with id " + namespace_id })
602
+ })
603
+
604
+ if (!namespace) {
605
+ winston.warn("No namespace found with id ", namespace_id);
606
+ return res.status(404).send({ success: false, error: "No namespace found with id " + namespace_id })
607
+ }
608
+
609
+ let name = namespace.name;
610
+ let preview_settings = namespace.preview_settings;
611
+
612
+ let contents = await KB.find(query).catch((err) => {
613
+ winston.error("Error getting contents for export ", err);
614
+ return res.status(500).send({ success: false, error: "Unable to get contents for namespace " + namespace_id })
615
+ })
616
+
617
+ try {
618
+ let filename = await generateFilename(name);
619
+ let json = {
620
+ name: name,
621
+ preview_settings: preview_settings,
622
+ contents: contents
623
+ }
624
+ let json_string = JSON.stringify(json);
625
+ res.set({ "Content-Disposition": `attachment; filename="${filename}.json"` });
626
+ return res.send(json_string);
627
+ } catch(err) {
628
+ winston.error("Error genereting json ", err);
629
+ return res.status(500).send({ success: false, error: "Error genereting json file" })
630
+ }
631
+
632
+ })
633
+
583
634
  router.post('/namespace', async (req, res) => {
584
635
 
585
636
  let project_id = req.projectid;
@@ -625,6 +676,180 @@ router.post('/namespace', async (req, res) => {
625
676
  })
626
677
  })
627
678
 
679
+ router.post('/namespace/import/:id', upload.single('uploadFile'), async (req, res) => {
680
+
681
+ let id_project = req.projectid;
682
+ let namespace_id = req.params.id;
683
+
684
+ let json_string;
685
+ let json;
686
+ if (req.file) {
687
+ json_string = req.file.buffer.toString('utf-8');
688
+ json = JSON.parse(json_string);
689
+ } else {
690
+ json = req.body;
691
+ }
692
+
693
+ if (!json.contents) {
694
+ winston.warn("Imported json don't contain contents array");
695
+ return res.status(400).send({ success: false, error: "Imported json must contain the contents array" });
696
+ }
697
+
698
+ if (!Array.isArray(json.contents)) {
699
+ winston.warn("Invalid contents type. Expected type: array");
700
+ return res.status(400).send({ success: false, error: "The content field must be of type Array[]" });
701
+ }
702
+
703
+ let contents = json.contents;
704
+
705
+ let namespaces = await Namespace.find({ id_project: id_project }).catch((err) => {
706
+ winston.error("find namespaces error: ", err)
707
+ res.status(500).send({ success: false, error: err })
708
+ })
709
+
710
+ if (!namespaces || namespaces.length == 0) {
711
+ let alert = "No namespace found for the selected project " + id_project + ". Cannot add content to a non-existent namespace."
712
+ winston.warn(alert);
713
+ res.status(403).send(alert);
714
+ }
715
+
716
+ let namespaceIds = namespaces.map(namespace => namespace.id);
717
+
718
+ if (!namespaceIds.includes(namespace_id)) {
719
+ return res.status(403).send({ success: false, error: "Not allowed. The namespace does not belong to the current project." })
720
+ }
721
+
722
+ let quoteManager = req.app.get('quote_manager');
723
+ let limits = await quoteManager.getPlanLimits(req.project);
724
+ let kbs_limit = limits.kbs;
725
+ winston.verbose("Limit of kbs for current plan: " + kbs_limit);
726
+
727
+ // let kbs_count = await KB.countDocuments({ id_project: id_project }).exec();
728
+ // winston.verbose("Kbs count: " + kbs_count);
729
+
730
+ // if (kbs_count >= kbs_limit) {
731
+ // return res.status(403).send({ success: false, error: "Maximum number of resources reached for the current plan", plan_limit: kbs_limit })
732
+ // }
733
+
734
+ // let total_count = kbs_count + contents.length;
735
+ if (contents.length > kbs_limit) {
736
+ return res.status(403).send({ success: false, error: "Cannot exceed the number of resources in the current plan", plan_limit: kbs_limit })
737
+ }
738
+
739
+ let addingContents = [];
740
+ contents.forEach( async (e) => {
741
+ let content = {
742
+ id_project: id_project,
743
+ name: e.name,
744
+ source: e.source,
745
+ type: e.type,
746
+ content: e.content,
747
+ namespace: namespace_id,
748
+ status: -1
749
+ }
750
+
751
+ const optionalFields = ['scrape_type', 'scrape_options', 'refresh_rate'];
752
+
753
+ for (const key of optionalFields) {
754
+ if (e[key] !== undefined) {
755
+ content[key] = e[key];
756
+ }
757
+ }
758
+
759
+ addingContents.push(content);
760
+ })
761
+
762
+ // const operations = addingContents.map(({ _id, ...doc }) => ({
763
+ // replaceOne: {
764
+ // filter: {
765
+ // id_project: doc.id_project,
766
+ // type: doc.type,
767
+ // source: doc.source,
768
+ // namespace: namespace_id
769
+ // },
770
+ // replacement: doc,
771
+ // upsert: true
772
+ // }
773
+ // }));
774
+
775
+ // Try without delete all contents before imports
776
+ // Issue 1: the indexes of the content replaced are not deleted from Pinecone
777
+ // Issue 2: isn't possibile to know how many content will be replaced, so isn't possibile to determine if after the
778
+ // import operation the content's limit is respected
779
+ let ns = namespaces.find(n => n.id === namespace_id);
780
+ let engine = ns.engine || default_engine;
781
+
782
+ if (process.env.NODE_ENV !== "test") {
783
+ await aiService.deleteNamespace({
784
+ namespace: namespace_id,
785
+ engine: engine
786
+ }).catch((err) => {
787
+ winston.error("Error deleting namespace contents: ", err)
788
+ return res.status(500).send({ success: true, error: "Unable to delete namespace contents" })
789
+ });
790
+ }
791
+
792
+ let deleteResponse = await KB.deleteMany({ id_project: id_project, namespace: namespace_id }).catch((err) => {
793
+ winston.error("deleteMany error: ", err);
794
+ return res.status(500).send({ success: false, error: err });
795
+ })
796
+
797
+ winston.verbose("Content deletetion response: ", deleteResponse);
798
+
799
+ await KB.insertMany(addingContents).catch((err) => {
800
+ winston.error("Error adding contents with insertMany: ", err);
801
+ return res.status(500).send({ success: true, error: "Error importing contents" });
802
+ })
803
+
804
+ let new_contents;
805
+ try {
806
+ new_contents = await KB.find({ id_project: id_project, namespace: namespace_id }).lean();
807
+ } catch (err) {
808
+ winston.error("Error getting new contents: ", err);
809
+ return res.status(500).send({ success: false, error: "Unable to get new content" });
810
+ }
811
+
812
+ let resources = new_contents.map(({ name, status, __v, createdAt, updatedAt, id_project, ...keepAttrs }) => keepAttrs)
813
+ resources = resources.map(({ _id, scrape_options, ...rest }) => {
814
+ return { id: _id, parameters_scrape_type_4: scrape_options, engine: engine, ...rest}
815
+ });
816
+
817
+ winston.verbose("resources to be sent to worker: ", resources);
818
+
819
+ if (process.env.NODE_ENV !== "test") {
820
+ scheduleScrape(resources);
821
+ }
822
+
823
+ res.status(200).send({ success: true, message: "Contents imported successfully" });
824
+
825
+
826
+ // saveBulk(operations, addingContents, id_project).then((result) => {
827
+
828
+ // let ns = namespaces.find(n => n.id === namespace_id);
829
+ // let engine = ns.engine || default_engine;
830
+
831
+ // let resources = result.map(({ name, status, __v, createdAt, updatedAt, id_project, ...keepAttrs }) => keepAttrs)
832
+ // resources = resources.map(({ _id, scrape_options, ...rest }) => {
833
+ // return { id: _id, parameters_scrape_type_4: scrape_options, engine: engine, ...rest}
834
+ // });
835
+
836
+ // winston.verbose("resources to be sent to worker: ", resources);
837
+
838
+ // if (process.env.NODE_ENV !== 'test') {
839
+ // scheduleScrape(resources);
840
+ // }
841
+
842
+ // //res.status(200).send(result);
843
+ // res.status(200).send({ success: true, message: "Contents imported successfully"});
844
+
845
+ // }).catch((err) => {
846
+ // winston.error("Unable to save kbs in bulk ", err)
847
+ // res.status(500).send(err);
848
+ // })
849
+
850
+ })
851
+
852
+
628
853
  router.put('/namespace/:id', async (req, res) => {
629
854
 
630
855
  let namespace_id = req.params.id;
@@ -943,7 +1168,7 @@ router.post('/', async (req, res) => {
943
1168
  new_kb.scrape_type = 1;
944
1169
  }
945
1170
  if (new_kb.type === 'url') {
946
- new_kb.refresh = body.refresh;
1171
+ new_kb.refresh_rate = body.refresh_rate;
947
1172
  if (!body.scrape_type || body.scrape_type === 2) {
948
1173
  new_kb.scrape_type = 2;
949
1174
  new_kb.scrape_options = await setDefaultScrapeOptions();
@@ -1366,7 +1591,7 @@ router.delete('/:kb_id', async (req, res) => {
1366
1591
  }
1367
1592
 
1368
1593
  }).catch((err) => {
1369
- let status = err.response.status;
1594
+ let status = err.response?.status || 500;
1370
1595
  res.status(status).send({ success: false, statusText: err.response.statusText, error: err.response.data.detail });
1371
1596
  })
1372
1597
 
@@ -1539,6 +1764,20 @@ async function setCustomScrapeOptions(options) {
1539
1764
  }
1540
1765
  }
1541
1766
  }
1767
+
1768
+ async function generateFilename(name) {
1769
+ return name
1770
+ .toLowerCase()
1771
+ .trim()
1772
+ .normalize("NFD") // Normalize characters with accents
1773
+ .replace(/[\u0300-\u036f]/g, "") // Removes diacritics (e.g. à becomes a)
1774
+ .replace(/[^a-z0-9\s-_]/g, "") // Remove special characters
1775
+ .replace(/\s+/g, "-") // Replaces spaces with dashes
1776
+ .replace(/_/g, "-")
1777
+ .replace(/-+/g, "-"); // Removes consecutive hyphens
1778
+ }
1779
+
1780
+
1542
1781
  /**
1543
1782
  * ****************************************
1544
1783
  * Utils Methods Section - End
package/test/faqRoute.js CHANGED
@@ -7,7 +7,7 @@ var projectService = require('../services/projectService');
7
7
  var userService = require('../services/userService');
8
8
  const Project_user = require('../models/project_user');
9
9
 
10
- const example_data = require('./example-json-multiple-operation-mock');
10
+ const example_data = require('./fixtures/example-json-multiple-operation-mock');
11
11
 
12
12
  //Require the dev-dependencies
13
13
  let chai = require('chai');
@@ -611,7 +611,7 @@ describe('FaqKBRoute', () => {
611
611
  .post('/' + savedProject._id + '/faq/uploadcsv')
612
612
  .auth(email, pwd)
613
613
  .set('Content-Type', 'text/csv')
614
- .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './example-faqs.csv')), 'example-faqs.csv')
614
+ .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './fixtures/example-faqs.csv')), 'example-faqs.csv')
615
615
  .field('id_faq_kb', id_faq_kb)
616
616
  .field('delimiter', ';')
617
617
  // .send({id_faq_kb: id_faq_kb})
@@ -663,7 +663,7 @@ describe('FaqKBRoute', () => {
663
663
  .post('/' + savedProject._id + '/faq/uploadcsv')
664
664
  .auth(email, pwd)
665
665
  .set('Content-Type', 'text/csv')
666
- .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './example-faqs.csv')), 'example-faqs.csv')
666
+ .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './fixtures/example-faqs.csv')), 'example-faqs.csv')
667
667
  .field('id_faq_kb', id_faq_kb)
668
668
  .field('delimiter', ';')
669
669
  // .send({id_faq_kb: id_faq_kb})
@@ -7,7 +7,7 @@ var projectService = require('../services/projectService');
7
7
  var userService = require('../services/userService');
8
8
  var faqService = require('../services/faqService');
9
9
 
10
- let chatbot_mock = require('./chatbot-mock');
10
+ let chatbot_mock = require('./mock/chatbotMock');
11
11
  let log = false;
12
12
 
13
13
 
@@ -748,7 +748,7 @@ describe('FaqKBRoute', () => {
748
748
  .post('/' + savedProject._id + '/faq_kb/importjson/' + null + "?create=true")
749
749
  .auth(email, pwd)
750
750
  .set('Content-Type', 'text/plain')
751
- .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './example-json-rules.txt')), 'example-json-rules')
751
+ .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './fixtures/example-json-rules.txt')), 'example-json-rules')
752
752
  .end((err, res) => {
753
753
 
754
754
  if (err) { console.error("err: ", err); }
@@ -810,7 +810,7 @@ describe('FaqKBRoute', () => {
810
810
  .post('/' + savedProject._id + '/faq_kb/importjson/' + id_faq_kb)
811
811
  .auth(email, pwd)
812
812
  .set('Content-Type', 'text/plain')
813
- .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './example-json-rules.txt')), 'example-json-rules')
813
+ .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './fixtures/example-json-rules.txt')), 'example-json-rules')
814
814
  .end((err, res) => {
815
815
 
816
816
  if (err) { console.error("err: ", err); }
@@ -868,7 +868,7 @@ describe('FaqKBRoute', () => {
868
868
  .post('/' + savedProject._id + '/faq_kb/importjson/' + id_faq_kb)
869
869
  .auth(email, pwd)
870
870
  .set('Content-Type', 'text/plain')
871
- .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './example-json-rules.txt')), 'example-json-rules')
871
+ .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './fixtures/example-json-rules.txt')), 'example-json-rules')
872
872
  .end((err, res) => {
873
873
 
874
874
  if (err) { console.error("err: ", err); }
@@ -915,7 +915,7 @@ describe('FaqKBRoute', () => {
915
915
  .post('/' + savedProject._id + '/faq_kb/importjson/' + id_faq_kb)
916
916
  .auth(email, pwd)
917
917
  .set('Content-Type', 'text/plain')
918
- .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './example-json.txt')), 'example-json.txt')
918
+ .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './fixtures/example-json.txt')), 'example-json.txt')
919
919
  .end((err, res) => {
920
920
 
921
921
  if (err) { console.error("err: ", err) };
@@ -1080,7 +1080,7 @@ describe('FaqKBRoute', () => {
1080
1080
  .post('/' + savedProject._id + '/faq_kb/importjson/null?create=true')
1081
1081
  .auth(email, pwd)
1082
1082
  .set('Content-Type', 'text/plain')
1083
- .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './example-webhook-json.txt')), 'example-webhook-json.txt')
1083
+ .attach('uploadFile', fs.readFileSync(path.resolve(__dirname, './fixtures/example-webhook-json.txt')), 'example-webhook-json.txt')
1084
1084
  .end((err, res) => {
1085
1085
 
1086
1086
  if (err) { console.error("err: ", err) };
package/test/fileRoute.js CHANGED
@@ -34,7 +34,7 @@ describe('FileRoute', () => {
34
34
  .post('/files/users/')
35
35
  .auth(email, pwd)
36
36
  .set('Content-Type', 'application/pdf')
37
- .attach('file', fs.readFileSync('./test/sample.pdf'), 'sample.pdf')
37
+ .attach('file', fs.readFileSync('./test/fixtures/sample.pdf'), 'sample.pdf')
38
38
  // .field('delimiter', ';')
39
39
  .end((err, res) => {
40
40
 
@@ -60,7 +60,7 @@ describe('FileRoute', () => {
60
60
  chai.request(server)
61
61
  .post('/files/public/')
62
62
  .set('Content-Type', 'application/pdf')
63
- .attach('file', fs.readFileSync('./test/sample.pdf'), 'sample.pdf')
63
+ .attach('file', fs.readFileSync('./test/fixtures/sample.pdf'), 'sample.pdf')
64
64
  .field('delimiter', ';')
65
65
  .end((err, res) => {
66
66
 
@@ -0,0 +1,62 @@
1
+ {
2
+ "name": "export2",
3
+ "preview_settings": {
4
+ "model": "gpt-4o",
5
+ "max_tokens": 256,
6
+ "temperature": 0.7,
7
+ "top_k": 4,
8
+ "context": null
9
+ },
10
+ "contents": [
11
+ {
12
+ "_id": "6835bee22c061f7021f4f7c0",
13
+ "id_project": "62c3f10152dc7400352bab0d",
14
+ "source": "Example content",
15
+ "type": "text",
16
+ "__v": 0,
17
+ "content": "This is an example content.",
18
+ "createdAt": "2025-05-27T13:32:18.078Z",
19
+ "name": "Example content",
20
+ "namespace": "6835be87d0a352002d806f73",
21
+ "status": -1,
22
+ "updatedAt": "2025-05-27T13:32:18.078Z"
23
+ },
24
+ {
25
+ "_id": "6835bed02c061f7021f46e99",
26
+ "id_project": "62c3f10152dc7400352bab0d",
27
+ "source": "custom question",
28
+ "type": "faq",
29
+ "__v": 0,
30
+ "content": "custom question\nthis is the answer!",
31
+ "createdAt": "2025-05-27T13:32:00.574Z",
32
+ "name": "custom question",
33
+ "namespace": "6835be87d0a352002d806f73",
34
+ "status": -1,
35
+ "updatedAt": "2025-05-27T13:32:00.574Z"
36
+ },
37
+ {
38
+ "_id": "6835bebc2c061f7021f3daa4",
39
+ "id_project": "62c3f10152dc7400352bab0d",
40
+ "namespace": "6835be87d0a352002d806f73",
41
+ "source": "https://gethelp.tiledesk.com/articles/configure-a-whatsapp-business-account/",
42
+ "type": "url",
43
+ "content": "",
44
+ "createdAt": "2025-05-27T13:31:40.958Z",
45
+ "name": "https://gethelp.tiledesk.com/articles/configure-a-whatsapp-business-account/",
46
+ "refresh_rate": "never",
47
+ "scrape_options": {
48
+ "tags_to_extract": [
49
+ "article"
50
+ ],
51
+ "unwanted_tags": [
52
+ "h1",
53
+ "h2"
54
+ ],
55
+ "unwanted_classnames": []
56
+ },
57
+ "scrape_type": 4,
58
+ "status": 400,
59
+ "updatedAt": "2025-05-27T13:31:41.022Z"
60
+ }
61
+ ]
62
+ }
@@ -36,7 +36,7 @@ describe('ImagesRoute', () => {
36
36
  .post('/images/users/')
37
37
  .auth(email, pwd)
38
38
  .set('Content-Type', 'image/jpeg')
39
- .attach('file', fs.readFileSync('./test/test-image.png'), 'test-image.png')
39
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'test-image.png')
40
40
  // .field('delimiter', ';')
41
41
  .end((err, res) => {
42
42
 
@@ -69,7 +69,7 @@ describe('ImagesRoute', () => {
69
69
  .put('/images/users')
70
70
  .auth(email, pwd)
71
71
  .set('Content-Type', 'image/jpeg')
72
- .attach('file', fs.readFileSync('./test/test-image.png'), 'profile.png')
72
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'profile.png')
73
73
  // .field('folder', 'myfolder')
74
74
  .end((err, res) => {
75
75
 
@@ -91,7 +91,7 @@ describe('ImagesRoute', () => {
91
91
  .put('/images/users')
92
92
  .auth(email, pwd)
93
93
  .set('Content-Type', 'image/jpeg')
94
- .attach('file', fs.readFileSync('./test/test-image.png'), 'profile.png')
94
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'profile.png')
95
95
  // .field('folder', 'myfolder')
96
96
  .end((err, res) => {
97
97
  res.should.have.status(409);
@@ -114,7 +114,7 @@ describe('ImagesRoute', () => {
114
114
  .put('/images/users/photo')
115
115
  .auth(email, pwd)
116
116
  .set('Content-Type', 'image/jpeg')
117
- .attach('file', fs.readFileSync('./test/test-image.png'), 'profile.png')
117
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'profile.png')
118
118
  // .field('folder', 'myfolder')
119
119
  .end((err, res) => {
120
120
 
@@ -136,7 +136,7 @@ describe('ImagesRoute', () => {
136
136
  .put('/images/users/photo')
137
137
  .auth(email, pwd)
138
138
  .set('Content-Type', 'image/jpeg')
139
- .attach('file', fs.readFileSync('./test/test-image.png'), 'profile.png')
139
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'profile.png')
140
140
  // .field('folder', 'myfolder')
141
141
  .end((err, res) => {
142
142
 
@@ -160,7 +160,7 @@ describe('ImagesRoute', () => {
160
160
  .put('/images/users/photo?force=true')
161
161
  .auth(email, pwd)
162
162
  .set('Content-Type', 'image/jpeg')
163
- .attach('file', fs.readFileSync('./test/test-image.png'), 'profile.png')
163
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'profile.png')
164
164
  // .field('folder', 'myfolder')
165
165
  .end((err, res) => {
166
166
 
@@ -182,7 +182,7 @@ describe('ImagesRoute', () => {
182
182
  .put('/images/users/photo?force=true')
183
183
  .auth(email, pwd)
184
184
  .set('Content-Type', 'image/jpeg')
185
- .attach('file', fs.readFileSync('./test/test-image.png'), 'profile.png')
185
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'profile.png')
186
186
  // .field('folder', 'myfolder')
187
187
  .end((err, res) => {
188
188
  res.should.have.status(201);
@@ -217,7 +217,7 @@ describe('ImagesRoute', () => {
217
217
  .put('/images/users/photo?bot_id=' + bot_id)
218
218
  .auth(email, pwd)
219
219
  .set('Content-Type', 'image/jpeg')
220
- .attach('file', fs.readFileSync('./test/test-image.png'), 'profile.png')
220
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'profile.png')
221
221
  // .field('folder', 'myfolder')
222
222
  .end((err, res) => {
223
223
 
@@ -239,7 +239,7 @@ describe('ImagesRoute', () => {
239
239
  .put('/images/users/photo?bot_id=' + bot_id)
240
240
  .auth(email, pwd)
241
241
  .set('Content-Type', 'image/jpeg')
242
- .attach('file', fs.readFileSync('./test/test-image.png'), 'profile.png')
242
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'profile.png')
243
243
  // .field('folder', 'myfolder')
244
244
  .end((err, res) => {
245
245
 
@@ -270,7 +270,7 @@ describe('ImagesRoute', () => {
270
270
  .put('/images/users')
271
271
  .auth(email, pwd)
272
272
  .set('Content-Type', 'image/jpeg')
273
- .attach('file', fs.readFileSync('./test/test-image.png'), 'profile.png')
273
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'profile.png')
274
274
  // .field('folder', 'myfolder')
275
275
  .end((err, res) => {
276
276
 
@@ -308,7 +308,7 @@ describe('ImagesRoute', () => {
308
308
  chai.request(server)
309
309
  .post('/images/public/')
310
310
  .set('Content-Type', 'image/jpeg')
311
- .attach('file', fs.readFileSync('./test/test-image.png'), 'test-image.png')
311
+ .attach('file', fs.readFileSync('./test/fixtures/test-image.png'), 'test-image.png')
312
312
  // .field('delimiter', ';')
313
313
  .end((err, res) => {
314
314