data-primals-engine 1.7.0 → 1.7.2

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 (39) hide show
  1. package/README.md +160 -160
  2. package/client/package-lock.json +1080 -212
  3. package/client/package.json +12 -6
  4. package/client/src/AssistantChat.jsx +1 -3
  5. package/client/src/DataLayout.jsx +19 -20
  6. package/client/src/DataTable.jsx +2 -2
  7. package/client/src/DocumentationPageLayout.scss +1 -1
  8. package/client/src/ViewSwitcher.jsx +1 -1
  9. package/client/vite.config.js +31 -30
  10. package/package.json +39 -23
  11. package/src/ai.jobs.js +135 -0
  12. package/src/constants.js +561 -545
  13. package/src/core.js +487 -477
  14. package/src/data.js +2 -0
  15. package/src/email.js +0 -2
  16. package/src/engine.js +50 -42
  17. package/src/filter.js +348 -343
  18. package/src/modules/assistant/assistant.js +782 -763
  19. package/src/modules/assistant/constants.js +23 -16
  20. package/src/modules/assistant/providers.js +77 -37
  21. package/src/modules/bucket.js +4 -0
  22. package/src/modules/data/data.cluster.js +191 -0
  23. package/src/modules/data/data.core.js +11 -8
  24. package/src/modules/data/data.js +13 -4
  25. package/src/modules/data/data.operations.js +186 -106
  26. package/src/modules/data/data.relations.js +1 -0
  27. package/src/modules/data/data.replication.js +83 -0
  28. package/src/modules/data/data.routes.js +2183 -1879
  29. package/src/modules/mongodb.js +76 -73
  30. package/src/modules/user.js +7 -1
  31. package/src/modules/worker-script-runner.js +97 -0
  32. package/src/modules/workflow.js +1953 -1815
  33. package/src/packs.js +5701 -5697
  34. package/src/providers.js +298 -297
  35. package/test/assistant.test.js +207 -206
  36. package/test/data.integration.test.js +1425 -1416
  37. package/test/import_export.integration.test.js +210 -210
  38. package/test/workflow.actions.integration.test.js +487 -475
  39. package/test/workflow.integration.test.js +332 -329
@@ -1,211 +1,211 @@
1
- import {expect, describe, it, beforeEach, beforeAll, afterAll, vi, afterEach} from 'vitest';
2
-
3
- // --- Importations des modules de votre application ---
4
- import {
5
- insertData,
6
- exportData,
7
- importData
8
- } from '../src/index.js';
9
-
10
- import {
11
- modelsCollection as getAppModelsCollection,
12
- getCollectionForUser as getAppUserCollection, getCollectionForUser
13
- } from '../src/modules/mongodb.js';
14
- import {sleep} from "../src/core.js";
15
- import fs from "node:fs";
16
- import {initEngine} from "../src/setenv.js";
17
- import {Config} from "../src/index.js";
18
-
19
- // --- Données Mock ---
20
- const mockUser = {
21
- username: 'testuserImpex',
22
- _user: 'testuserImpex',
23
- userPlan: 'premium',
24
- email: 'testImpex@example.com'
25
- };
26
-
27
- const impexTestModel = {
28
- name: 'impex_products',
29
- description: 'test',
30
- _user: mockUser.username,
31
- fields: [
32
- { name: 'name', type: 'string', required: true },
33
- { name: 'sku', type: 'string', unique: true },
34
- { name: 'price', type: 'number', required: true },
35
- { name: 'inStock', type: 'boolean', default: true }
36
- ]
37
- };
38
-
39
- // --- Setup de l'environnement de test ---
40
- let testModelsColInstance;
41
- let testDatasColInstance;
42
-
43
- function blobToFile(theBlob, fileName){
44
- //A Blob() is almost a File() - it's just missing the two properties below which we will add
45
- theBlob.lastModifiedDate = new Date();
46
- theBlob.name = fileName;
47
- return theBlob;
48
- }
49
-
50
- beforeAll(async () =>{
51
- Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant"]);
52
- await initEngine();
53
- })
54
-
55
- beforeEach(async() =>{
56
- // tell vitest we use mocked time
57
- vi.useFakeTimers({ shouldAdvanceTime: true })
58
- })
59
-
60
- afterEach(() => {
61
- vi.runOnlyPendingTimers();
62
- // restoring date after each test run
63
- vi.useRealTimers()
64
- })
65
- afterAll(async () => {
66
- const coll = await getCollectionForUser(mockUser);
67
- await coll.drop();
68
- })
69
- // --- Début des tests ---
70
- describe('Intégration des fonctions d\'Import/Export', () => {
71
-
72
- // Préparation avant chaque test du bloc
73
- beforeEach(async () => {
74
- testModelsColInstance = getAppModelsCollection;
75
- testDatasColInstance = await getAppUserCollection(mockUser);
76
-
77
- // Nettoyage complet pour un état propre
78
- await testDatasColInstance.deleteMany({ _user: "testuserImpex"});
79
-
80
- if( await testModelsColInstance.find({ name: impexTestModel.name, _user: mockUser.username }).count() === 0) {
81
- await testModelsColInstance.insertOne(impexTestModel);
82
- }
83
- // Insérer des données de base pour les tests d'exportation
84
- await insertData(impexTestModel.name, [
85
- { name: 'Produit A', sku: 'SKU-A', price: 10.50, inStock: true },
86
- { name: 'Produit B', sku: 'SKU-B', price: 25.00, inStock: false },
87
- { name: 'Produit C', sku: 'SKU-C', price: 99.99, inStock: true }
88
- ], {}, mockUser, false);
89
- });
90
-
91
- describe('Export de données', () => {
92
- it('devrait exporter les données en format JSON', async () => {
93
-
94
- const res= await exportData({
95
- models: [impexTestModel.name],
96
- depth: 1
97
- }, mockUser);
98
-
99
- expect(res.success).toBeTruthy();
100
-
101
- const data = res.data[impexTestModel.name];
102
- expect(data).toBeInstanceOf(Array);
103
- expect(data).toHaveLength(3);
104
- expect(data[0]).toMatchObject({ name: 'Produit A', sku: 'SKU-A', price: 10.50 });
105
- // Les champs système (_id, _model, _user, _hash) ne devraient pas être exportés par défaut
106
- expect(data[0]).not.toHaveProperty('_model');
107
- });
108
-
109
- it('devrait lever une exception si le modèle n\'existe pas', async () => {
110
- // La fonction devrait rejeter la promesse ou lancer une erreur
111
- await expect((await exportData('model_inexistant', 'json', mockUser)).success).toBeFalsy();
112
- });
113
- });
114
-
115
- describe('Import de données', () => {
116
- it('devrait importer des données depuis une chaîne JSON', async () => {
117
-
118
- const jsonDataToImport = [
119
- { name: 'Produit D', sku: 'SKU-D', price: 1.00, inStock: true },
120
- { name: 'Produit E', sku: 'SKU-E', price: 2.00 } // inStock utilisera la valeur par défaut
121
- ];
122
- const jsonString = JSON.stringify(jsonDataToImport);
123
-
124
- // Exécution de la fonction d'import (hypothétique)x
125
- var blob = new Blob([jsonString], {type: "application/json"});
126
- fs.writeFileSync('test.json', jsonString);
127
- blob.path = 'test.json';
128
- blob.originalFilename = 'test.json';
129
- const result = await importData({model: impexTestModel.name}, {file: blobToFile(blob, "test.json")}, mockUser);
130
-
131
- // Vérifications du résultat de l'opération
132
- expect(result.success).toBe(true);
133
- expect(result.job.jobId).not.toBeNull();
134
-
135
- await sleep(5000);
136
-
137
- // Vérification directe en base de données
138
- const importedDocs = await testDatasColInstance.find({
139
- _model: impexTestModel.name,
140
- sku: { $in: ['SKU-D', 'SKU-E'] }
141
- }).toArray();
142
-
143
- expect(importedDocs).toHaveLength(2);
144
- const docD = importedDocs.find(d => d.sku === 'SKU-D');
145
- const docE = importedDocs.find(d => d.sku === 'SKU-E');
146
-
147
- expect(docD.name).toBe('Produit D');
148
- expect(docD.inStock).toBe(true); // Vérification de la valeur par défaut
149
- expect(docE.price).toBe(2.00);
150
-
151
- }, 20000);
152
-
153
- it('devrait importer des données depuis une chaîne CSV et convertir les types', async () => {
154
- const csvStringToImport = `name,sku,price,inStock\nProduit F,SKU-F,3.55,true\nProduit G,SKU-G,4.99,false`;
155
-
156
- // Exécution de la fonction d'import (hypothétique)x
157
- var blob = new Blob([csvStringToImport], {type: "text/csv"});
158
- fs.writeFileSync('test.csv', csvStringToImport);
159
- blob.path = 'test.csv';
160
- blob.originalFilename = 'test.csv';
161
-
162
- // Exécution de la fonction d'import
163
- const result = await importData({model:impexTestModel.name}, {file: blobToFile(blob, "test.csv")}, mockUser);
164
-
165
- // Vérifications du résultat
166
- expect(result.success).toBe(true);
167
- expect(result.job.jobId).not.toBeNull();
168
-
169
- await sleep(5000);
170
- // Vérification en base de données
171
- const importedDocs = await testDatasColInstance.find({
172
- _model: impexTestModel.name,
173
- sku: { $in: ['SKU-F', 'SKU-G'] }
174
- }).toArray();
175
-
176
- expect(importedDocs).toHaveLength(2);
177
- const docF = importedDocs.find(d => d.sku === 'SKU-F');
178
- const docG = importedDocs.find(d => d.sku === 'SKU-G');
179
-
180
- // Vérification de la conversion des types
181
- expect(docF.price).toBe(3.55);
182
- expect(docF.inStock).toBe(true);
183
- expect(docG.inStock).toBe(false);
184
- }, 20000);
185
-
186
- it('devrait rejeter les lignes invalides et rapporter les erreurs', async () => {
187
- const csvStringToImport = `name,sku,price,inStock
188
- Valide H,SKU-H,10,true
189
- ,SKU-I,20,true
190
- Valide J,SKU-J,,false
191
- Valide K,SKU-A,40,true`;
192
-
193
- // Exécution de la fonction d'import (hypothétique)x
194
- const blob = new Blob([csvStringToImport], {type: "text/csv"});
195
- fs.writeFileSync('test.csv', csvStringToImport);
196
- blob.path = 'test.csv';
197
- blob.originalFilename = 'test.csv';
198
-
199
- const result = await importData({ model: impexTestModel.name }, {file: blobToFile(blob,"test.csv")}, mockUser);
200
-
201
- // L'initiation du job doit réussir
202
- expect(result.success).toBe(true);
203
- expect(result.job.jobId).not.toBeNull();
204
-
205
- await sleep(5000);
206
- // Vérifier que seule les données valides sont en BDD
207
- const count = await testDatasColInstance.countDocuments({ _model: impexTestModel.name, sku: 'SKU-H' });
208
- expect(count).toBe(1); // Seule la ligne valide 'SKU-H' doit être insérée.
209
- }, 20000);
210
- });
1
+ import {expect, describe, it, beforeEach, beforeAll, afterAll, vi, afterEach} from 'vitest';
2
+
3
+ // --- Importations des modules de votre application ---
4
+ import {
5
+ insertData,
6
+ exportData,
7
+ importData
8
+ } from '../src/index.js';
9
+
10
+ import {
11
+ modelsCollection as getAppModelsCollection,
12
+ getCollectionForUser as getAppUserCollection, getCollectionForUser
13
+ } from '../src/modules/mongodb.js';
14
+ import {sleep} from "../src/core.js";
15
+ import fs from "node:fs";
16
+ import {initEngine} from "../src/setenv.js";
17
+ import {Config} from "../src/index.js";
18
+
19
+ // --- Données Mock ---
20
+ const mockUser = {
21
+ username: 'testuserImpex',
22
+ _user: 'testuserImpex',
23
+ userPlan: 'premium',
24
+ email: 'testImpex@example.com'
25
+ };
26
+
27
+ const impexTestModel = {
28
+ name: 'impex_products',
29
+ description: 'test',
30
+ _user: mockUser.username,
31
+ fields: [
32
+ { name: 'name', type: 'string', required: true },
33
+ { name: 'sku', type: 'string', unique: true },
34
+ { name: 'price', type: 'number', required: true },
35
+ { name: 'inStock', type: 'boolean', default: true }
36
+ ]
37
+ };
38
+
39
+ // --- Setup de l'environnement de test ---
40
+ let testModelsColInstance;
41
+ let testDatasColInstance;
42
+
43
+ function blobToFile(theBlob, fileName){
44
+ //A Blob() is almost a File() - it's just missing the two properties below which we will add
45
+ theBlob.lastModifiedDate = new Date();
46
+ theBlob.name = fileName;
47
+ return theBlob;
48
+ }
49
+
50
+ beforeAll(async () =>{
51
+ Config.Set("modules", ["mongodb", "data", "file", "bucket", "workflow","user", "assistant"]);
52
+ await initEngine();
53
+ })
54
+
55
+ beforeEach(async() =>{
56
+ // tell vitest we use mocked time
57
+ vi.useFakeTimers({ shouldAdvanceTime: true })
58
+ })
59
+
60
+ afterEach(() => {
61
+ vi.runOnlyPendingTimers();
62
+ // restoring date after each test run
63
+ vi.useRealTimers()
64
+ })
65
+ afterAll(async () => {
66
+ const coll = await getCollectionForUser(mockUser);
67
+ await coll.drop();
68
+ })
69
+ // --- Début des tests ---
70
+ describe('Intégration des fonctions d\'Import/Export', () => {
71
+
72
+ // Préparation avant chaque test du bloc
73
+ beforeEach(async () => {
74
+ testModelsColInstance = getAppModelsCollection;
75
+ testDatasColInstance = await getAppUserCollection(mockUser);
76
+
77
+ // Nettoyage complet pour un état propre
78
+ await testDatasColInstance.deleteMany({ _user: "testuserImpex"});
79
+
80
+ if( await testModelsColInstance.find({ name: impexTestModel.name, _user: mockUser.username }).count() === 0) {
81
+ await testModelsColInstance.insertOne(impexTestModel);
82
+ }
83
+ // Insérer des données de base pour les tests d'exportation
84
+ await insertData(impexTestModel.name, [
85
+ { name: 'Produit A', sku: 'SKU-A', price: 10.50, inStock: true },
86
+ { name: 'Produit B', sku: 'SKU-B', price: 25.00, inStock: false },
87
+ { name: 'Produit C', sku: 'SKU-C', price: 99.99, inStock: true }
88
+ ], {}, mockUser, false);
89
+ });
90
+
91
+ describe('Export de données', () => {
92
+ it('devrait exporter les données en format JSON', async () => {
93
+
94
+ const res= await exportData({
95
+ models: [impexTestModel.name],
96
+ depth: 1
97
+ }, mockUser);
98
+
99
+ expect(res.success).toBeTruthy();
100
+
101
+ const data = res.data[impexTestModel.name];
102
+ expect(data).toBeInstanceOf(Array);
103
+ expect(data).toHaveLength(3);
104
+ expect(data[0]).toMatchObject({ name: 'Produit A', sku: 'SKU-A', price: 10.50 });
105
+ // Les champs système (_id, _model, _user, _hash) ne devraient pas être exportés par défaut
106
+ expect(data[0]).not.toHaveProperty('_model');
107
+ });
108
+
109
+ it('devrait lever une exception si le modèle n\'existe pas', async () => {
110
+ // La fonction devrait rejeter la promesse ou lancer une erreur
111
+ await expect((await exportData('model_inexistant', 'json', mockUser)).success).toBeFalsy();
112
+ });
113
+ });
114
+
115
+ describe('Import de données', () => {
116
+ it('devrait importer des données depuis une chaîne JSON', async () => {
117
+
118
+ const jsonDataToImport = [
119
+ { name: 'Produit D', sku: 'SKU-D', price: 1.00, inStock: true },
120
+ { name: 'Produit E', sku: 'SKU-E', price: 2.00 } // inStock utilisera la valeur par défaut
121
+ ];
122
+ const jsonString = JSON.stringify(jsonDataToImport);
123
+
124
+ // Exécution de la fonction d'import (hypothétique)x
125
+ var blob = new Blob([jsonString], {type: "application/json"});
126
+ fs.writeFileSync('test.json', jsonString);
127
+ blob.path = 'test.json';
128
+ blob.originalFilename = 'test.json';
129
+ const result = await importData({model: impexTestModel.name}, {file: blobToFile(blob, "test.json")}, mockUser);
130
+
131
+ // Vérifications du résultat de l'opération
132
+ expect(result.success).toBe(true);
133
+ expect(result.jobId).not.toBeNull();
134
+
135
+ await sleep(5000);
136
+
137
+ // Vérification directe en base de données
138
+ const importedDocs = await testDatasColInstance.find({
139
+ _model: impexTestModel.name,
140
+ sku: { $in: ['SKU-D', 'SKU-E'] }
141
+ }).toArray();
142
+
143
+ expect(importedDocs).toHaveLength(2);
144
+ const docD = importedDocs.find(d => d.sku === 'SKU-D');
145
+ const docE = importedDocs.find(d => d.sku === 'SKU-E');
146
+
147
+ expect(docD.name).toBe('Produit D');
148
+ expect(docD.inStock).toBe(true); // Vérification de la valeur par défaut
149
+ expect(docE.price).toBe(2.00);
150
+
151
+ }, 20000);
152
+
153
+ it('devrait importer des données depuis une chaîne CSV et convertir les types', async () => {
154
+ const csvStringToImport = `name,sku,price,inStock\nProduit F,SKU-F,3.55,true\nProduit G,SKU-G,4.99,false`;
155
+
156
+ // Exécution de la fonction d'import (hypothétique)x
157
+ var blob = new Blob([csvStringToImport], {type: "text/csv"});
158
+ fs.writeFileSync('test.csv', csvStringToImport);
159
+ blob.path = 'test.csv';
160
+ blob.originalFilename = 'test.csv';
161
+
162
+ // Exécution de la fonction d'import
163
+ const result = await importData({model:impexTestModel.name}, {file: blobToFile(blob, "test.csv")}, mockUser);
164
+
165
+ // Vérifications du résultat
166
+ expect(result.success).toBe(true);
167
+ expect(result.jobId).not.toBeNull();
168
+
169
+ await sleep(5000);
170
+ // Vérification en base de données
171
+ const importedDocs = await testDatasColInstance.find({
172
+ _model: impexTestModel.name,
173
+ sku: { $in: ['SKU-F', 'SKU-G'] }
174
+ }).toArray();
175
+
176
+ expect(importedDocs).toHaveLength(2);
177
+ const docF = importedDocs.find(d => d.sku === 'SKU-F');
178
+ const docG = importedDocs.find(d => d.sku === 'SKU-G');
179
+
180
+ // Vérification de la conversion des types
181
+ expect(docF.price).toBe(3.55);
182
+ expect(docF.inStock).toBe(true);
183
+ expect(docG.inStock).toBe(false);
184
+ }, 20000);
185
+
186
+ it('devrait rejeter les lignes invalides et rapporter les erreurs', async () => {
187
+ const csvStringToImport = `name,sku,price,inStock
188
+ Valide H,SKU-H,10,true
189
+ ,SKU-I,20,true
190
+ Valide J,SKU-J,,false
191
+ Valide K,SKU-A,40,true`;
192
+
193
+ // Exécution de la fonction d'import (hypothétique)x
194
+ const blob = new Blob([csvStringToImport], {type: "text/csv"});
195
+ fs.writeFileSync('test.csv', csvStringToImport);
196
+ blob.path = 'test.csv';
197
+ blob.originalFilename = 'test.csv';
198
+
199
+ const result = await importData({ model: impexTestModel.name }, {file: blobToFile(blob,"test.csv")}, mockUser);
200
+
201
+ // L'initiation du job doit réussir
202
+ expect(result.success).toBe(true);
203
+ expect(result.jobId).not.toBeNull();
204
+
205
+ await sleep(5000);
206
+ // Vérifier que seule les données valides sont en BDD
207
+ const count = await testDatasColInstance.countDocuments({ _model: impexTestModel.name, sku: 'SKU-H' });
208
+ expect(count).toBe(1); // Seule la ligne valide 'SKU-H' doit être insérée.
209
+ }, 20000);
210
+ });
211
211
  });