@flowerforce/flowerbase 1.8.3 → 1.8.4-beta.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.
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/utils.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,aAAa,EAAW,QAAQ,EAAE,MAAM,aAAa,CAAA;AA0E9D;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,gBAAuB,KAAG,OAAO,CAAC,QAAQ,CAkB5E,CAAA;AA2qBD,eAAO,MAAM,gBAAgB;kHAppB1B,aAAa;iHA4jBb,aAAa;uHA1cb,aAAa;CAsiBf,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/triggers/utils.ts"],"names":[],"mappings":"AASA,OAAO,EAAE,aAAa,EAAW,QAAQ,EAAE,MAAM,aAAa,CAAA;AA6E9D;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAU,gBAAuB,KAAG,OAAO,CAAC,QAAQ,CAkB5E,CAAA;AAmrBD,eAAO,MAAM,gBAAgB;kHA5pB1B,aAAa;iHAmkBb,aAAa;uHAhdb,aAAa;CA6iBf,CAAA"}
@@ -27,11 +27,13 @@ exports.TRIGGER_HANDLERS = exports.loadTriggers = void 0;
27
27
  const fs_1 = __importDefault(require("fs"));
28
28
  const node_path_1 = __importDefault(require("node:path"));
29
29
  const node_cron_1 = __importDefault(require("node-cron"));
30
+ const bson_1 = require("bson");
30
31
  const constants_1 = require("../../constants");
31
32
  const utils_1 = require("../../monitoring/utils");
32
33
  const state_1 = require("../../state");
33
34
  const utils_2 = require("../../utils");
34
35
  const context_1 = require("../../utils/context");
36
+ const normalizeTriggerPayload = (value) => bson_1.EJSON.deserialize(bson_1.EJSON.serialize(value, { relaxed: false }));
35
37
  const registerOnClose = (app, handler, label) => {
36
38
  if (app.server) {
37
39
  app.server.once('close', () => {
@@ -148,7 +150,8 @@ const handleCronTrigger = (_a) => __awaiter(void 0, [_a], void 0, function* ({ c
148
150
  currentFunction: triggerHandler,
149
151
  functionName,
150
152
  functionsList,
151
- services
153
+ services,
154
+ deserializeArgs: false
152
155
  });
153
156
  }
154
157
  catch (error) {
@@ -332,7 +335,7 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
332
335
  meta: Object.assign(Object.assign({}, baseMeta), { event: 'LOGOUT' })
333
336
  });
334
337
  yield (0, context_1.GenerateContext)({
335
- args: [Object.assign({ user: userData }, op)],
338
+ args: [normalizeTriggerPayload(Object.assign({ user: userData }, op))],
336
339
  app,
337
340
  rules: state_1.StateManager.select("rules"),
338
341
  user: {}, // TODO from currentUser ??
@@ -340,7 +343,8 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
340
343
  functionName,
341
344
  functionsList,
342
345
  services,
343
- runAsSystem: true
346
+ runAsSystem: true,
347
+ deserializeArgs: false
344
348
  });
345
349
  }
346
350
  catch (error) {
@@ -352,7 +356,6 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
352
356
  meta: Object.assign(Object.assign({}, baseMeta), { event: 'LOGOUT' }),
353
357
  error
354
358
  });
355
- console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
356
359
  }
357
360
  return;
358
361
  }
@@ -389,7 +392,7 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
389
392
  meta: Object.assign(Object.assign({}, baseMeta), { event: 'LOGIN' })
390
393
  });
391
394
  yield (0, context_1.GenerateContext)({
392
- args: [Object.assign({ user: userData }, op)],
395
+ args: [normalizeTriggerPayload(Object.assign({ user: userData }, op))],
393
396
  app,
394
397
  rules: state_1.StateManager.select("rules"),
395
398
  user: {}, // TODO from currentUser ??
@@ -397,7 +400,8 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
397
400
  functionName,
398
401
  functionsList,
399
402
  services,
400
- runAsSystem: true
403
+ runAsSystem: true,
404
+ deserializeArgs: false
401
405
  });
402
406
  }
403
407
  catch (error) {
@@ -409,7 +413,6 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
409
413
  meta: Object.assign(Object.assign({}, baseMeta), { event: 'LOGIN' }),
410
414
  error
411
415
  });
412
- console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
413
416
  }
414
417
  return;
415
418
  }
@@ -441,7 +444,9 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
441
444
  meta: Object.assign(Object.assign({}, baseMeta), { event: 'DELETE' })
442
445
  });
443
446
  yield (0, context_1.GenerateContext)({
444
- args: isAutoTrigger ? [userData] : [Object.assign({ user: userData }, op)],
447
+ args: isAutoTrigger
448
+ ? [normalizeTriggerPayload(userData)]
449
+ : [normalizeTriggerPayload(Object.assign({ user: userData }, op))],
445
450
  app,
446
451
  rules: state_1.StateManager.select("rules"),
447
452
  user: {}, // TODO from currentUser ??
@@ -449,7 +454,8 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
449
454
  functionName,
450
455
  functionsList,
451
456
  services,
452
- runAsSystem: true
457
+ runAsSystem: true,
458
+ deserializeArgs: false
453
459
  });
454
460
  }
455
461
  catch (error) {
@@ -461,7 +467,6 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
461
467
  meta: Object.assign(Object.assign({}, baseMeta), { event: 'DELETE' }),
462
468
  error
463
469
  });
464
- console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
465
470
  }
466
471
  return;
467
472
  }
@@ -495,7 +500,9 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
495
500
  meta: Object.assign(Object.assign({}, baseMeta), { event: 'UPDATE' })
496
501
  });
497
502
  yield (0, context_1.GenerateContext)({
498
- args: isAutoTrigger ? [userData] : [Object.assign({ user: userData }, op)],
503
+ args: isAutoTrigger
504
+ ? [normalizeTriggerPayload(userData)]
505
+ : [normalizeTriggerPayload(Object.assign({ user: userData }, op))],
499
506
  app,
500
507
  rules: state_1.StateManager.select("rules"),
501
508
  user: {}, // TODO from currentUser ??
@@ -503,7 +510,8 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
503
510
  functionName,
504
511
  functionsList,
505
512
  services,
506
- runAsSystem: true
513
+ runAsSystem: true,
514
+ deserializeArgs: false
507
515
  });
508
516
  }
509
517
  catch (error) {
@@ -515,7 +523,6 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
515
523
  meta: Object.assign(Object.assign({}, baseMeta), { event: 'UPDATE' }),
516
524
  error
517
525
  });
518
- console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
519
526
  }
520
527
  return;
521
528
  }
@@ -586,7 +593,9 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
586
593
  meta: Object.assign(Object.assign({}, baseMeta), { event: 'CREATE' })
587
594
  });
588
595
  yield (0, context_1.GenerateContext)({
589
- args: isAutoTrigger ? [userData] : [Object.assign({ user: userData }, op)],
596
+ args: isAutoTrigger
597
+ ? [normalizeTriggerPayload(userData)]
598
+ : [normalizeTriggerPayload(Object.assign({ user: userData }, op))],
590
599
  app,
591
600
  rules: state_1.StateManager.select("rules"),
592
601
  user: {}, // TODO from currentUser ??
@@ -594,7 +603,8 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
594
603
  functionName,
595
604
  functionsList,
596
605
  services,
597
- runAsSystem: true
606
+ runAsSystem: true,
607
+ deserializeArgs: false
598
608
  });
599
609
  }
600
610
  catch (error) {
@@ -606,7 +616,6 @@ const handleAuthenticationTrigger = (_a) => __awaiter(void 0, [_a], void 0, func
606
616
  meta: Object.assign(Object.assign({}, baseMeta), { event: 'CREATE' }),
607
617
  error
608
618
  });
609
- console.log("🚀 ~ handleAuthenticationTrigger ~ error:", error);
610
619
  }
611
620
  });
612
621
  });
@@ -677,14 +686,15 @@ const handleDataBaseTrigger = (_a) => __awaiter(void 0, [_a], void 0, function*
677
686
  });
678
687
  try {
679
688
  yield (0, context_1.GenerateContext)({
680
- args: [change],
689
+ args: [normalizeTriggerPayload(change)],
681
690
  app,
682
691
  rules: state_1.StateManager.select("rules"),
683
692
  user: {}, // TODO add from?
684
693
  currentFunction: triggerHandler,
685
694
  functionName,
686
695
  functionsList,
687
- services
696
+ services,
697
+ deserializeArgs: false
688
698
  });
689
699
  }
690
700
  catch (error) {
@@ -77,7 +77,7 @@ export declare const getErrorDetails: (error: unknown) => {
77
77
  };
78
78
  export declare const pickHeaders: (headers: FastifyRequest["headers"]) => unknown;
79
79
  export declare const createEventStore: (maxAgeMs: number, maxEvents: number) => EventStore;
80
- export declare const classifyRequest: (url: string) => "function" | "auth" | "http_endpoint" | "trigger" | "api" | "http";
80
+ export declare const classifyRequest: (url: string) => "function" | "api" | "auth" | "http_endpoint" | "trigger" | "http";
81
81
  export declare const createEventId: () => string;
82
82
  export declare const buildRulesMeta: (meta?: MonitMeta) => {
83
83
  collection: string;
@@ -490,7 +490,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
490
490
  emitMongoEvent('findOne');
491
491
  return null;
492
492
  }
493
- const winningRole = (0, utils_2.getWinningRole)(result, user, roles);
493
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(result, user, roles);
494
494
  logDebug('findOne winningRole', {
495
495
  collection: collName,
496
496
  winningRoleName: (_a = winningRole === null || winningRole === void 0 ? void 0 : winningRole.name) !== null && _a !== void 0 ? _a : null,
@@ -546,7 +546,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
546
546
  const formattedQuery = (0, utils_3.getFormattedQuery)(filters, query, user);
547
547
  // Retrieve the document to check permissions before deleting
548
548
  const result = yield collection.findOne(buildAndQuery(formattedQuery));
549
- const winningRole = (0, utils_2.getWinningRole)(result, user, roles);
549
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(result, user, roles);
550
550
  logDebug('delete winningRole', {
551
551
  collection: collName,
552
552
  userId: getUserId(user),
@@ -600,7 +600,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
600
600
  try {
601
601
  if (!run_as_system) {
602
602
  (0, utils_3.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.CREATE);
603
- const winningRole = (0, utils_2.getWinningRole)(data, user, roles);
603
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(data, user, roles);
604
604
  const { status, document } = winningRole
605
605
  ? yield (0, machines_1.checkValidation)(winningRole, {
606
606
  type: 'insert',
@@ -673,7 +673,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
673
673
  }
674
674
  throw new Error('Update not permitted');
675
675
  }
676
- const winningRole = (0, utils_2.getWinningRole)(result, user, roles);
676
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(result, user, roles);
677
677
  // Check if the update data contains MongoDB update operators (e.g., $set, $inc)
678
678
  const updatedPaths = getUpdatedPaths(normalizedData);
679
679
  const docToCheck = applyDocumentUpdateOperators(result, normalizedData);
@@ -756,7 +756,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
756
756
  : [applyDocumentUpdateOperators(currentDoc, normalizedData)];
757
757
  docToCheck = computedDoc;
758
758
  }
759
- const winningRole = (0, utils_2.getWinningRole)(docToCheck, user, roles);
759
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(docToCheck, user, roles);
760
760
  const { status, document } = winningRole
761
761
  ? yield (0, machines_1.checkValidation)(winningRole, {
762
762
  type: validationType,
@@ -776,7 +776,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
776
776
  emitMongoEvent('findOneAndUpdate');
777
777
  return updateResult;
778
778
  }
779
- const readRole = (0, utils_2.getWinningRole)(updateResult, user, roles);
779
+ const readRole = yield (0, utils_2.getWinningRoleAsync)(updateResult, user, roles);
780
780
  const readResult = readRole
781
781
  ? yield (0, machines_1.checkValidation)(readRole, {
782
782
  type: 'read',
@@ -844,7 +844,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
844
844
  const response = yield originalToArray();
845
845
  const filteredResponse = yield Promise.all(response.map((currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
846
846
  var _a;
847
- const winningRole = (0, utils_2.getWinningRole)(currentDoc, user, roles);
847
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
848
848
  logDebug('find winningRole', {
849
849
  collection: collName,
850
850
  userId: getUserId(user),
@@ -976,7 +976,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
976
976
  const isValidChange = (change) => __awaiter(void 0, void 0, void 0, function* () {
977
977
  const { fullDocument, updateDescription } = change;
978
978
  const hasFullDocument = !!fullDocument;
979
- const winningRole = (0, utils_2.getWinningRole)(fullDocument, user, roles);
979
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(fullDocument, user, roles);
980
980
  const fullDocumentValidation = winningRole
981
981
  ? yield (0, machines_1.checkValidation)(winningRole, {
982
982
  type: 'read',
@@ -1088,7 +1088,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
1088
1088
  (0, utils_3.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.CREATE);
1089
1089
  // Validate each document against user's roles
1090
1090
  const filteredItems = yield Promise.all(documents.map((currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
1091
- const winningRole = (0, utils_2.getWinningRole)(currentDoc, user, roles);
1091
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
1092
1092
  const { status, document } = winningRole
1093
1093
  ? yield (0, machines_1.checkValidation)(winningRole, {
1094
1094
  type: 'insert',
@@ -1133,7 +1133,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
1133
1133
  const updatedPaths = getUpdatedPaths(normalizedData);
1134
1134
  const docsToCheck = result.map((currentDoc) => applyDocumentUpdateOperators(currentDoc, normalizedData));
1135
1135
  const filteredItems = yield Promise.all(docsToCheck.map((currentDoc, index) => __awaiter(void 0, void 0, void 0, function* () {
1136
- const winningRole = (0, utils_2.getWinningRole)(currentDoc, user, roles);
1136
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
1137
1137
  const { status, document } = winningRole
1138
1138
  ? yield (0, machines_1.checkValidation)(winningRole, {
1139
1139
  type: 'write',
@@ -1187,7 +1187,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
1187
1187
  const data = yield collection.find({ $and: formattedQuery }).toArray();
1188
1188
  // Filter and validate each document based on user's roles
1189
1189
  const filteredItems = yield Promise.all(data.map((currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
1190
- const winningRole = (0, utils_2.getWinningRole)(currentDoc, user, roles);
1190
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
1191
1191
  const { status, document } = winningRole
1192
1192
  ? yield (0, machines_1.checkValidation)(winningRole, {
1193
1193
  type: 'delete',
@@ -35,11 +35,11 @@ export declare const generateContextData: ({ user, services, app, rules, current
35
35
  request: {
36
36
  remoteIPAddress: string | undefined;
37
37
  id?: string | undefined;
38
- host?: string | undefined;
39
- method?: string | undefined;
40
- url?: string | undefined;
41
38
  ips?: string[];
39
+ host?: string | undefined;
42
40
  hostname?: string | undefined;
41
+ url?: string | undefined;
42
+ method?: string | undefined;
43
43
  ip?: string | undefined;
44
44
  };
45
45
  user: unknown;
@@ -1,4 +1,5 @@
1
1
  import { PermissionExpression } from './interface';
2
2
  import { MachineContext } from './machines/interface';
3
+ export declare const evaluateExpandedExpression: (expression: unknown, params: MachineContext["params"], user?: MachineContext["user"]) => Promise<boolean>;
3
4
  export declare const evaluateExpression: (params: MachineContext["params"], expression?: PermissionExpression, user?: MachineContext["user"]) => Promise<boolean>;
4
5
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/roles/helpers.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AAkBrD,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,cAAc,CAAC,QAAQ,CAAC,EAChC,aAAa,oBAAoB,EACjC,OAAO,cAAc,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,OAAO,CAoBjB,CAAA"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/utils/roles/helpers.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAClD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAA;AA+DrD,eAAO,MAAM,0BAA0B,GACrC,YAAY,OAAO,EACnB,QAAQ,cAAc,CAAC,QAAQ,CAAC,EAChC,OAAO,cAAc,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,OAAO,CAwDjB,CAAA;AAED,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,cAAc,CAAC,QAAQ,CAAC,EAChC,aAAa,oBAAoB,EACjC,OAAO,cAAc,CAAC,MAAM,CAAC,KAC5B,OAAO,CAAC,OAAO,CAMjB,CAAA"}
@@ -12,13 +12,15 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.evaluateExpression = void 0;
15
+ exports.evaluateExpression = exports.evaluateExpandedExpression = void 0;
16
16
  const services_1 = require("../../services");
17
17
  const state_1 = require("../../state");
18
18
  const context_1 = require("../context");
19
19
  const rules_1 = require("../rules");
20
20
  const utils_1 = __importDefault(require("../rules-matcher/utils"));
21
21
  const functionsConditions = ['%%true', '%%false'];
22
+ const andConditions = ['$and', '%and'];
23
+ const orConditions = ['$or', '%or'];
22
24
  const normalizeUserRole = (user) => {
23
25
  if (!user)
24
26
  return user;
@@ -34,17 +36,84 @@ const normalizeUserRole = (user) => {
34
36
  ? Object.assign(Object.assign({}, candidate), { role: customRole })
35
37
  : user;
36
38
  };
39
+ const buildEvaluationContext = (params, user) => {
40
+ var _a, _b, _c;
41
+ const normalizedUser = normalizeUserRole(user);
42
+ return Object.assign(Object.assign(Object.assign({}, ((_a = params.expansions) !== null && _a !== void 0 ? _a : {})), ((_b = params.cursor) !== null && _b !== void 0 ? _b : {})), { '%%root': params.cursor, '%%prevRoot': (_c = params.expansions) === null || _c === void 0 ? void 0 : _c['%%prevRoot'], '%%user': normalizedUser, '%%true': true, '%%false': false });
43
+ };
44
+ const getFunctionCondition = (expression) => {
45
+ if (!expression || typeof expression !== 'object' || Array.isArray(expression)) {
46
+ return null;
47
+ }
48
+ const entries = Object.entries(expression);
49
+ if (entries.length !== 1) {
50
+ return null;
51
+ }
52
+ const [key, value] = entries[0];
53
+ if (!functionsConditions.includes(key)) {
54
+ return null;
55
+ }
56
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
57
+ return null;
58
+ }
59
+ return Object.prototype.hasOwnProperty.call(value, '%function')
60
+ ? [key, value]
61
+ : null;
62
+ };
63
+ const evaluateExpandedExpression = (expression, params, user) => __awaiter(void 0, void 0, void 0, function* () {
64
+ if (typeof expression === 'boolean') {
65
+ return expression;
66
+ }
67
+ if (!expression || typeof expression !== 'object') {
68
+ return Boolean(expression);
69
+ }
70
+ const block = expression;
71
+ const functionCondition = getFunctionCondition(block);
72
+ if (functionCondition) {
73
+ return evaluateComplexExpression(functionCondition, params, user);
74
+ }
75
+ const andKey = andConditions.find((key) => Object.prototype.hasOwnProperty.call(block, key));
76
+ if (andKey) {
77
+ const conditions = Array.isArray(block[andKey]) ? block[andKey] : [];
78
+ if (!conditions.length)
79
+ return true;
80
+ for (const condition of conditions) {
81
+ if (!(yield (0, exports.evaluateExpandedExpression)(condition, params, user))) {
82
+ return false;
83
+ }
84
+ }
85
+ return true;
86
+ }
87
+ const orKey = orConditions.find((key) => Object.prototype.hasOwnProperty.call(block, key));
88
+ if (orKey) {
89
+ const conditions = Array.isArray(block[orKey]) ? block[orKey] : [];
90
+ if (!conditions.length)
91
+ return true;
92
+ for (const condition of conditions) {
93
+ if (yield (0, exports.evaluateExpandedExpression)(condition, params, user)) {
94
+ return true;
95
+ }
96
+ }
97
+ return false;
98
+ }
99
+ const keys = Object.keys(block);
100
+ if (keys.length > 1) {
101
+ for (const key of keys) {
102
+ if (!(yield (0, exports.evaluateExpandedExpression)({ [key]: block[key] }, params, user))) {
103
+ return false;
104
+ }
105
+ }
106
+ return true;
107
+ }
108
+ return utils_1.default.checkRule(block, buildEvaluationContext(params, user), {});
109
+ });
110
+ exports.evaluateExpandedExpression = evaluateExpandedExpression;
37
111
  const evaluateExpression = (params, expression, user) => __awaiter(void 0, void 0, void 0, function* () {
38
- var _a;
39
112
  if (!expression || typeof expression === 'boolean')
40
113
  return !!expression;
41
- const normalizedUser = normalizeUserRole(user);
42
- const value = Object.assign(Object.assign(Object.assign({}, params.expansions), params.cursor), { '%%root': params.cursor, '%%prevRoot': (_a = params.expansions) === null || _a === void 0 ? void 0 : _a['%%prevRoot'], '%%user': normalizedUser, '%%true': true, '%%false': false });
114
+ const value = buildEvaluationContext(params, user);
43
115
  const conditions = (0, rules_1.expandQuery)(expression, value);
44
- const complexCondition = Object.entries(conditions).find(([key]) => functionsConditions.includes(key));
45
- return complexCondition
46
- ? yield evaluateComplexExpression(complexCondition, params, normalizedUser)
47
- : utils_1.default.checkRule(conditions, value, {});
116
+ return (0, exports.evaluateExpandedExpression)(conditions, params, user);
48
117
  });
49
118
  exports.evaluateExpression = evaluateExpression;
50
119
  const evaluateComplexExpression = (condition, params, user) => __awaiter(void 0, void 0, void 0, function* () {
@@ -12,6 +12,7 @@ import { LogMachineInfoParams } from './interface';
12
12
  * @returns {Role | null} - Returns the first role that matches the `apply_when` condition, or `null` if none match.
13
13
  */
14
14
  export declare const getWinningRole: (document: OptionalId<Document> | null, user: User, roles?: Role[]) => Role | null;
15
+ export declare const getWinningRoleAsync: (document: OptionalId<Document> | null, user: User, roles?: Role[]) => Promise<Role | null>;
15
16
  /**
16
17
  * Checks if the `apply_when` condition is valid for the given user and document.
17
18
  *
@@ -22,6 +23,7 @@ export declare const getWinningRole: (document: OptionalId<Document> | null, use
22
23
  * @returns {boolean} - Returns `true` if at least one valid rule is found, otherwise `false`.
23
24
  */
24
25
  export declare const checkApplyWhen: (apply_when: Role["apply_when"], user: User, document: OptionalId<Document> | null) => boolean;
26
+ export declare const checkApplyWhenAsync: (apply_when: Role["apply_when"], user: User, document: OptionalId<Document> | null) => Promise<boolean>;
25
27
  /**
26
28
  * Logs machine step information if logging is enabled.
27
29
  *
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/roles/machines/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAGzC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,EACrC,MAAM,IAAI,EACV,QAAO,IAAI,EAAO,KACjB,IAAI,GAAG,IAQT,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,YAAY,IAAI,CAAC,YAAY,CAAC,EAC9B,MAAM,IAAI,EACV,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,YAQtC,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GAAI,sCAK5B,oBAAoB,SAEtB,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../../src/utils/roles/machines/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,SAAS,CAAA;AAC9C,OAAO,EAAE,IAAI,EAAE,MAAM,oBAAoB,CAAA;AAIzC,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,OAAO,EAAE,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAElD;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,EACrC,MAAM,IAAI,EACV,QAAO,IAAI,EAAO,KACjB,IAAI,GAAG,IAQT,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC9B,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,EACrC,MAAM,IAAI,EACV,QAAO,IAAI,EAAO,KACjB,OAAO,CAAC,IAAI,GAAG,IAAI,CAQrB,CAAA;AAED;;;;;;;;GAQG;AACH,eAAO,MAAM,cAAc,GACzB,YAAY,IAAI,CAAC,YAAY,CAAC,EAC9B,MAAM,IAAI,EACV,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,YAQtC,CAAA;AAED,eAAO,MAAM,mBAAmB,GAC9B,YAAY,IAAI,CAAC,YAAY,CAAC,EAC9B,MAAM,IAAI,EACV,UAAU,UAAU,CAAC,QAAQ,CAAC,GAAG,IAAI,qBActC,CAAA;AAED;;;;;;;;;;GAUG;AACH,eAAO,MAAM,cAAc,GAAI,sCAK5B,oBAAoB,SAEtB,CAAA"}
@@ -1,7 +1,17 @@
1
1
  "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
2
11
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.logMachineInfo = exports.checkApplyWhen = exports.getWinningRole = void 0;
12
+ exports.logMachineInfo = exports.checkApplyWhenAsync = exports.checkApplyWhen = exports.getWinningRoleAsync = exports.getWinningRole = void 0;
4
13
  const utils_1 = require("../../../services/mongodb-atlas/utils");
14
+ const helpers_1 = require("../helpers");
5
15
  /**
6
16
  * Determines the first applicable role for a given user and document.
7
17
  *
@@ -22,6 +32,17 @@ const getWinningRole = (document, user, roles = []) => {
22
32
  return null;
23
33
  };
24
34
  exports.getWinningRole = getWinningRole;
35
+ const getWinningRoleAsync = (document_1, user_1, ...args_1) => __awaiter(void 0, [document_1, user_1, ...args_1], void 0, function* (document, user, roles = []) {
36
+ if (!roles.length)
37
+ return null;
38
+ for (const role of roles) {
39
+ if (yield (0, exports.checkApplyWhenAsync)(role.apply_when, user, document)) {
40
+ return role;
41
+ }
42
+ }
43
+ return null;
44
+ });
45
+ exports.getWinningRoleAsync = getWinningRoleAsync;
25
46
  /**
26
47
  * Checks if the `apply_when` condition is valid for the given user and document.
27
48
  *
@@ -40,6 +61,17 @@ const checkApplyWhen = (apply_when, user, document) => {
40
61
  return !!validRule.length;
41
62
  };
42
63
  exports.checkApplyWhen = checkApplyWhen;
64
+ const checkApplyWhenAsync = (apply_when, user, document) => __awaiter(void 0, void 0, void 0, function* () {
65
+ return (0, helpers_1.evaluateExpression)({
66
+ type: 'read',
67
+ roles: [],
68
+ cursor: document,
69
+ expansions: {
70
+ '%%prevRoot': undefined
71
+ }
72
+ }, apply_when, user);
73
+ });
74
+ exports.checkApplyWhenAsync = checkApplyWhenAsync;
43
75
  /**
44
76
  * Logs machine step information if logging is enabled.
45
77
  *
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flowerbase",
3
- "version": "1.8.3",
3
+ "version": "1.8.4-beta.2",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -0,0 +1,112 @@
1
+ import { ObjectId } from 'bson'
2
+ import { GenerateContext } from '../../../utils/context'
3
+ import { StateManager } from '../../../state'
4
+ import { TRIGGER_HANDLERS } from '../utils'
5
+
6
+ jest.mock('../../../utils/context', () => ({
7
+ GenerateContext: jest.fn()
8
+ }))
9
+
10
+ jest.mock('../../../state', () => ({
11
+ StateManager: {
12
+ select: jest.fn()
13
+ }
14
+ }))
15
+
16
+ const mockedGenerateContext = jest.mocked(GenerateContext)
17
+ const mockedStateSelect = StateManager.select as jest.Mock
18
+
19
+ describe('TRIGGER_HANDLERS.DATABASE', () => {
20
+ beforeEach(() => {
21
+ mockedGenerateContext.mockReset()
22
+ mockedGenerateContext.mockResolvedValue(undefined)
23
+ mockedStateSelect.mockReset()
24
+ mockedStateSelect.mockReturnValue(undefined)
25
+ })
26
+
27
+ it('preserves BSON ObjectId values in fullDocument before invoking the trigger function', async () => {
28
+ const changeListeners: Record<string, (...args: any[]) => unknown> = {}
29
+ const close = jest.fn(async () => undefined)
30
+ const watch = jest.fn(() => ({
31
+ on: jest.fn((event: string, listener: (...args: any[]) => unknown) => {
32
+ changeListeners[event] = listener
33
+ }),
34
+ close
35
+ }))
36
+
37
+ const collection = { watch }
38
+ const db = jest.fn(() => ({ collection: jest.fn(() => collection) }))
39
+ const client = { db }
40
+
41
+ const app = {
42
+ mongo: {
43
+ changestream: { client }
44
+ },
45
+ server: {
46
+ once: jest.fn()
47
+ }
48
+ } as any
49
+
50
+ await TRIGGER_HANDLERS.DATABASE({
51
+ config: {
52
+ database: 'flowerbase-test',
53
+ collection: 'activityLogs',
54
+ operation_types: ['INSERT'],
55
+ full_document: true,
56
+ full_document_before_change: false,
57
+ match: {},
58
+ project: {}
59
+ } as any,
60
+ triggerHandler: { code: 'module.exports = async function () {}' } as any,
61
+ functionsList: {
62
+ logTriggerEvent: { code: 'module.exports = async function () {}' }
63
+ } as any,
64
+ services: {} as any,
65
+ app,
66
+ triggerName: 'log-trigger-event',
67
+ triggerType: 'DATABASE',
68
+ functionName: 'logTriggerEvent'
69
+ })
70
+
71
+ const documentId = new ObjectId('507f1f77bcf86cd799439011')
72
+ const ownerId = new ObjectId('507f191e810c19729de860ea')
73
+
74
+ await changeListeners.change({
75
+ clusterTime: new Date(),
76
+ operationType: 'insert',
77
+ ns: { db: 'flowerbase-test', coll: 'activityLogs' },
78
+ documentKey: { _id: documentId },
79
+ fullDocument: {
80
+ _id: documentId,
81
+ ownerId
82
+ }
83
+ })
84
+
85
+ expect(mockedGenerateContext).toHaveBeenCalledTimes(1)
86
+ expect(mockedGenerateContext).toHaveBeenCalledWith(
87
+ expect.objectContaining({
88
+ deserializeArgs: false,
89
+ args: [
90
+ expect.objectContaining({
91
+ documentKey: expect.objectContaining({
92
+ _id: expect.any(ObjectId)
93
+ }),
94
+ fullDocument: expect.objectContaining({
95
+ _id: expect.any(ObjectId),
96
+ ownerId: expect.any(ObjectId)
97
+ })
98
+ })
99
+ ]
100
+ })
101
+ )
102
+
103
+ const payload = mockedGenerateContext.mock.calls[0][0].args[0] as {
104
+ documentKey: { _id: ObjectId }
105
+ fullDocument: { _id: ObjectId; ownerId: ObjectId }
106
+ }
107
+
108
+ expect(payload.documentKey._id.toHexString()).toBe(documentId.toHexString())
109
+ expect(payload.fullDocument._id.toHexString()).toBe(documentId.toHexString())
110
+ expect(payload.fullDocument.ownerId.toHexString()).toBe(ownerId.toHexString())
111
+ })
112
+ })