data-primals-engine 1.6.2 → 1.6.5

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 +56 -911
  2. package/client/package-lock.json +64 -50
  3. package/client/package.json +6 -0
  4. package/client/src/App.scss +29 -0
  5. package/client/src/Dashboard.jsx +1 -1
  6. package/client/src/WorkflowEditor.jsx +101 -0
  7. package/client/src/WorkflowEditor.scss +29 -4
  8. package/client/src/_variables.scss +3 -0
  9. package/doc/automation-workflows.md +102 -0
  10. package/doc/core-concepts.md +33 -0
  11. package/doc/custom-api-endpoints.md +40 -0
  12. package/doc/dashboards-kpis-charts.md +49 -0
  13. package/doc/data-management.md +120 -0
  14. package/doc/data-models.md +75 -0
  15. package/doc/index.md +14 -0
  16. package/doc/roles-permissions.md +43 -0
  17. package/doc/users.md +30 -0
  18. package/package.json +7 -5
  19. package/src/client.js +6 -4
  20. package/src/core.js +31 -12
  21. package/src/defaultModels +1628 -0
  22. package/src/defaultModels.js +14 -0
  23. package/src/filter.js +110 -42
  24. package/src/modules/data/data.history.js +5 -1
  25. package/src/modules/data/data.js +2 -1
  26. package/src/modules/data/data.operations.js +116 -72
  27. package/src/modules/data/data.relations.js +1 -1
  28. package/src/modules/mongodb.js +2 -1
  29. package/src/modules/swagger.js +25 -5
  30. package/src/modules/user.js +138 -76
  31. package/src/modules/workflow.js +187 -177
  32. package/src/providers.js +1 -1
  33. package/swagger-en.yml +2308 -472
  34. package/swagger-fr.yml +487 -3
  35. package/test/core.test.js +341 -0
  36. package/test/data.history.integration.test.js +140 -16
  37. package/test/data.integration.test.js +137 -2
  38. package/test/user.test.js +201 -280
  39. package/test/workflow.integration.test.js +8 -0
@@ -9,7 +9,7 @@ import {
9
9
  insertData,
10
10
  editData,
11
11
  deleteData,
12
- searchData, installPack, deleteModels, createModel, patchData
12
+ searchData, installPack, deleteModels, createModel, patchData, datasCollection
13
13
  } from '../src/index.js';
14
14
 
15
15
  import {
@@ -17,7 +17,8 @@ import {
17
17
  getCollection,
18
18
  getCollectionForUser as getAppUserCollection, getCollectionForUser
19
19
  } from '../src/modules/mongodb.js';
20
- import {getRandom} from "../src/core.js";
20
+ import { isConditionMet } from '../src/filter.js';
21
+ import {getRandom, isPlainObject} from "../src/core.js";
21
22
  import {generateUniqueName, initEngine} from "../src/setenv.js";
22
23
  import {purgeData} from "../src/modules/data/data.history.js";
23
24
  import {removeFile} from "../src/modules/file.js";
@@ -1279,4 +1280,138 @@ describe('Data integration tests (CRUD, validation...)', () => {
1279
1280
  expect(data[0]._id.toString()).toBe(noEnvDocId.toString());
1280
1281
  });
1281
1282
  });
1283
+
1284
+ describe('Data operations with permission filters', () => {
1285
+ let testUser, testSystemUser, permissionAdd, permissionEdit, permissionDelete, role;
1286
+ let orderModelDef, pendingOrderId, shippedOrderId, otherUserOrderId;
1287
+
1288
+ beforeEach(async () => {
1289
+ // Création d'un utilisateur de test isolé
1290
+ const username = generateUniqueName('perm_filter_user');
1291
+ testUser = { _id: new ObjectId(), username: username, _user: 'testSystemUser', _model: 'user', roles: [] };
1292
+ testSystemUser = { _id: new ObjectId(), username: 'testSystemUser', roles: ['API_ADMIN'] }; // L'utilisateur système a tous les droits
1293
+ const collection = await getCollectionForUser(testUser);
1294
+
1295
+ // 1. Créer les modèles nécessaires
1296
+ await createModel({ name: 'permission', description:'t', _user: testSystemUser.username, fields: [{ name: 'name', type: 'string' }, { name: 'filter', type: 'code', language: 'json' }, { name: 'description', type: 'string' }] });
1297
+ await createModel({ name: 'role',description:'t', _user: testSystemUser.username, fields: [{ name: 'name', type: 'string' }, { name: 'permissions', type: 'relation', relation: 'permission', multiple: true }] });
1298
+ // Le modèle 'orderTest' doit exister pour les deux utilisateurs : testUser (pour les opérations de test) et testSystemUser (pour le setup)
1299
+ orderModelDef = { name: 'orderTest',description:'t', _user: testSystemUser.username, fields: [{ name: 'status', type: 'string' }, { name: 'amount', type: 'number' }, { name: 'customer', type: 'string' }] };
1300
+ const t = await createModel(orderModelDef);
1301
+
1302
+ // 2. Créer les permissions avec filtres pour les tests
1303
+ const permissionsToCreate = [
1304
+ // On a besoin de ces permissions pour que testUser puisse créer le rôle et les permissions
1305
+ { name: 'API_ADD_DATA_permission', _user: testSystemUser.username },
1306
+ { name: 'API_ADD_DATA_role', _user: testSystemUser.username },
1307
+ // Permissions de l'application
1308
+ { name: 'API_SEARCH_DATA_orderTest' }, // <-- AJOUTER CETTE LIGNE
1309
+ { name: 'API_ADD_DATA_orderTest', filter: { "$lt" : ["$amount", 1000] }, description: "Filter for adding orders" },
1310
+ { name: 'API_EDIT_DATA_orderTest', filter: { status: 'pending' } },
1311
+ { name: 'API_DELETE_DATA_orderTest', filter: { customer: testUser._id.toString() } }
1312
+ ];
1313
+ // On insère les permissions pour les deux utilisateurs pour que le rôle soit valide pour testUser
1314
+ let permResult = await insertData('permission', permissionsToCreate, {}, testSystemUser, false, false, false); // Pour le setup
1315
+ expect(permResult.success, `La création des permissions a échoué: ${permResult.error}`).toBe(true);
1316
+
1317
+ // 3. Récupérer les IDs des permissions et créer le rôle
1318
+ // On se base sur les permissions de l'utilisateur de test pour créer son rôle
1319
+ const allPerms = await (await getCollectionForUser(testSystemUser)).find({ _model: 'permission' }).toArray();
1320
+ const permIds = allPerms.map(p => p._id.toString());
1321
+
1322
+ // On insère le rôle pour les deux utilisateurs
1323
+ let roleRes = await insertData('role', { name: 'OrderManager', permissions: permIds }, {}, testSystemUser, false, false, false);
1324
+ expect(roleRes.success, `La création du rôle a échoué: ${roleRes.error}`).toBe(true);
1325
+ role = roleRes.insertedIds[0];
1326
+
1327
+ // 4. Assigner le rôle à l'utilisateur de test
1328
+ testUser.roles = [role.toString()];
1329
+
1330
+ // 5. Créer les données de test avec l'utilisateur de test lui-même.
1331
+ // Pour cela, on lui donne temporairement les droits d'admin pour le setup.
1332
+ const originalRoles = testUser.roles;
1333
+ testUser.roles = ['API_ADMIN']; // Droits temporaires pour créer les données
1334
+ const pendingOrder = await insertData('orderTest', { status: 'pending', amount: 100, customer: testUser._id.toString() }, {}, testUser, false, false, false);
1335
+ pendingOrderId = pendingOrder.insertedIds[0];
1336
+ const shippedOrder = await insertData('orderTest', { status: 'shipped', amount: 200, customer: testUser._id.toString() }, {}, testUser, false, false, false);
1337
+ shippedOrderId = shippedOrder.insertedIds[0];
1338
+ const otherOrder = await insertData('orderTest', { status: 'pending', amount: 300, customer: 'other_user_id' }, {}, testUser, false, false, false);
1339
+ otherUserOrderId = otherOrder.insertedIds[0];
1340
+ testUser.roles = originalRoles; // Restaurer les vrais rôles pour le test
1341
+ });
1342
+
1343
+ afterEach(async () => {
1344
+ await (await getCollectionForUser(testUser)).deleteMany({ _user: testUser.username });
1345
+ await (await getCollectionForUser(testSystemUser)).deleteMany({ _user: testSystemUser.username });
1346
+ await deleteModels({ _user: testUser.username });
1347
+ await deleteModels({ _user: testSystemUser.username });
1348
+ });
1349
+
1350
+ afterAll(async () =>{
1351
+ await purgeData(testUser);
1352
+ await purgeData(testSystemUser);
1353
+ await datasCollection.drop();
1354
+ })
1355
+
1356
+ // --- TEST POUR insertData ---
1357
+ it('should REJECT inserting data that violates the permission filter', async () => {
1358
+ // This should fail because the amount (1500) is not < 1000
1359
+ const result = await insertData('orderTest', { status: 'new', amount: 1500, customer: 'new_customer' }, {}, testUser, false, false);
1360
+
1361
+ expect(result.success).toBe(false);
1362
+
1363
+ });
1364
+
1365
+ it('should ALLOW inserting data that respects the permission filter', async () => {
1366
+ // This should succeed because the amount (500) is < 1000
1367
+ const result = await insertData('orderTest', { status: 'new', amount: 500, customer: 'new_customer' }, {}, testUser, false, false);
1368
+
1369
+ expect(result.success, `L'insertion aurait dû réussir: ${result.error}`).toBe(true);
1370
+ expect(result.insertedIds).toHaveLength(1);
1371
+ });
1372
+
1373
+ // --- TESTS POUR editData ---
1374
+ it('should ALLOW editing a document that matches the permission filter', async () => {
1375
+ // Le filtre de permission est { status: 'pending' }
1376
+ const result = await editData('orderTest', { _id: pendingOrderId }, { amount: 150 }, {}, testUser, false, false);
1377
+
1378
+ expect(result.success, `editData failed with error: ${result.error}`).toBe(true);
1379
+ expect(result.modifiedCount).toBeGreaterThanOrEqual(0); // Peut être 0 si le hash ne change pas
1380
+
1381
+ const coll = await getCollectionForUser(testUser);
1382
+ const doc = await coll.findOne({ _id: new ObjectId(pendingOrderId)});
1383
+ expect(doc.amount).toBe(150);
1384
+ });
1385
+
1386
+ it('should REJECT editing a document that does NOT match the permission filter', async () => {
1387
+ // On tente de modifier la commande 'shipped', mais la permission ne l'autorise que pour 'pending'
1388
+ const result = await editData('orderTest', { $eq: ['$_id', new ObjectId(shippedOrderId)] }, { amount: 250 }, {}, testUser, false, false);
1389
+
1390
+ expect(result.success).toBe(false);
1391
+ });
1392
+
1393
+ // --- TESTS POUR deleteData ---
1394
+ it('should ALLOW deleting a document that matches the permission filter', async () => {
1395
+ // La permission autorise la suppression des commandes où `customer` est l'ID de l'utilisateur
1396
+ const result = await deleteData('orderTest', { _id: new ObjectId(pendingOrderId) }, testUser, false, false);
1397
+
1398
+ expect(result.success, `deleteData failed with error: ${result.error}`).toBe(true);
1399
+ expect(result.deletedCount).toBe(1);
1400
+
1401
+ const doc = await (await getCollectionForUser(testUser)).findOne({ _id: new ObjectId(pendingOrderId) });
1402
+ expect(doc).toBeNull();
1403
+ });
1404
+
1405
+ it('should REJECT deleting a document that does NOT match the permission filter', async () => {
1406
+ // On tente de supprimer une commande qui n'appartient pas à l'utilisateur
1407
+ const result = await deleteData('orderTest', { _id: otherUserOrderId }, testUser, false, false);
1408
+
1409
+ // La fonction va trouver 0 document à supprimer après application du filtre de permission
1410
+ expect(result.success).toBe(true); // La fonction réussit, mais ne supprime rien
1411
+ expect(result.deletedCount).toBe(0);
1412
+
1413
+ const doc = await (await getCollectionForUser(testSystemUser)).findOne({ _id: new ObjectId(otherUserOrderId) });
1414
+ expect(doc).not.toBeNull(); // Le document existe toujours
1415
+ });
1416
+ });
1282
1417
  });