data-primals-engine 1.2.6-rc3 → 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.
@@ -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
  };