data-primals-engine 1.2.6-rc2 → 1.2.6

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.
@@ -1,6 +1,6 @@
1
1
  import {Logger} from "../../gameObject.js";
2
2
  import {BSON, ObjectId} from "mongodb";
3
- import {promisify} from 'node:util';
3
+ import util, {promisify} from 'node:util';
4
4
  import crypto from "node:crypto";
5
5
  import {exec, execFile} from 'node:child_process';
6
6
  import sanitizeHtml from 'sanitize-html';
@@ -67,7 +67,6 @@ import {
67
67
  import NodeCache from "node-cache";
68
68
  import AWS from 'aws-sdk';
69
69
  import checkDiskSpace from "check-disk-space";
70
- import {Worker} from 'worker_threads';
71
70
  import {addFile, removeFile} from "../file.js";
72
71
  import {downloadFromS3, getUserS3Config, listS3Backups, uploadToS3} from "../bucket.js";
73
72
  import {
@@ -78,7 +77,7 @@ import {getAllPacks} from "../../packs.js";
78
77
  import {Config} from "../../config.js";
79
78
  import {profiles} from "../../../client/src/constants.js";
80
79
  import {registerRoutes, sendSseToUser} from "./data.routes.js";
81
- import {importJobs, modelsCache, mongoDBWhitelist} from "./data.core.js";
80
+ import {importJobs, modelsCache, mongoDBWhitelist, runCryptoWorkerTask, runImportExportWorker} from "./data.core.js";
82
81
  import readXlsxFile from "read-excel-file/node";
83
82
 
84
83
  let engine;
@@ -500,75 +499,6 @@ export const getAPILang = (langs) => {
500
499
  }, []).sort(((p,r) => p.quality < r.quality ? 1 : -1))?.[0].lang.split(/[-_]/)?.[0];
501
500
  }
502
501
 
503
- /**
504
- * Exécute une tâche d'import/export (parsing, stringify) dans un worker thread.
505
- * @param {('parse-json'|'parse-csv'|'stringify-json')} action - L'action à effectuer.
506
- * @param {object} payload - Les données nécessaires pour l'action.
507
- * @returns {Promise<any>} - Une promesse qui se résout avec les données traitées.
508
- */
509
- function runImportExportWorker(action, payload) {
510
- return new Promise((resolve, reject) => {
511
- const workerPath = path.resolve(process.cwd(), './src/workers/import-export-worker.js');
512
- const worker = new Worker(workerPath);
513
-
514
- worker.postMessage({ action, payload });
515
-
516
- worker.on('message', (result) => {
517
- if (result.success) {
518
- resolve(result.data);
519
- } else {
520
- // Correction : On s'assure de toujours passer une chaîne de caractères à new Error()
521
- const errorMessage = result.error || `Import/Export Worker failed with an unknown error. Action: ${action}.`;
522
- reject(new Error(errorMessage));
523
- }
524
- worker.terminate();
525
- });
526
-
527
- worker.on('error', (err) => {
528
- reject(err);
529
- worker.terminate();
530
- });
531
-
532
- worker.on('exit', (code) => {
533
- if (code !== 0) {
534
- reject(new Error(`Import/Export Worker stopped with exit code ${code}`));
535
- }
536
- });
537
- });
538
- }
539
- /** Exécute une tâche de cryptographie dans un worker thread.
540
- * @param {('encrypt'|'decrypt'|'hash')} action - L'action à effectuer.
541
- * @param {object} payload - Les données nécessaires pour l'action.
542
- * @returns {Promise<any>} - Une promesse qui se résout avec le résultat (si pertinent).
543
- */
544
- function runCryptoWorkerTask(action, payload) {
545
- return new Promise((resolve, reject) => {
546
- const workerPath = path.resolve(process.cwd(), './src/workers/crypto-worker.js');
547
- const worker = new Worker(workerPath);
548
-
549
- worker.postMessage({ action, payload });
550
-
551
- worker.on('message', (result) => {
552
- if (result.success) {
553
- resolve(result.data); // Résout avec les données (ex: le hash) ou undefined si pas de retour
554
- } else {
555
- reject(new Error(result.error));
556
- }
557
- worker.terminate();
558
- });
559
-
560
- worker.on('error', (err) => {
561
- reject(err);
562
- worker.terminate();
563
- });
564
-
565
- worker.on('exit', (code) => {
566
- if (code !== 0) {
567
- reject(new Error(`Crypto Worker stopped with exit code ${code}`));
568
- }
569
- });
570
- });
571
- }
572
502
 
573
503
  export function validateModelStructure(modelStructure) {
574
504
 
@@ -2111,22 +2041,41 @@ async function insertAndResolveRelations(doc, model, collection, me, idMap) {
2111
2041
 
2112
2042
  for (const field of model.fields) {
2113
2043
  if (field.type === 'relation' && field.relationFilter && docToProcess[field.name]) {
2114
- const relatedIds = Array.isArray(docToProcess[field.name]) ? docToProcess[field.name] : [docToProcess[field.name]];
2115
- for (const id of relatedIds) {
2116
- const targetCollection = await getCollectionForUser(me, field.targetModel);
2117
- const validationQuery = {
2118
- _id: new ObjectId(id), // L'ID doit correspondre
2119
- ...field.relationFilter // ET le document doit respecter le filtre
2120
- };
2121
- const relatedDoc = await targetCollection.findOne(validationQuery);
2122
- if (!relatedDoc) {
2123
- // Si on ne trouve rien, c'est que l'ID est invalide ou ne respecte pas le filtre.
2124
- throw new Error(`La valeur '${id}' pour le champ '${field.name}' ne respecte pas le filtre de relation défini.`);
2125
- }
2044
+
2045
+ const relatedIds = Array.isArray(docToProcess[field.name])
2046
+ ? docToProcess[field.name]
2047
+ : [docToProcess[field.name]];
2048
+
2049
+ // Préparer un filtre global : match si _id dans relatedIds ET respecte relationFilter
2050
+ const validationQuery = {
2051
+ $and: [
2052
+ { $in: ['$_id', relatedIds.map(id => ({ $toObjectId: id }))] },
2053
+ field.relationFilter
2054
+ ]
2055
+ };
2056
+
2057
+ const relatedDocs = await searchData({
2058
+ filter: validationQuery,
2059
+ model: field.relation,
2060
+ limit: relatedIds.length
2061
+ }, me);
2062
+
2063
+ if ((relatedDocs?.count || 0) !== relatedIds.length) {
2064
+ const invalidIds = relatedIds.filter(id =>
2065
+ !relatedDocs.data.some(doc => doc._id.toString() === id.toString())
2066
+ );
2067
+ throw new Error(
2068
+ i18n.t(
2069
+ 'api.data.relationFilterFailed',
2070
+ 'Les valeurs {{values}} pour le champ {{field}} ne respectent pas le filtre de relation défini.',
2071
+ { field: field.name, values: invalidIds.join(', ') }
2072
+ )
2073
+ );
2126
2074
  }
2127
2075
  }
2128
2076
  }
2129
2077
 
2078
+
2130
2079
  // Insertion en conservant éventuellement l'ID original
2131
2080
  const result = docToProcess._id
2132
2081
  ? await collection.insertOne(docToProcess)
@@ -2366,28 +2315,37 @@ const internalEditOrPatchData = async (modelName, filter, data, files, user, isP
2366
2315
  }
2367
2316
 
2368
2317
  for (const field of model.fields) {
2369
- // On ne vérifie que si un champ de relation avec un filtre est en cours de modification.
2370
- if (field.type === 'relation' && field.relationFilter && updateData[field.name] !== undefined) {
2318
+ if (field.type === 'relation' && field.relationFilter && updateData[field.name]) {
2319
+
2371
2320
  const relatedIds = Array.isArray(updateData[field.name])
2372
2321
  ? updateData[field.name]
2373
- : (updateData[field.name] ? [updateData[field.name]] : []);
2322
+ : [updateData[field.name]];
2374
2323
 
2375
- for (const id of relatedIds) {
2376
- if (!id || !isObjectId(id)) continue; // Ignorer les valeurs null/invalides
2377
-
2378
- const targetCollection = await getCollectionForUser(user, field.relation);
2379
-
2380
- const validationQuery = {
2381
- _id: new ObjectId(id),
2382
- ...field.relationFilter
2383
- };
2324
+ // Préparer un filtre global : match si _id dans relatedIds ET respecte relationFilter
2325
+ const validationQuery = {
2326
+ $and: [
2327
+ { $in: ['$_id', relatedIds.map(id => ({ $toObjectId: id }))] },
2328
+ field.relationFilter
2329
+ ]
2330
+ };
2384
2331
 
2385
- const relatedDoc = await targetCollection.findOne(validationQuery);
2332
+ const relatedDocs = await searchData({
2333
+ filter: validationQuery,
2334
+ model: field.relation,
2335
+ limit: relatedIds.length
2336
+ }, user);
2386
2337
 
2387
- if (!relatedDoc) {
2388
- // Si on ne trouve rien, c'est que l'ID est invalide ou ne respecte pas le filtre.
2389
- throw new Error(`La valeur '${id}' pour le champ '${field.name}' ne respecte pas le filtre de relation défini.`);
2390
- }
2338
+ if ((relatedDocs?.count || 0) !== relatedIds.length) {
2339
+ const invalidIds = relatedIds.filter(id =>
2340
+ !relatedDocs.data.some(doc => doc._id.toString() === id.toString())
2341
+ );
2342
+ throw new Error(
2343
+ i18n.t(
2344
+ 'api.data.relationFilterFailed',
2345
+ 'Les valeurs {{values}} pour le champ {{field}} ne respectent pas le filtre de relation défini.',
2346
+ { field: field.name, values: invalidIds.join(', ') }
2347
+ )
2348
+ );
2391
2349
  }
2392
2350
  }
2393
2351
  }
@@ -40,10 +40,10 @@ import {tutorialsConfig} from "../../../client/src/tutorials.js";
40
40
  import {
41
41
  cancelAlerts, deleteData,
42
42
  dumpUserData,
43
- editData, exportData,
43
+ editData, editModel, exportData,
44
44
  getModel, getResource,
45
45
  handleCustomEndpointRequest,
46
- handleDemoInitialization, importData, insertData, installPack,
46
+ handleDemoInitialization, importData, insertData, installPack, loadFromDump,
47
47
  patchData, searchData, validateModelStructure
48
48
  } from "./data.js";
49
49
  import process from "node:process";
@@ -1,10 +1,12 @@
1
1
 
2
- import process from "process";
3
2
  import {Logger} from "../gameObject.js";
4
3
  import {MongoDatabase} from "../engine.js";
4
+ import {ObjectId} from "mongodb";
5
5
 
6
6
  export let modelsCollection, datasCollection, filesCollection, packsCollection;
7
7
 
8
+ export {ObjectId};
9
+
8
10
  let engine, logger;
9
11
  export async function onInit(defaultEngine) {
10
12
  engine = defaultEngine;
@@ -6,7 +6,7 @@ import crypto from "node:crypto";
6
6
  import ivm from 'isolated-vm';
7
7
 
8
8
  import {Logger} from "../gameObject.js";
9
- import {deleteData, insertData, patchData, scheduleAlerts, searchData} from "./data/index.js";
9
+ import {deleteData, getModel, insertData, patchData, scheduleAlerts, searchData} from "./data/index.js";
10
10
  import {emailDefaultConfig, maxExecutionsByStep, maxWorkflowSteps} from "../constants.js";
11
11
  import {ChatOpenAI} from "@langchain/openai";
12
12
  import {ChatGoogleGenerativeAI} from "@langchain/google-genai";
@@ -17,6 +17,8 @@ import {sendEmail} from "../email.js";
17
17
 
18
18
  import * as workflowModule from './workflow.js';
19
19
  import util from "node:util";
20
+ import {object_equals} from "../core.js";
21
+ import {isConditionMet} from "../filter.js";
20
22
 
21
23
  let logger = null;
22
24
  export async function onInit(defaultEngine) {
@@ -219,12 +221,18 @@ export async function executeSafeJavascript(actionDef, context, user) {
219
221
  };
220
222
 
221
223
  // 1. Build the sandboxed API
222
- await jail.set('_db_create', new ivm.Reference((modelName, dataObject) => insertData(modelName, JSON.parse(dataObject), {}, user, false)));
224
+ await jail.set('_db_create', new ivm.Reference(async (modelName, dataObject) => {
225
+ const result = await insertData(modelName, JSON.parse(dataObject), {}, user, false);
226
+ if (result.success && result.insertedIds) {
227
+ result.insertedIds = result.insertedIds.map(id => id.toString());
228
+ }
229
+ return new ivm.ExternalCopy(result).copyInto();
230
+ }));
223
231
  await jail.set('_db_find', new ivm.Reference(find));
224
232
  await jail.set('_db_findOne', new ivm.Reference(findOne));
225
233
 
226
- await jail.set('_db_update', new ivm.Reference((modelName, filter, updateObject) => patchData(modelName, JSON.parse(filter), JSON.parse(updateObject), {}, user, false)));
227
- await jail.set('_db_delete', new ivm.Reference((modelName, filter) => deleteData(modelName, JSON.parse(filter), user, false)));
234
+ await jail.set('_db_update', new ivm.Reference(async (modelName, filter, updateObject) => patchData(modelName, JSON.parse(filter), JSON.parse(updateObject), {}, user, false)));
235
+ await jail.set('_db_delete', new ivm.Reference(async (modelName, filter) => deleteData(modelName, JSON.parse(filter), user, false)));
228
236
 
229
237
  const createLoggerMethod = (level) => {
230
238
  return (...args) => {
@@ -1099,102 +1107,96 @@ export async function substituteVariables(template, contextData, user) {
1099
1107
  }
1100
1108
 
1101
1109
  /**
1102
- * Déclenche l'instanciation d'un workflowRun si les conditions sont remplies.
1103
- * Vérifie le type d'événement et le filtre de données du déclencheur.
1104
- * Crée un document 'workflowRun' pour une exécution asynchrone ultérieure.
1110
+ * Triggers the instantiation of a workflowRun if conditions are met.
1111
+ * Checks the event type and trigger's data filter.
1112
+ * Creates a 'workflowRun' document for later asynchronous execution.
1105
1113
  *
1106
- * @param {object} triggerData - La donnée qui a déclenché le(s) workflow(s) (peut être un document de données ou un document de modèle).
1107
- * @param {object} user - L'utilisateur associé.
1108
- * @param {'DataAdded' | 'DataEdited' | 'DataDeleted' | 'ModelAdded' | 'ModelEdited' | 'ModelDeleted'} eventType - Le type d'événement.
1114
+ * @param {object} triggerData - The data that triggered the workflow(s) (can be a data document or model document).
1115
+ * @param {object} user - The associated user.
1116
+ * @param {'DataAdded' | 'DataEdited' | 'DataDeleted' | 'ModelAdded' | 'ModelEdited' | 'ModelDeleted'} eventType - The event type.
1109
1117
  */
1110
- export async function triggerWorkflows(triggerData, user, eventType) {
1111
- const dataId = eventType.startsWith('Model') ? null : triggerData._id;
1112
-
1118
+ export async function triggerWorkflows(triggerData, user, eventType) {
1113
1119
  const trigger = async (triggerData, user, eventType) => {
1114
-
1115
1120
  // Basic validation
1116
1121
  if (!triggerData || !user || !eventType) {
1117
- console.warn("triggerWorkflows: Appel invalide - triggerData, user, ou eventType manquant.", { hasTriggerData: !!triggerData, hasUser: !!user, eventType });
1122
+ console.warn("triggerWorkflows: Invalid call - missing triggerData, user, or eventType.", {
1123
+ hasTriggerData: !!triggerData,
1124
+ hasUser: !!user,
1125
+ eventType
1126
+ });
1118
1127
  return;
1119
1128
  }
1120
- // Determine the model name and data ID based on the event type
1129
+
1130
+ // Determine model name and data ID based on event type
1121
1131
  const targetModelName = eventType.startsWith('Model') ? triggerData.name : triggerData._model;
1122
- const dataId = eventType.startsWith('Model') ? null : triggerData._id; // ID only relevant for data events
1132
+ const dataId = eventType.startsWith('Model') ? null : triggerData._id;
1123
1133
 
1124
1134
  if (!targetModelName) {
1125
- console.warn(`triggerWorkflows: Impossible de déterminer le nom du modèle pour l'événement ${eventType}.`, triggerData);
1135
+ console.warn(`triggerWorkflows: Cannot determine model name for event ${eventType}.`, triggerData);
1126
1136
  return;
1127
1137
  }
1128
1138
 
1129
1139
  console.log(`[Workflow Trigger] Event: ${eventType}, Model: ${targetModelName}${dataId ? `, Data ID: ${dataId}` : ''}, User: ${user.username}`);
1130
1140
 
1131
1141
  try {
1132
- const dbCollection = await getCollectionForUser(user); // Collection des données utilisateur
1142
+ const dbCollection = await getCollectionForUser(user);
1133
1143
 
1134
- // 1. Trouver les WorkflowTriggers pertinents
1144
+ // 1. Find relevant WorkflowTriggers
1135
1145
  const workflowTriggers = await dbCollection.find({
1136
1146
  _model: 'workflowTrigger',
1137
1147
  targetModel: targetModelName,
1138
1148
  isActive: true,
1139
- onEvent: eventType, // Assurez-vous que le champ s'appelle bien 'onEvent' dans votre modèle
1149
+ onEvent: eventType,
1140
1150
  $or: [{_user: user._user}, {_user: user.username}]
1141
1151
  }).toArray();
1142
1152
 
1143
1153
  if (workflowTriggers.length === 0) {
1144
- console.debug(`[Workflow Trigger] Aucun déclencheur actif trouvé pour ${targetModelName} / ${eventType}.`);
1154
+ console.debug(`[Workflow Trigger] No active triggers found for ${targetModelName}/${eventType}.`);
1145
1155
  return;
1146
1156
  }
1147
- console.debug(`[Workflow Trigger] Trouvé ${workflowTriggers.length} déclencheur(s) potentiel(s) pour ${targetModelName} / ${eventType}.`);
1157
+ console.debug(`[Workflow Trigger] Found ${workflowTriggers.length} potential trigger(s) for ${targetModelName}/${eventType}.`);
1148
1158
 
1149
- // 2. Pour chaque déclencheur trouvé, vérifier le filtre de données et créer un workflowRun
1159
+ // 2. For each trigger, verify data filter and create workflowRun
1150
1160
  for (const trigger of workflowTriggers) {
1151
- console.debug(`[Workflow Trigger] Vérification du déclencheur ${trigger._id} (${trigger.name || 'Sans nom'})...`);
1161
+ console.debug(`[Workflow Trigger] Evaluating trigger ${trigger._id} (${trigger.name || 'Unnamed'})...`);
1152
1162
 
1153
- // 3. Vérifier le filtre de données (dataFilter) si applicable
1154
- if (eventType.startsWith('Data') && trigger.dataFilter && dataId) {
1163
+ // 3. Check data filter if applicable
1164
+ if (eventType.startsWith('Data') && trigger.dataFilter) {
1155
1165
  let dataFilterCondition = null;
1156
1166
  try {
1157
- // dataFilter est supposé être stocké comme un objet (ou une string JSON valide)
1167
+ // dataFilter is expected to be stored as an object or valid JSON string
1158
1168
  if (typeof trigger.dataFilter === 'string') {
1159
1169
  dataFilterCondition = JSON.parse(trigger.dataFilter);
1160
1170
  } else if (typeof trigger.dataFilter === 'object' && trigger.dataFilter !== null) {
1161
1171
  dataFilterCondition = trigger.dataFilter;
1162
1172
  }
1163
1173
  } catch (parseError) {
1164
- console.error(`[Workflow Trigger] Erreur de parsing JSON pour dataFilter du trigger ${trigger._id}:`, parseError);
1165
- continue; // Passer au trigger suivant si le filtre est invalide
1174
+ console.error(`[Workflow Trigger] JSON parsing error for dataFilter in trigger ${trigger._id}:`, parseError);
1175
+ continue; // Skip to next trigger if filter is invalid
1166
1176
  }
1167
1177
 
1168
1178
  try {
1169
- const finalFilter = {
1170
- '$and': [
1171
- {"_id": { "$toObjectId": triggerData._id.toString() }},
1172
- dataFilterCondition // Applique la condition du trigger
1173
- ]
1174
- };
1175
-
1176
-
1177
- console.debug(`[Workflow Trigger] Vérification dataFilter pour trigger ${trigger._id} avec filtre combiné:`, JSON.stringify(finalFilter));
1178
- const match = await searchData({ model: triggerData._model, filter: finalFilter, limit: 1 }, user);
1179
- if (!match.count) {
1180
- console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter non satisfait par la donnée ${dataId}. WorkflowRun non créé.`);
1181
- continue; // Passer au trigger suivant
1182
- } else {
1183
- console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter satisfait par la donnée ${dataId}.`);
1179
+ const mod = await getModel(targetModelName, user);
1180
+ const filterMatches = isConditionMet(mod, dataFilterCondition, triggerData, [], user);
1181
+
1182
+ if (!filterMatches) {
1183
+ console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter not satisfied by data. Skipping workflowRun creation.`);
1184
+ continue;
1184
1185
  }
1186
+ console.debug(`[Workflow Trigger] Trigger ${trigger._id}: dataFilter satisfied.`);
1185
1187
  } catch (filterError) {
1186
- console.error(`[Workflow Trigger] Erreur lors de la conversion ou de l'exécution du dataFilter pour le trigger ${trigger._id}:`, filterError);
1187
- continue; // Ne pas créer en cas d'erreur de filtre
1188
+ console.error(`[Workflow Trigger] Error evaluating dataFilter for trigger ${trigger._id}:`, filterError);
1189
+ continue;
1188
1190
  }
1189
- } // Fin de la vérification dataFilter
1191
+ }
1190
1192
 
1191
- // 4. Si les filtres (eventType, dataFilter) sont passés, créer l'instance workflowRun
1193
+ // 4. If filters passed, create workflowRun instance
1192
1194
  if (!trigger.workflow || !isObjectId(trigger.workflow)) {
1193
- console.warn(`[Workflow Trigger] Trigger ${trigger._id} n'a pas de workflow valide associé.`);
1195
+ console.warn(`[Workflow Trigger] Trigger ${trigger._id} has no valid associated workflow.`);
1194
1196
  continue;
1195
1197
  }
1196
1198
 
1197
- // a. Récupérer la définition du Workflow (juste pour vérifier qu'il existe)
1199
+ // a. Verify workflow exists
1198
1200
  const workflowDefinition = await dbCollection.findOne({
1199
1201
  _id: new ObjectId(trigger.workflow),
1200
1202
  _model: 'workflow',
@@ -1202,21 +1204,20 @@ export async function triggerWorkflows(triggerData, user, eventType) {
1202
1204
  });
1203
1205
 
1204
1206
  if (!workflowDefinition) {
1205
- console.warn(`[Workflow Trigger] Workflow ${trigger.workflow} associé au trigger ${trigger._id} non trouvé.`);
1207
+ console.warn(`[Workflow Trigger] Workflow ${trigger.workflow} associated with trigger ${trigger._id} not found.`);
1206
1208
  continue;
1207
1209
  }
1208
1210
 
1209
- // b. Créer le document workflowRun
1211
+ // b. Create workflowRun document
1210
1212
  const workflowRunData = {
1211
1213
  _model: 'workflowRun',
1212
1214
  _user: user._user || user.username,
1213
- workflow: workflowDefinition._id, // Référence au workflow parent
1214
- contextData: { // Contexte initial
1215
- triggerDataModel: targetModelName,// Modèle de la donnée déclencheuse
1216
- triggerData: triggerData // Inclure la donnée déclencheuse
1217
- // Vous pourriez ajouter d'autres infos ici si nécessaire
1215
+ workflow: workflowDefinition._id,
1216
+ contextData: {
1217
+ triggerDataModel: targetModelName,
1218
+ triggerData: triggerData
1218
1219
  },
1219
- status: 'pending', // Statut initial
1220
+ status: 'pending',
1220
1221
  owner: null,
1221
1222
  startedAt: new Date()
1222
1223
  };
@@ -1224,30 +1225,25 @@ export async function triggerWorkflows(triggerData, user, eventType) {
1224
1225
  try {
1225
1226
  const insertResult = await dbCollection.insertOne(workflowRunData);
1226
1227
  if (insertResult.insertedId) {
1227
- console.info(`[Workflow Trigger] WorkflowRun ${insertResult.insertedId} créé pour le workflow ${workflowDefinition.name} (ID: ${workflowDefinition._id}) déclenché par ${trigger._id}.`);
1228
-
1228
+ console.info(`[Workflow Trigger] Created workflowRun ${insertResult.insertedId} for workflow ${workflowDefinition.name} (ID: ${workflowDefinition._id}) triggered by ${trigger._id}.`);
1229
1229
  await workflowModule.processWorkflowRun(insertResult.insertedId, user);
1230
1230
  } else {
1231
- console.error(`[Workflow Trigger] Échec de la création du WorkflowRun pour le workflow ${workflowDefinition._id} (Trigger: ${trigger._id}).`);
1231
+ console.error(`[Workflow Trigger] Failed to create workflowRun for workflow ${workflowDefinition._id} (Trigger: ${trigger._id}).`);
1232
1232
  }
1233
1233
  } catch (insertError) {
1234
- console.error(`[Workflow Trigger] Erreur lors de l'insertion du WorkflowRun pour le workflow ${workflowDefinition._id} (Trigger: ${trigger._id}):`, insertError);
1234
+ console.error(`[Workflow Trigger] Error creating workflowRun for workflow ${workflowDefinition._id} (Trigger: ${trigger._id}):`, insertError);
1235
1235
  }
1236
-
1237
- } // Fin de la boucle des triggers
1238
-
1236
+ }
1239
1237
  } catch (error) {
1240
- console.error(`[Workflow Trigger] Erreur générale dans triggerWorkflows pour ${targetModelName}${dataId ? ` ID: ${dataId}` : ''} (Event: ${eventType}):`, error);
1238
+ console.error(`[Workflow Trigger] General error in triggerWorkflows for ${targetModelName}${dataId ? ` ID: ${dataId}` : ''} (Event: ${eventType}):`, error);
1241
1239
  }
1242
1240
  }
1243
1241
 
1244
1242
  return new Promise((resolve) => setTimeout(async () => {
1245
1243
  await trigger(triggerData, user, eventType);
1246
1244
  resolve();
1247
- }, 0)
1248
- );
1245
+ }, 0));
1249
1246
  }
1250
-
1251
1247
  /**
1252
1248
  * Processes a workflowRun instance step-by-step.
1253
1249
  * Fetches the run, evaluates conditions, executes actions, and transitions
@@ -795,7 +795,7 @@ describe('Intégration des fonctions CRUD de données avec validation complète'
795
795
  name: 'produit',
796
796
  type: 'relation',
797
797
  relation: 'produitTestFiltre',
798
- relationFilter: { actif: true } // Only link active products
798
+ relationFilter: { "$eq":["$actif", true] } // Only link active products
799
799
  }
800
800
  ]
801
801
  };