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.
- package/README.md +1 -1
- package/client/package-lock.json +247 -4354
- package/client/package.json +16 -15
- package/client/src/App.scss +1 -0
- package/client/src/DataEditor.jsx +1 -1
- package/client/src/Field.jsx +4 -16
- package/client/src/translations.js +17 -0
- package/package.json +120 -120
- package/src/engine.js +0 -1
- package/src/filter.js +35 -5
- package/src/modules/data/data.core.js +72 -1
- package/src/modules/data/data.js +59 -101
- package/src/modules/data/data.routes.js +2 -2
- package/src/modules/mongodb.js +3 -1
- package/src/modules/workflow.js +66 -70
- package/test/data.integration.test.js +1 -1
- package/test/workflow.actions.integration.test.js +474 -0
package/src/modules/data/data.js
CHANGED
|
@@ -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
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
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
|
-
|
|
2370
|
-
|
|
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
|
-
:
|
|
2322
|
+
: [updateData[field.name]];
|
|
2374
2323
|
|
|
2375
|
-
|
|
2376
|
-
|
|
2377
|
-
|
|
2378
|
-
|
|
2379
|
-
|
|
2380
|
-
|
|
2381
|
-
|
|
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
|
-
|
|
2332
|
+
const relatedDocs = await searchData({
|
|
2333
|
+
filter: validationQuery,
|
|
2334
|
+
model: field.relation,
|
|
2335
|
+
limit: relatedIds.length
|
|
2336
|
+
}, user);
|
|
2386
2337
|
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
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";
|
package/src/modules/mongodb.js
CHANGED
|
@@ -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;
|
package/src/modules/workflow.js
CHANGED
|
@@ -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) =>
|
|
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
|
-
*
|
|
1103
|
-
*
|
|
1104
|
-
*
|
|
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 -
|
|
1107
|
-
* @param {object} user -
|
|
1108
|
-
* @param {'DataAdded' | 'DataEdited' | 'DataDeleted' | 'ModelAdded' | 'ModelEdited' | 'ModelDeleted'} eventType -
|
|
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:
|
|
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
|
-
|
|
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;
|
|
1132
|
+
const dataId = eventType.startsWith('Model') ? null : triggerData._id;
|
|
1123
1133
|
|
|
1124
1134
|
if (!targetModelName) {
|
|
1125
|
-
console.warn(`triggerWorkflows:
|
|
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);
|
|
1142
|
+
const dbCollection = await getCollectionForUser(user);
|
|
1133
1143
|
|
|
1134
|
-
// 1.
|
|
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,
|
|
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]
|
|
1154
|
+
console.debug(`[Workflow Trigger] No active triggers found for ${targetModelName}/${eventType}.`);
|
|
1145
1155
|
return;
|
|
1146
1156
|
}
|
|
1147
|
-
console.debug(`[Workflow Trigger]
|
|
1157
|
+
console.debug(`[Workflow Trigger] Found ${workflowTriggers.length} potential trigger(s) for ${targetModelName}/${eventType}.`);
|
|
1148
1158
|
|
|
1149
|
-
// 2.
|
|
1159
|
+
// 2. For each trigger, verify data filter and create workflowRun
|
|
1150
1160
|
for (const trigger of workflowTriggers) {
|
|
1151
|
-
console.debug(`[Workflow Trigger]
|
|
1161
|
+
console.debug(`[Workflow Trigger] Evaluating trigger ${trigger._id} (${trigger.name || 'Unnamed'})...`);
|
|
1152
1162
|
|
|
1153
|
-
// 3.
|
|
1154
|
-
if (eventType.startsWith('Data') && trigger.dataFilter
|
|
1163
|
+
// 3. Check data filter if applicable
|
|
1164
|
+
if (eventType.startsWith('Data') && trigger.dataFilter) {
|
|
1155
1165
|
let dataFilterCondition = null;
|
|
1156
1166
|
try {
|
|
1157
|
-
// dataFilter
|
|
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]
|
|
1165
|
-
continue; //
|
|
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
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
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]
|
|
1187
|
-
continue;
|
|
1188
|
+
console.error(`[Workflow Trigger] Error evaluating dataFilter for trigger ${trigger._id}:`, filterError);
|
|
1189
|
+
continue;
|
|
1188
1190
|
}
|
|
1189
|
-
}
|
|
1191
|
+
}
|
|
1190
1192
|
|
|
1191
|
-
// 4.
|
|
1193
|
+
// 4. If filters passed, create workflowRun instance
|
|
1192
1194
|
if (!trigger.workflow || !isObjectId(trigger.workflow)) {
|
|
1193
|
-
console.warn(`[Workflow Trigger] Trigger ${trigger._id}
|
|
1195
|
+
console.warn(`[Workflow Trigger] Trigger ${trigger._id} has no valid associated workflow.`);
|
|
1194
1196
|
continue;
|
|
1195
1197
|
}
|
|
1196
1198
|
|
|
1197
|
-
// a.
|
|
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}
|
|
1207
|
+
console.warn(`[Workflow Trigger] Workflow ${trigger.workflow} associated with trigger ${trigger._id} not found.`);
|
|
1206
1208
|
continue;
|
|
1207
1209
|
}
|
|
1208
1210
|
|
|
1209
|
-
// b.
|
|
1211
|
+
// b. Create workflowRun document
|
|
1210
1212
|
const workflowRunData = {
|
|
1211
1213
|
_model: 'workflowRun',
|
|
1212
1214
|
_user: user._user || user.username,
|
|
1213
|
-
workflow: workflowDefinition._id,
|
|
1214
|
-
contextData: {
|
|
1215
|
-
triggerDataModel: targetModelName
|
|
1216
|
-
triggerData: triggerData
|
|
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',
|
|
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]
|
|
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]
|
|
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]
|
|
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]
|
|
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
|
|
798
|
+
relationFilter: { "$eq":["$actif", true] } // Only link active products
|
|
799
799
|
}
|
|
800
800
|
]
|
|
801
801
|
};
|