@flowerforce/flowerbase 1.8.4-beta.4 → 1.8.4-beta.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/README.md +3 -0
  2. package/dist/auth/plugins/jwt.d.ts +1 -1
  3. package/dist/auth/plugins/jwt.d.ts.map +1 -1
  4. package/dist/features/functions/controller.d.ts.map +1 -1
  5. package/dist/features/functions/controller.js +29 -2
  6. package/dist/features/functions/dtos.d.ts +2 -0
  7. package/dist/features/functions/dtos.d.ts.map +1 -1
  8. package/dist/features/functions/interface.d.ts +2 -0
  9. package/dist/features/functions/interface.d.ts.map +1 -1
  10. package/dist/features/functions/utils.d.ts +3 -1
  11. package/dist/features/functions/utils.d.ts.map +1 -1
  12. package/dist/features/functions/utils.js +3 -1
  13. package/dist/services/index.d.ts +8 -8
  14. package/dist/services/mongodb-atlas/index.d.ts.map +1 -1
  15. package/dist/services/mongodb-atlas/index.js +91 -20
  16. package/dist/services/mongodb-atlas/model.d.ts +2 -0
  17. package/dist/services/mongodb-atlas/model.d.ts.map +1 -1
  18. package/dist/utils/context/helpers.d.ts +24 -24
  19. package/dist/utils/roles/machines/read/D/validators.d.ts +1 -1
  20. package/dist/utils/roles/machines/read/D/validators.d.ts.map +1 -1
  21. package/dist/utils/roles/machines/write/C/validators.d.ts +1 -1
  22. package/dist/utils/roles/machines/write/C/validators.d.ts.map +1 -1
  23. package/package.json +1 -1
  24. package/src/features/functions/__tests__/controller.test.ts +42 -2
  25. package/src/features/functions/__tests__/utils.test.ts +44 -0
  26. package/src/features/functions/controller.ts +36 -1
  27. package/src/features/functions/dtos.ts +2 -0
  28. package/src/features/functions/interface.ts +2 -0
  29. package/src/features/functions/utils.ts +13 -0
  30. package/src/services/mongodb-atlas/__tests__/realmCompatibility.test.ts +116 -0
  31. package/src/services/mongodb-atlas/index.ts +122 -25
  32. package/src/services/mongodb-atlas/model.ts +8 -0
  33. package/tsconfig.json +0 -5
  34. package/tsconfig.spec.json +2 -1
package/README.md CHANGED
@@ -652,6 +652,7 @@ const todos = user.mongoClient('mongodb-atlas')
652
652
 
653
653
  await todos.insertOne({ title: 'Ship docs update', done: false })
654
654
  const openTodos = await todos.find({ done: false })
655
+ const labels = await todos.distinct('label', { done: false })
655
656
  ```
656
657
 
657
658
  `@flowerforce/flowerbase-client` supports:
@@ -661,6 +662,8 @@ const openTodos = await todos.find({ done: false })
661
662
  - change streams with `watch()` async iterator
662
663
  - BSON/EJSON interoperability (`ObjectId`, `Date`, etc.)
663
664
 
665
+ `bulkWrite()` is available on the MongoDB Atlas wrapper only for privileged functions executed with `run_as_system: true`. Regular client-scoped calls continue to use the per-operation methods (`insertOne`, `updateOne`, `updateMany`, `deleteOne`, `deleteMany`, `insertMany`).
666
+
664
667
  ## 💡 Use Cases by Feature
665
668
 
666
669
  ### 🔐 Authentication
@@ -9,6 +9,6 @@ type Options = {
9
9
  * @param {Object} opts - Options for the plugin.
10
10
  * @param {string} opts.secret - The secret key used for signing JWTs.
11
11
  */
12
- declare const _default: (fastify: import("fastify/fastify").FastifyInstance<import("fastify/fastify").RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify/fastify").FastifyBaseLogger, import("fastify/fastify").FastifyTypeProviderDefault>, opts: Options) => Promise<void>;
12
+ declare const _default: (fastify: import("fastify").FastifyInstance<import("fastify").RawServerDefault, import("http").IncomingMessage, import("http").ServerResponse<import("http").IncomingMessage>, import("fastify").FastifyBaseLogger, import("fastify").FastifyTypeProviderDefault>, opts: Options) => Promise<void>;
13
13
  export default _default;
14
14
  //# sourceMappingURL=jwt.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../../src/auth/plugins/jwt.ts"],"names":[],"mappings":"AAKA,KAAK,OAAO,GAAG;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAkBD;;;;;;;GAOG;iUAC8C,OAAO;AAAxD,wBA4GE"}
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../../src/auth/plugins/jwt.ts"],"names":[],"mappings":"AAKA,KAAK,OAAO,GAAG;IACb,MAAM,EAAE,MAAM,CAAA;CACf,CAAA;AAkBD;;;;;;;GAOG;iSAC8C,OAAO;AAAxD,wBA4GE"}
@@ -1 +1 @@
1
- {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/features/functions/controller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAKvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AAmHhD,eAAO,MAAM,iCAAiC,GAAI,OAAO,OAAO,KAAG,OA0BlE,CAAA;AAID,eAAO,MAAM,6BAA6B,GAAI,OAAO,OAAO,KAAG,OAgD9D,CAAA;AAqHD,eAAO,MAAM,oCAAoC,GAAI,QAAQ,QAAQ,YAClC,CAAA;AAEnC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,kBA0RjC,CAAA"}
1
+ {"version":3,"file":"controller.d.ts","sourceRoot":"","sources":["../../../src/features/functions/controller.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAKvC,OAAO,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAA;AA0IhD,eAAO,MAAM,iCAAiC,GAAI,OAAO,OAAO,KAAG,OA0BlE,CAAA;AAID,eAAO,MAAM,6BAA6B,GAAI,OAAO,OAAO,KAAG,OAgD9D,CAAA;AAqHD,eAAO,MAAM,oCAAoC,GAAI,QAAQ,QAAQ,YAClC,CAAA;AAEnC;;;;;GAKG;AACH,eAAO,MAAM,mBAAmB,EAAE,kBAsSjC,CAAA"}
@@ -26,6 +26,24 @@ const constants_1 = require("../../constants");
26
26
  const services_1 = require("../../services");
27
27
  const context_1 = require("../../utils/context");
28
28
  const utils_1 = require("./utils");
29
+ const SUPPORTED_QUERY_METHODS = [
30
+ 'find',
31
+ 'findOne',
32
+ 'count',
33
+ 'countDocuments',
34
+ 'distinct',
35
+ 'deleteOne',
36
+ 'insertOne',
37
+ 'updateOne',
38
+ 'findOneAndUpdate',
39
+ 'aggregate',
40
+ 'insertMany',
41
+ 'bulkWrite',
42
+ 'updateMany',
43
+ 'deleteMany'
44
+ ];
45
+ const isSupportedQueryMethod = (value) => typeof value === 'string' &&
46
+ SUPPORTED_QUERY_METHODS.includes(value);
29
47
  const normalizeUser = (payload) => {
30
48
  var _a, _b, _c, _d, _e, _f;
31
49
  if (!payload)
@@ -290,7 +308,10 @@ const functionsController = (app_1, _a) => __awaiter(void 0, [app_1, _a], void 0
290
308
  if (!serviceFn) {
291
309
  throw new Error(`Service "${req.body.service}" does not exist`);
292
310
  }
293
- const [{ database, collection, query, filter, update, projection, options, returnNewDocument, document, documents, pipeline = [] }] = args;
311
+ const [{ database, collection, key, query, filter, update, projection, options, returnNewDocument, document, documents, operations, pipeline = [] }] = args;
312
+ if (!isSupportedQueryMethod(method)) {
313
+ throw new Error(`Unsupported service method "${String(method)}"`);
314
+ }
294
315
  const currentMethod = serviceFn(app, { rules, user })
295
316
  .db(database)
296
317
  .collection(collection)[method];
@@ -298,6 +319,7 @@ const functionsController = (app_1, _a) => __awaiter(void 0, [app_1, _a], void 0
298
319
  const operatorsByType = yield (0, utils_1.executeQuery)({
299
320
  currentMethod,
300
321
  query,
322
+ key,
301
323
  filter,
302
324
  update,
303
325
  projection,
@@ -305,10 +327,15 @@ const functionsController = (app_1, _a) => __awaiter(void 0, [app_1, _a], void 0
305
327
  returnNewDocument,
306
328
  document,
307
329
  documents,
330
+ operations,
308
331
  pipeline,
309
332
  isClient: true
310
333
  });
311
- const serviceResult = yield operatorsByType[method]();
334
+ const operator = operatorsByType[method];
335
+ if (typeof operator !== 'function') {
336
+ throw new Error(`Unsupported service method "${method}"`);
337
+ }
338
+ const serviceResult = yield operator();
312
339
  res.type('application/json');
313
340
  return serializeEjson(serviceResult);
314
341
  }
@@ -17,6 +17,7 @@ export type FunctionCallBase64Dto = {
17
17
  type ArgumentsData = Arguments<{
18
18
  database: string;
19
19
  collection: string;
20
+ key?: string;
20
21
  filter?: Document;
21
22
  query: Parameters<GetOperatorsFunction>;
22
23
  update: Document;
@@ -25,6 +26,7 @@ type ArgumentsData = Arguments<{
25
26
  returnNewDocument?: boolean;
26
27
  document: Document;
27
28
  documents: Document[];
29
+ operations?: Document[];
28
30
  pipeline?: Document[];
29
31
  }>;
30
32
  export type Base64Function = {
@@ -1 +1 @@
1
- {"version":3,"file":"dtos.d.ts","sourceRoot":"","sources":["../../../src/features/functions/dtos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AAEzE,KAAK,UAAU,GAAG,OAAO,CAAC,MAAM,UAAU,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC,CAAA;AAE1E,MAAM,MAAM,eAAe,GACvB;IACA,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,aAAa,CAAA;CACzB,GACC;IACA,SAAS,EAAE,aAAa,CAAA;IACxB,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,EAAE,eAAe,CAAA;CACzB,CAAA;AAEH,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,KAAK,aAAa,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,KAAK,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAA;IACvC,MAAM,EAAE,QAAQ,CAAA;IAChB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,OAAO,CAAC,EAAE,QAAQ,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;CACtB,CAAC,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA"}
1
+ {"version":3,"file":"dtos.d.ts","sourceRoot":"","sources":["../../../src/features/functions/dtos.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAA;AAC3C,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AAEzE,KAAK,UAAU,GAAG,OAAO,CAAC,MAAM,UAAU,CAAC,oBAAoB,CAAC,EAAE,OAAO,CAAC,CAAA;AAE1E,MAAM,MAAM,eAAe,GACvB;IACA,IAAI,EAAE,UAAU,CAAA;IAChB,SAAS,EAAE,aAAa,CAAA;CACzB,GACC;IACA,SAAS,EAAE,aAAa,CAAA;IACxB,IAAI,EAAE,UAAU,CAAA;IAChB,OAAO,EAAE,eAAe,CAAA;CACzB,CAAA;AAEH,MAAM,MAAM,qBAAqB,GAAG;IAClC,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,CAAA;CACxB,CAAA;AAED,KAAK,aAAa,GAAG,SAAS,CAAC;IAC7B,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;IAClB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,KAAK,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAA;IACvC,MAAM,EAAE,QAAQ,CAAA;IAChB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,OAAO,CAAC,EAAE,QAAQ,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAA;IACvB,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAA;CACtB,CAAC,CAAA;AAEF,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,OAAO,EAAE,MAAM,CAAA;CAChB,CAAA;AAED,KAAK,QAAQ,GAAG;IACd,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB,CAAA"}
@@ -22,12 +22,14 @@ export type ExecuteQueryParams = {
22
22
  currentMethod: ReturnType<GetOperatorsFunction>[keyof ReturnType<GetOperatorsFunction>];
23
23
  query: Parameters<GetOperatorsFunction>;
24
24
  update: Document;
25
+ key?: string;
25
26
  filter?: Document;
26
27
  projection?: Document;
27
28
  options?: Document;
28
29
  returnNewDocument?: boolean;
29
30
  document: Document;
30
31
  documents: Document[];
32
+ operations?: Document[];
31
33
  pipeline: Document[];
32
34
  isClient?: boolean;
33
35
  };
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/functions/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAEhD,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE,eAAe,CAAA;IACpB,aAAa,EAAE,SAAS,CAAA;IACxB,SAAS,EAAE,KAAK,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC,MAAM,UAAU,CAAC,oBAAoB,CAAC,CAAC,CAAA;IACvF,KAAK,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAA;IACvC,MAAM,EAAE,QAAQ,CAAA;IAChB,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,OAAO,CAAC,EAAE,QAAQ,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,KAAK,0BAA0B,GAAG;IAChC,aAAa,EAAE,SAAS,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;CACb,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,CAC/B,GAAG,EAAE,eAAe,EACpB,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,0BAA0B,KACjD,OAAO,CAAC,IAAI,CAAC,CAAA"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../../../src/features/functions/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,MAAM,SAAS,CAAA;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAClC,OAAO,EAAE,oBAAoB,EAAE,MAAM,oCAAoC,CAAA;AACzE,OAAO,EAAE,KAAK,EAAE,MAAM,oBAAoB,CAAA;AAE1C,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,MAAM,CAAA;IACZ,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,OAAO,CAAA;IACvB,gBAAgB,CAAC,EAAE,OAAO,CAAA;CAC3B;AAED,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,cAAc,EAAE,MAAM,CAAC,GAAG;IACpD,IAAI,EAAE,MAAM,CAAA;IACZ,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,CAAA;AAED,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAA;AAEhD,MAAM,MAAM,uBAAuB,GAAG;IACpC,GAAG,EAAE,eAAe,CAAA;IACpB,aAAa,EAAE,SAAS,CAAA;IACxB,SAAS,EAAE,KAAK,CAAA;CACjB,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG;IAC/B,aAAa,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAC,MAAM,UAAU,CAAC,oBAAoB,CAAC,CAAC,CAAA;IACvF,KAAK,EAAE,UAAU,CAAC,oBAAoB,CAAC,CAAA;IACvC,MAAM,EAAE,QAAQ,CAAA;IAChB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,QAAQ,CAAA;IACjB,UAAU,CAAC,EAAE,QAAQ,CAAA;IACrB,OAAO,CAAC,EAAE,QAAQ,CAAA;IAClB,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,QAAQ,EAAE,QAAQ,CAAA;IAClB,SAAS,EAAE,QAAQ,EAAE,CAAA;IACrB,UAAU,CAAC,EAAE,QAAQ,EAAE,CAAA;IACvB,QAAQ,EAAE,QAAQ,EAAE,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAED,KAAK,0BAA0B,GAAG;IAChC,aAAa,EAAE,SAAS,CAAA;IACxB,KAAK,EAAE,KAAK,CAAA;CACb,CAAA;AAED,MAAM,MAAM,kBAAkB,GAAG,CAC/B,GAAG,EAAE,eAAe,EACpB,EAAE,aAAa,EAAE,KAAK,EAAE,EAAE,0BAA0B,KACjD,OAAO,CAAC,IAAI,CAAC,CAAA"}
@@ -11,17 +11,19 @@ export declare const loadFunctions: (rootDir?: string) => Promise<Functions>;
11
11
  * @param query -> the query data
12
12
  * @param update -> the update Document that should be deserialized
13
13
  */
14
- export declare const executeQuery: ({ currentMethod, query, update, filter, projection, options, returnNewDocument, document, documents, pipeline, isClient }: ExecuteQueryParams) => Promise<{
14
+ export declare const executeQuery: ({ currentMethod, query, update, key, filter, projection, options, returnNewDocument, document, documents, operations, pipeline, isClient }: ExecuteQueryParams) => Promise<{
15
15
  find: () => Promise<any[]>;
16
16
  findOne: () => Promise<unknown>;
17
17
  count: () => Promise<number>;
18
18
  countDocuments: () => Promise<number>;
19
+ distinct: () => Promise<any[]>;
19
20
  deleteOne: () => Promise<import("mongodb").DeleteResult>;
20
21
  insertOne: () => Promise<import("mongodb").InsertOneResult<Document>>;
21
22
  updateOne: () => Promise<import("mongodb").UpdateResult<Document>>;
22
23
  findOneAndUpdate: () => Promise<Document | null>;
23
24
  aggregate: () => Promise<Document[]>;
24
25
  insertMany: () => Promise<import("mongodb").InsertManyResult<Document>>;
26
+ bulkWrite: () => Promise<import("mongodb").BulkWriteResult>;
25
27
  updateMany: () => Promise<import("mongodb").UpdateResult<Document>>;
26
28
  deleteMany: () => Promise<import("mongodb").DeleteResult>;
27
29
  }>;
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/functions/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGlC,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE3D;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAU,gBAAuB,KAAG,OAAO,CAAC,SAAS,CAwB9E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAU,2HAYhC,kBAAkB;;;;;;;;;;;;;EAsGpB,CAAA"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../../../src/features/functions/utils.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAA;AAGlC,OAAO,EAAE,kBAAkB,EAAE,SAAS,EAAE,MAAM,aAAa,CAAA;AAE3D;;;GAGG;AACH,eAAO,MAAM,aAAa,GAAU,gBAAuB,KAAG,OAAO,CAAC,SAAS,CAwB9E,CAAA;AAED;;;;;GAKG;AACH,eAAO,MAAM,YAAY,GAAU,4IAchC,kBAAkB;;;;;;;;;;;;;;;EAiHpB,CAAA"}
@@ -58,7 +58,7 @@ exports.loadFunctions = loadFunctions;
58
58
  * @param query -> the query data
59
59
  * @param update -> the update Document that should be deserialized
60
60
  */
61
- const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ currentMethod, query, update, filter, projection, options, returnNewDocument, document, documents, pipeline, isClient = false }) {
61
+ const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ currentMethod, query, update, key, filter, projection, options, returnNewDocument, document, documents, operations, pipeline, isClient = false }) {
62
62
  const resolvedQuery = typeof query !== 'undefined'
63
63
  ? query
64
64
  : typeof filter !== 'undefined'
@@ -98,6 +98,7 @@ const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ curren
98
98
  findOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), resolvedProjection, parsedOptions),
99
99
  count: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), parsedOptions),
100
100
  countDocuments: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), parsedOptions),
101
+ distinct: () => currentMethod(key !== null && key !== void 0 ? key : '', bson_1.EJSON.deserialize(resolvedQuery), parsedOptions),
101
102
  deleteOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), parsedOptions),
102
103
  insertOne: () => currentMethod(bson_1.EJSON.deserialize(document)),
103
104
  updateOne: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate), parsedOptions),
@@ -107,6 +108,7 @@ const executeQuery = (_a) => __awaiter(void 0, [_a], void 0, function* ({ curren
107
108
  isClient)).toArray();
108
109
  }),
109
110
  insertMany: () => currentMethod(bson_1.EJSON.deserialize(documents)),
111
+ bulkWrite: () => currentMethod(bson_1.EJSON.deserialize(operations !== null && operations !== void 0 ? operations : []), parsedOptions),
110
112
  updateMany: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), bson_1.EJSON.deserialize(resolvedUpdate)),
111
113
  deleteMany: () => currentMethod(bson_1.EJSON.deserialize(resolvedQuery), parsedOptions)
112
114
  };
@@ -126,23 +126,23 @@ export declare const services: {
126
126
  invokedFrom?: string;
127
127
  };
128
128
  }) => {
129
- lambda: (region: string) => import("@aws-sdk/client-lambda/dist-types").Lambda & {
130
- Invoke: (params: import("@aws-sdk/client-lambda/dist-types").InvokeCommandInput) => Promise<Omit<import("@aws-sdk/client-lambda/dist-types").InvokeCommandOutput, "Payload"> & {
129
+ lambda: (region: string) => import("@aws-sdk/client-lambda").Lambda & {
130
+ Invoke: (params: import("@aws-sdk/client-lambda").InvokeCommandInput) => Promise<Omit<import("@aws-sdk/client-lambda").InvokeCommandOutput, "Payload"> & {
131
131
  Payload: {
132
132
  text: () => string | undefined;
133
133
  };
134
134
  }>;
135
- InvokeAsync: (params: import("@aws-sdk/client-lambda/dist-types").InvokeAsyncCommandInput) => Promise<import("@aws-sdk/client-lambda/dist-types").InvokeAsyncCommandOutput>;
135
+ InvokeAsync: (params: import("@aws-sdk/client-lambda").InvokeAsyncCommandInput) => Promise<import("@aws-sdk/client-lambda").InvokeAsyncCommandOutput>;
136
136
  };
137
- s3: (region: string) => import("@aws-sdk/client-s3/dist-types").S3Client & {
138
- PutObject: (params: import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) => Promise<import("@aws-sdk/client-s3/dist-types").PutObjectCommandOutput>;
139
- GetObject: (params: import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput) => Promise<import("@aws-sdk/client-s3/dist-types").GetObjectCommandOutput>;
140
- getSignedUrl: (operation: "getObject" | "putObject", params: (import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput | import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) & {
137
+ s3: (region: string) => import("@aws-sdk/client-s3").S3Client & {
138
+ PutObject: (params: import("@aws-sdk/client-s3").PutObjectCommandInput) => Promise<import("@aws-sdk/client-s3").PutObjectCommandOutput>;
139
+ GetObject: (params: import("@aws-sdk/client-s3").GetObjectCommandInput) => Promise<import("@aws-sdk/client-s3").GetObjectCommandOutput>;
140
+ getSignedUrl: (operation: "getObject" | "putObject", params: (import("@aws-sdk/client-s3").GetObjectCommandInput | import("@aws-sdk/client-s3").PutObjectCommandInput) & {
141
141
  Expires?: number;
142
142
  }, options?: {
143
143
  expiresIn?: number;
144
144
  }) => Promise<string>;
145
- PresignURL: (params: (import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput | import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) & {
145
+ PresignURL: (params: (import("@aws-sdk/client-s3").GetObjectCommandInput | import("@aws-sdk/client-s3").PutObjectCommandInput) & {
146
146
  Method?: string;
147
147
  ExpirationMS?: number;
148
148
  Expires?: number;
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,QAAQ,EAQT,MAAM,SAAS,CAAA;AAOhB,OAAO,EAGL,oBAAoB,EAErB,MAAM,SAAS,CAAA;AA8JhB,eAAO,MAAM,kBAAkB,GAAI,OAAO,OAAO,KAAG,OA0BnD,CAAA;AA8BD,eAAO,MAAM,2BAA2B,GAAI,UAAU,QAAQ,EAAE,YAK5D,CAAA;AA80CJ,QAAA,MAAM,YAAY,EAAE,oBAwBlB,CAAA;AAEF,eAAe,YAAY,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/index.ts"],"names":[],"mappings":"AAKA,OAAO,EAIL,QAAQ,EAQT,MAAM,SAAS,CAAA;AAOhB,OAAO,EAGL,oBAAoB,EAErB,MAAM,SAAS,CAAA;AA8JhB,eAAO,MAAM,kBAAkB,GAAI,OAAO,OAAO,KAAG,OA0BnD,CAAA;AA8BD,eAAO,MAAM,2BAA2B,GAAI,UAAU,QAAQ,EAAE,YAK5D,CAAA;AA+6CJ,QAAA,MAAM,YAAY,EAAE,oBAwBlB,CAAA;AAEF,eAAe,YAAY,CAAA"}
@@ -408,6 +408,22 @@ const areUpdatedFieldsAllowed = (filtered, updated, updatedPaths) => {
408
408
  return (0, isEqual_1.default)(filtered, updated);
409
409
  return updatedPaths.every((path) => (0, isEqual_1.default)((0, get_1.default)(filtered, path), (0, get_1.default)(updated, path)));
410
410
  };
411
+ const appendDistinctValue = (values, candidate) => {
412
+ if (typeof candidate === 'undefined')
413
+ return;
414
+ if (!values.some((entry) => (0, isEqual_1.default)(entry, candidate))) {
415
+ values.push(candidate);
416
+ }
417
+ };
418
+ const collectDistinctValues = (documents, key) => documents.reduce((values, document) => {
419
+ const currentValue = (0, get_1.default)(document, key);
420
+ if (Array.isArray(currentValue)) {
421
+ currentValue.forEach((entry) => appendDistinctValue(values, entry));
422
+ return values;
423
+ }
424
+ appendDistinctValue(values, currentValue);
425
+ return values;
426
+ }, []);
411
427
  const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, monitoringOrigin }) => {
412
428
  var _a, _b;
413
429
  const collection = mongo.client.db(dbName).collection(collName);
@@ -437,7 +453,39 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
437
453
  origin: monitoringOrigin
438
454
  });
439
455
  };
440
- return {
456
+ const validateReadableDocument = (currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
457
+ var _a;
458
+ const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
459
+ logDebug('find winningRole', {
460
+ collection: collName,
461
+ userId: getUserId(user),
462
+ winningRoleName: (_a = winningRole === null || winningRole === void 0 ? void 0 : winningRole.name) !== null && _a !== void 0 ? _a : null,
463
+ rolesLength: roles.length
464
+ });
465
+ const { status, document } = winningRole
466
+ ? yield (0, machines_1.checkValidation)(winningRole, {
467
+ type: 'read',
468
+ roles,
469
+ cursor: currentDoc,
470
+ expansions: getValidationExpansions(currentDoc)
471
+ }, user)
472
+ : fallbackAccess(currentDoc);
473
+ return status ? document : undefined;
474
+ });
475
+ const getScopedQuery = (query = {}) => {
476
+ const formattedQuery = (0, utils_3.getFormattedQuery)(filters, query, user);
477
+ const currentQuery = formattedQuery.length ? { $and: formattedQuery } : {};
478
+ const safeQuery = Array.isArray(formattedQuery)
479
+ ? (0, utils_3.normalizeQuery)(formattedQuery)
480
+ : formattedQuery;
481
+ return {
482
+ formattedQuery,
483
+ currentQuery,
484
+ safeQuery,
485
+ builtQuery: buildAndQuery(safeQuery)
486
+ };
487
+ };
488
+ const operators = {
441
489
  /**
442
490
  * Finds a single document in a MongoDB collection with optional role-based filtering and validation.
443
491
  *
@@ -852,25 +900,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
852
900
  */
853
901
  cursor.toArray = () => __awaiter(void 0, void 0, void 0, function* () {
854
902
  const response = yield originalToArray();
855
- const filteredResponse = yield Promise.all(response.map((currentDoc) => __awaiter(void 0, void 0, void 0, function* () {
856
- var _a;
857
- const winningRole = yield (0, utils_2.getWinningRoleAsync)(currentDoc, user, roles);
858
- logDebug('find winningRole', {
859
- collection: collName,
860
- userId: getUserId(user),
861
- winningRoleName: (_a = winningRole === null || winningRole === void 0 ? void 0 : winningRole.name) !== null && _a !== void 0 ? _a : null,
862
- rolesLength: roles.length
863
- });
864
- const { status, document } = winningRole
865
- ? yield (0, machines_1.checkValidation)(winningRole, {
866
- type: 'read',
867
- roles,
868
- cursor: currentDoc,
869
- expansions: getValidationExpansions(currentDoc)
870
- }, user)
871
- : fallbackAccess(currentDoc);
872
- return status ? document : undefined;
873
- })));
903
+ const filteredResponse = yield Promise.all(response.map((currentDoc) => validateReadableDocument(currentDoc)));
874
904
  return filteredResponse.filter(Boolean);
875
905
  });
876
906
  emitMongoEvent('find');
@@ -928,6 +958,32 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
928
958
  throw error;
929
959
  }
930
960
  }),
961
+ distinct: (key_1, ...args_1) => __awaiter(void 0, [key_1, ...args_1], void 0, function* (key, query = {}, options) {
962
+ try {
963
+ if (!key) {
964
+ throw new Error('distinct key is required');
965
+ }
966
+ if (!run_as_system) {
967
+ (0, utils_3.checkDenyOperation)(normalizedRules, collection.collectionName, model_1.CRUD_OPERATIONS.READ);
968
+ const { currentQuery } = getScopedQuery(query);
969
+ const projectedOptions = Object.assign(Object.assign({}, (options !== null && options !== void 0 ? options : {})), { projection: { _id: 1, [key]: 1 } });
970
+ const documents = yield collection.find(currentQuery, projectedOptions).toArray();
971
+ const readableDocuments = (yield Promise.all(documents.map((currentDoc) => validateReadableDocument(currentDoc)))).filter(Boolean);
972
+ const result = collectDistinctValues(readableDocuments, key);
973
+ emitMongoEvent('distinct');
974
+ return result;
975
+ }
976
+ const result = typeof options === 'undefined'
977
+ ? yield collection.distinct(key, query)
978
+ : yield collection.distinct(key, query, options);
979
+ emitMongoEvent('distinct');
980
+ return result;
981
+ }
982
+ catch (error) {
983
+ emitMongoEvent('distinct', undefined, error);
984
+ throw error;
985
+ }
986
+ }),
931
987
  /**
932
988
  * Watches changes on a MongoDB collection with optional role-based filtering of change events.
933
989
  *
@@ -1129,6 +1185,20 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
1129
1185
  throw error;
1130
1186
  }
1131
1187
  }),
1188
+ bulkWrite: (operations, options) => __awaiter(void 0, void 0, void 0, function* () {
1189
+ try {
1190
+ if (!run_as_system) {
1191
+ throw new Error('bulkWrite is available only when run_as_system is enabled');
1192
+ }
1193
+ const result = yield collection.bulkWrite(operations, options);
1194
+ emitMongoEvent('bulkWrite', { operations: operations.length });
1195
+ return result;
1196
+ }
1197
+ catch (error) {
1198
+ emitMongoEvent('bulkWrite', { operations: Array.isArray(operations) ? operations.length : 0 }, error);
1199
+ throw error;
1200
+ }
1201
+ }),
1132
1202
  updateMany: (query, data, options) => __awaiter(void 0, void 0, void 0, function* () {
1133
1203
  try {
1134
1204
  const normalizedData = normalizeUpdatePayload(data);
@@ -1239,6 +1309,7 @@ const getOperators = (mongo, { rules, dbName, collName, user, run_as_system, mon
1239
1309
  }
1240
1310
  })
1241
1311
  };
1312
+ return operators;
1242
1313
  };
1243
1314
  const MongodbAtlas = (app, { rules, user, run_as_system, monitoring } = {}) => ({
1244
1315
  startSession: (options) => {
@@ -40,6 +40,8 @@ export type GetOperatorsFunction = (mongoInstance: FastifyInstance["mongo"], { r
40
40
  countDocuments: (...params: Parameters<Method<'countDocuments'>>) => ReturnType<Method<'countDocuments'>>;
41
41
  watch: (...params: Parameters<Method<'watch'>>) => ReturnType<Method<'watch'>>;
42
42
  aggregate: (...params: [...Parameters<Method<'aggregate'>>, isClient: boolean]) => ReturnType<Method<'aggregate'>>;
43
+ distinct: (key: Parameters<Method<'distinct'>>[0], filter?: Parameters<Method<'distinct'>>[1], options?: Parameters<Method<'distinct'>>[2]) => ReturnType<Method<'distinct'>>;
44
+ bulkWrite: (...params: Parameters<Method<'bulkWrite'>>) => ReturnType<Method<'bulkWrite'>>;
43
45
  insertMany: (...params: Parameters<Method<'insertMany'>>) => ReturnType<Method<'insertMany'>>;
44
46
  updateMany: (...params: Parameters<Method<'updateMany'>>) => ReturnType<Method<'updateMany'>>;
45
47
  deleteMany: (...params: Parameters<Method<'deleteMany'>>) => ReturnType<Method<'deleteMany'>>;
@@ -1 +1 @@
1
- {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/model.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"model.d.ts","sourceRoot":"","sources":["../../../src/services/mongodb-atlas/model.ts"],"names":[],"mappings":"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"}
@@ -176,23 +176,23 @@ export declare const generateContextData: ({ user, services, app, rules, current
176
176
  context: object;
177
177
  }>;
178
178
  } | {
179
- lambda: (region: string) => import("@aws-sdk/client-lambda/dist-types").Lambda & {
180
- Invoke: (params: import("@aws-sdk/client-lambda/dist-types").InvokeCommandInput) => Promise<Omit<import("@aws-sdk/client-lambda/dist-types").InvokeCommandOutput, "Payload"> & {
179
+ lambda: (region: string) => import("@aws-sdk/client-lambda").Lambda & {
180
+ Invoke: (params: import("@aws-sdk/client-lambda").InvokeCommandInput) => Promise<Omit<import("@aws-sdk/client-lambda").InvokeCommandOutput, "Payload"> & {
181
181
  Payload: {
182
182
  text: () => string | undefined;
183
183
  };
184
184
  }>;
185
- InvokeAsync: (params: import("@aws-sdk/client-lambda/dist-types").InvokeAsyncCommandInput) => Promise<import("@aws-sdk/client-lambda/dist-types").InvokeAsyncCommandOutput>;
185
+ InvokeAsync: (params: import("@aws-sdk/client-lambda").InvokeAsyncCommandInput) => Promise<import("@aws-sdk/client-lambda").InvokeAsyncCommandOutput>;
186
186
  };
187
- s3: (region: string) => import("@aws-sdk/client-s3/dist-types").S3Client & {
188
- PutObject: (params: import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) => Promise<import("@aws-sdk/client-s3/dist-types").PutObjectCommandOutput>;
189
- GetObject: (params: import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput) => Promise<import("@aws-sdk/client-s3/dist-types").GetObjectCommandOutput>;
190
- getSignedUrl: (operation: "getObject" | "putObject", params: (import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput | import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) & {
187
+ s3: (region: string) => import("@aws-sdk/client-s3").S3Client & {
188
+ PutObject: (params: import("@aws-sdk/client-s3").PutObjectCommandInput) => Promise<import("@aws-sdk/client-s3").PutObjectCommandOutput>;
189
+ GetObject: (params: import("@aws-sdk/client-s3").GetObjectCommandInput) => Promise<import("@aws-sdk/client-s3").GetObjectCommandOutput>;
190
+ getSignedUrl: (operation: "getObject" | "putObject", params: (import("@aws-sdk/client-s3").GetObjectCommandInput | import("@aws-sdk/client-s3").PutObjectCommandInput) & {
191
191
  Expires?: number;
192
192
  }, options?: {
193
193
  expiresIn?: number;
194
194
  }) => Promise<string>;
195
- PresignURL: (params: (import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput | import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) & {
195
+ PresignURL: (params: (import("@aws-sdk/client-s3").GetObjectCommandInput | import("@aws-sdk/client-s3").PutObjectCommandInput) & {
196
196
  Method?: string;
197
197
  ExpirationMS?: number;
198
198
  Expires?: number;
@@ -328,23 +328,23 @@ export declare const generateContextData: ({ user, services, app, rules, current
328
328
  context: object;
329
329
  }>;
330
330
  } | {
331
- lambda: (region: string) => import("@aws-sdk/client-lambda/dist-types").Lambda & {
332
- Invoke: (params: import("@aws-sdk/client-lambda/dist-types").InvokeCommandInput) => Promise<Omit<import("@aws-sdk/client-lambda/dist-types").InvokeCommandOutput, "Payload"> & {
331
+ lambda: (region: string) => import("@aws-sdk/client-lambda").Lambda & {
332
+ Invoke: (params: import("@aws-sdk/client-lambda").InvokeCommandInput) => Promise<Omit<import("@aws-sdk/client-lambda").InvokeCommandOutput, "Payload"> & {
333
333
  Payload: {
334
334
  text: () => string | undefined;
335
335
  };
336
336
  }>;
337
- InvokeAsync: (params: import("@aws-sdk/client-lambda/dist-types").InvokeAsyncCommandInput) => Promise<import("@aws-sdk/client-lambda/dist-types").InvokeAsyncCommandOutput>;
337
+ InvokeAsync: (params: import("@aws-sdk/client-lambda").InvokeAsyncCommandInput) => Promise<import("@aws-sdk/client-lambda").InvokeAsyncCommandOutput>;
338
338
  };
339
- s3: (region: string) => import("@aws-sdk/client-s3/dist-types").S3Client & {
340
- PutObject: (params: import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) => Promise<import("@aws-sdk/client-s3/dist-types").PutObjectCommandOutput>;
341
- GetObject: (params: import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput) => Promise<import("@aws-sdk/client-s3/dist-types").GetObjectCommandOutput>;
342
- getSignedUrl: (operation: "getObject" | "putObject", params: (import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput | import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) & {
339
+ s3: (region: string) => import("@aws-sdk/client-s3").S3Client & {
340
+ PutObject: (params: import("@aws-sdk/client-s3").PutObjectCommandInput) => Promise<import("@aws-sdk/client-s3").PutObjectCommandOutput>;
341
+ GetObject: (params: import("@aws-sdk/client-s3").GetObjectCommandInput) => Promise<import("@aws-sdk/client-s3").GetObjectCommandOutput>;
342
+ getSignedUrl: (operation: "getObject" | "putObject", params: (import("@aws-sdk/client-s3").GetObjectCommandInput | import("@aws-sdk/client-s3").PutObjectCommandInput) & {
343
343
  Expires?: number;
344
344
  }, options?: {
345
345
  expiresIn?: number;
346
346
  }) => Promise<string>;
347
- PresignURL: (params: (import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput | import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) & {
347
+ PresignURL: (params: (import("@aws-sdk/client-s3").GetObjectCommandInput | import("@aws-sdk/client-s3").PutObjectCommandInput) & {
348
348
  Method?: string;
349
349
  ExpirationMS?: number;
350
350
  Expires?: number;
@@ -479,23 +479,23 @@ export declare const generateContextData: ({ user, services, app, rules, current
479
479
  context: object;
480
480
  }>;
481
481
  } | {
482
- lambda: (region: string) => import("@aws-sdk/client-lambda/dist-types").Lambda & {
483
- Invoke: (params: import("@aws-sdk/client-lambda/dist-types").InvokeCommandInput) => Promise<Omit<import("@aws-sdk/client-lambda/dist-types").InvokeCommandOutput, "Payload"> & {
482
+ lambda: (region: string) => import("@aws-sdk/client-lambda").Lambda & {
483
+ Invoke: (params: import("@aws-sdk/client-lambda").InvokeCommandInput) => Promise<Omit<import("@aws-sdk/client-lambda").InvokeCommandOutput, "Payload"> & {
484
484
  Payload: {
485
485
  text: () => string | undefined;
486
486
  };
487
487
  }>;
488
- InvokeAsync: (params: import("@aws-sdk/client-lambda/dist-types").InvokeAsyncCommandInput) => Promise<import("@aws-sdk/client-lambda/dist-types").InvokeAsyncCommandOutput>;
488
+ InvokeAsync: (params: import("@aws-sdk/client-lambda").InvokeAsyncCommandInput) => Promise<import("@aws-sdk/client-lambda").InvokeAsyncCommandOutput>;
489
489
  };
490
- s3: (region: string) => import("@aws-sdk/client-s3/dist-types").S3Client & {
491
- PutObject: (params: import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) => Promise<import("@aws-sdk/client-s3/dist-types").PutObjectCommandOutput>;
492
- GetObject: (params: import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput) => Promise<import("@aws-sdk/client-s3/dist-types").GetObjectCommandOutput>;
493
- getSignedUrl: (operation: "getObject" | "putObject", params: (import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput | import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) & {
490
+ s3: (region: string) => import("@aws-sdk/client-s3").S3Client & {
491
+ PutObject: (params: import("@aws-sdk/client-s3").PutObjectCommandInput) => Promise<import("@aws-sdk/client-s3").PutObjectCommandOutput>;
492
+ GetObject: (params: import("@aws-sdk/client-s3").GetObjectCommandInput) => Promise<import("@aws-sdk/client-s3").GetObjectCommandOutput>;
493
+ getSignedUrl: (operation: "getObject" | "putObject", params: (import("@aws-sdk/client-s3").GetObjectCommandInput | import("@aws-sdk/client-s3").PutObjectCommandInput) & {
494
494
  Expires?: number;
495
495
  }, options?: {
496
496
  expiresIn?: number;
497
497
  }) => Promise<string>;
498
- PresignURL: (params: (import("@aws-sdk/client-s3/dist-types").GetObjectCommandInput | import("@aws-sdk/client-s3/dist-types").PutObjectCommandInput) & {
498
+ PresignURL: (params: (import("@aws-sdk/client-s3").GetObjectCommandInput | import("@aws-sdk/client-s3").PutObjectCommandInput) & {
499
499
  Method?: string;
500
500
  ExpirationMS?: number;
501
501
  Expires?: number;
@@ -1,4 +1,4 @@
1
1
  import { MachineContext } from '../../interface';
2
2
  export declare const checkAdditionalFieldsFn: ({ role }: MachineContext) => boolean;
3
- export declare const checkIsValidFieldNameFn: (context: MachineContext) => Promise<import("bson/bson").Document>;
3
+ export declare const checkIsValidFieldNameFn: (context: MachineContext) => Promise<import("bson").Document>;
4
4
  //# sourceMappingURL=validators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/read/D/validators.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,eAAO,MAAM,uBAAuB,GAAI,UAAU,cAAc,YAE/D,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAU,SAAS,cAAc,0CAOpE,CAAA"}
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/read/D/validators.ts"],"names":[],"mappings":"AAKA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,eAAO,MAAM,uBAAuB,GAAI,UAAU,cAAc,YAE/D,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAU,SAAS,cAAc,qCAOpE,CAAA"}
@@ -1,4 +1,4 @@
1
1
  import { MachineContext } from '../../interface';
2
2
  export declare const checkAdditionalFieldsFn: ({ role }: MachineContext) => boolean;
3
- export declare const checkIsValidFieldNameFn: (context: MachineContext) => Promise<import("bson/bson").Document>;
3
+ export declare const checkIsValidFieldNameFn: (context: MachineContext) => Promise<import("bson").Document>;
4
4
  //# sourceMappingURL=validators.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/write/C/validators.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,eAAO,MAAM,uBAAuB,GAAI,UAAU,cAAc,YAE/D,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAU,SAAS,cAAc,0CAIpE,CAAA"}
1
+ {"version":3,"file":"validators.d.ts","sourceRoot":"","sources":["../../../../../../src/utils/roles/machines/write/C/validators.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAA;AAEhD,eAAO,MAAM,uBAAuB,GAAI,UAAU,cAAc,YAE/D,CAAA;AAED,eAAO,MAAM,uBAAuB,GAAU,SAAS,cAAc,qCAIpE,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flowerforce/flowerbase",
3
- "version": "1.8.4-beta.4",
3
+ "version": "1.8.4-beta.5",
4
4
  "description": "",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -1,4 +1,5 @@
1
1
  import Fastify, { FastifyInstance, FastifyReply, FastifyRequest } from 'fastify'
2
+ import { services } from '../../../services'
2
3
  import { GenerateContext } from '../../../utils/context'
3
4
  import { functionsController } from '../controller'
4
5
 
@@ -8,18 +9,19 @@ jest.mock('../../../utils/context', () => ({
8
9
 
9
10
  describe('functionsController', () => {
10
11
  let app: FastifyInstance
12
+ const originalMongoService = services['mongodb-atlas']
11
13
 
12
14
  beforeEach(async () => {
13
15
  app = Fastify()
14
16
 
15
17
  app.decorate('jwtAuthentication', async (request: FastifyRequest, _reply: FastifyReply) => {
16
- ;(request as any).user = {
18
+ ; (request as any).user = {
17
19
  id: '507f191e810c19729de860ea',
18
20
  typ: 'access'
19
21
  }
20
22
  })
21
23
 
22
- ;(GenerateContext as jest.Mock).mockResolvedValue({ ok: true })
24
+ ; (GenerateContext as jest.Mock).mockResolvedValue({ ok: true })
23
25
 
24
26
  await app.register(functionsController, {
25
27
  functionsList: {
@@ -33,6 +35,7 @@ describe('functionsController', () => {
33
35
  })
34
36
 
35
37
  afterEach(async () => {
38
+ services['mongodb-atlas'] = originalMongoService
36
39
  await app.close()
37
40
  jest.clearAllMocks()
38
41
  })
@@ -57,4 +60,41 @@ describe('functionsController', () => {
57
60
  })
58
61
  )
59
62
  })
63
+
64
+ it('passes mongodb-atlas distinct service arguments through POST /call', async () => {
65
+ const distinct = jest.fn().mockResolvedValue(['open'])
66
+ services['mongodb-atlas'] = jest.fn(() => ({
67
+ db: jest.fn().mockReturnValue({
68
+ collection: jest.fn().mockReturnValue({
69
+ distinct
70
+ })
71
+ })
72
+ })) as any
73
+
74
+ const response = await app.inject({
75
+ method: 'POST',
76
+ url: '/call',
77
+ payload: {
78
+ service: 'mongodb-atlas',
79
+ name: 'distinct',
80
+ arguments: [
81
+ {
82
+ database: 'app',
83
+ collection: 'todos',
84
+ key: 'status',
85
+ query: { archived: false },
86
+ options: { maxTimeMS: 250 }
87
+ }
88
+ ]
89
+ }
90
+ })
91
+
92
+ expect(response.statusCode).toBe(200)
93
+ expect(JSON.parse(response.body)).toEqual(['open'])
94
+ expect(distinct).toHaveBeenCalledWith(
95
+ 'status',
96
+ { archived: false },
97
+ { maxTimeMS: 250 }
98
+ )
99
+ })
60
100
  })
@@ -30,4 +30,48 @@ describe('executeQuery', () => {
30
30
  { upsert: true }
31
31
  )
32
32
  })
33
+
34
+ it('passes distinct arguments through to the service method', async () => {
35
+ const currentMethod = jest.fn().mockResolvedValue(['open'])
36
+
37
+ const operators = await executeQuery({
38
+ currentMethod,
39
+ key: 'status',
40
+ query: { archived: false },
41
+ update: {},
42
+ options: { maxTimeMS: 500 }
43
+ } as any)
44
+
45
+ await operators.distinct()
46
+
47
+ expect(currentMethod).toHaveBeenCalledWith(
48
+ 'status',
49
+ { archived: false },
50
+ { maxTimeMS: 500 }
51
+ )
52
+ })
53
+
54
+ it('passes bulkWrite operations through to the service method', async () => {
55
+ const currentMethod = jest.fn().mockResolvedValue({ acknowledged: true })
56
+ const operations = [
57
+ {
58
+ updateOne: {
59
+ filter: { archived: false },
60
+ update: { $set: { archived: true } }
61
+ }
62
+ }
63
+ ]
64
+
65
+ const operators = await executeQuery({
66
+ currentMethod,
67
+ query: {},
68
+ update: {},
69
+ operations,
70
+ options: { ordered: false }
71
+ } as any)
72
+
73
+ await operators.bulkWrite()
74
+
75
+ expect(currentMethod).toHaveBeenCalledWith(operations, { ordered: false })
76
+ })
33
77
  })
@@ -9,6 +9,29 @@ import { Base64Function, FunctionCallBase64Dto, FunctionCallDto } from './dtos'
9
9
  import { FunctionController } from './interface'
10
10
  import { executeQuery } from './utils'
11
11
 
12
+ const SUPPORTED_QUERY_METHODS = [
13
+ 'find',
14
+ 'findOne',
15
+ 'count',
16
+ 'countDocuments',
17
+ 'distinct',
18
+ 'deleteOne',
19
+ 'insertOne',
20
+ 'updateOne',
21
+ 'findOneAndUpdate',
22
+ 'aggregate',
23
+ 'insertMany',
24
+ 'bulkWrite',
25
+ 'updateMany',
26
+ 'deleteMany'
27
+ ] as const
28
+
29
+ type SupportedQueryMethod = (typeof SUPPORTED_QUERY_METHODS)[number]
30
+
31
+ const isSupportedQueryMethod = (value: unknown): value is SupportedQueryMethod =>
32
+ typeof value === 'string' &&
33
+ (SUPPORTED_QUERY_METHODS as readonly string[]).includes(value)
34
+
12
35
  const normalizeUser = (payload: Record<string, any> | undefined) => {
13
36
  if (!payload) return undefined
14
37
  const nestedUser =
@@ -352,6 +375,7 @@ export const functionsController: FunctionController = async (
352
375
  const [{
353
376
  database,
354
377
  collection,
378
+ key,
355
379
  query,
356
380
  filter,
357
381
  update,
@@ -360,9 +384,14 @@ export const functionsController: FunctionController = async (
360
384
  returnNewDocument,
361
385
  document,
362
386
  documents,
387
+ operations,
363
388
  pipeline = []
364
389
  }] = args
365
390
 
391
+ if (!isSupportedQueryMethod(method)) {
392
+ throw new Error(`Unsupported service method "${String(method)}"`)
393
+ }
394
+
366
395
  const currentMethod = serviceFn(app, { rules, user })
367
396
  .db(database)
368
397
  .collection(collection)[method]
@@ -371,6 +400,7 @@ export const functionsController: FunctionController = async (
371
400
  const operatorsByType = await executeQuery({
372
401
  currentMethod,
373
402
  query,
403
+ key,
374
404
  filter,
375
405
  update,
376
406
  projection,
@@ -378,10 +408,15 @@ export const functionsController: FunctionController = async (
378
408
  returnNewDocument,
379
409
  document,
380
410
  documents,
411
+ operations,
381
412
  pipeline,
382
413
  isClient: true
383
414
  })
384
- const serviceResult = await operatorsByType[method as keyof typeof operatorsByType]()
415
+ const operator = operatorsByType[method]
416
+ if (typeof operator !== 'function') {
417
+ throw new Error(`Unsupported service method "${method}"`)
418
+ }
419
+ const serviceResult = await operator()
385
420
  res.type('application/json')
386
421
  return serializeEjson(serviceResult)
387
422
  }
@@ -23,6 +23,7 @@ export type FunctionCallBase64Dto = {
23
23
  type ArgumentsData = Arguments<{
24
24
  database: string
25
25
  collection: string
26
+ key?: string
26
27
  filter?: Document
27
28
  query: Parameters<GetOperatorsFunction>
28
29
  update: Document
@@ -31,6 +32,7 @@ type ArgumentsData = Arguments<{
31
32
  returnNewDocument?: boolean
32
33
  document: Document
33
34
  documents: Document[]
35
+ operations?: Document[]
34
36
  pipeline?: Document[]
35
37
  }>
36
38
 
@@ -27,12 +27,14 @@ export type ExecuteQueryParams = {
27
27
  currentMethod: ReturnType<GetOperatorsFunction>[keyof ReturnType<GetOperatorsFunction>]
28
28
  query: Parameters<GetOperatorsFunction>
29
29
  update: Document
30
+ key?: string
30
31
  filter?: Document
31
32
  projection?: Document
32
33
  options?: Document
33
34
  returnNewDocument?: boolean
34
35
  document: Document
35
36
  documents: Document[]
37
+ operations?: Document[]
36
38
  pipeline: Document[]
37
39
  isClient?: boolean
38
40
  }
@@ -45,12 +45,14 @@ export const executeQuery = async ({
45
45
  currentMethod,
46
46
  query,
47
47
  update,
48
+ key,
48
49
  filter,
49
50
  projection,
50
51
  options,
51
52
  returnNewDocument,
52
53
  document,
53
54
  documents,
55
+ operations,
54
56
  pipeline,
55
57
  isClient = false
56
58
  }: ExecuteQueryParams) => {
@@ -113,6 +115,12 @@ export const executeQuery = async ({
113
115
  EJSON.deserialize(resolvedQuery),
114
116
  parsedOptions
115
117
  ),
118
+ distinct: () =>
119
+ (currentMethod as ReturnType<GetOperatorsFunction>['distinct'])(
120
+ key ?? '',
121
+ EJSON.deserialize(resolvedQuery),
122
+ parsedOptions
123
+ ),
116
124
  deleteOne: () =>
117
125
  (currentMethod as ReturnType<GetOperatorsFunction>['deleteOne'])(
118
126
  EJSON.deserialize(resolvedQuery),
@@ -144,6 +152,11 @@ export const executeQuery = async ({
144
152
  (currentMethod as ReturnType<GetOperatorsFunction>['insertMany'])(
145
153
  EJSON.deserialize(documents)
146
154
  ),
155
+ bulkWrite: () =>
156
+ (currentMethod as ReturnType<GetOperatorsFunction>['bulkWrite'])(
157
+ EJSON.deserialize(operations ?? []),
158
+ parsedOptions
159
+ ),
147
160
  updateMany: () =>
148
161
  (currentMethod as ReturnType<GetOperatorsFunction>['updateMany'])(
149
162
  EJSON.deserialize(resolvedQuery),
@@ -1,3 +1,5 @@
1
+ /// <reference types="jest" />
2
+
1
3
  import { ObjectId } from 'mongodb'
2
4
  import MongoDbAtlas from '..'
3
5
  import { Rules } from '../../../features/rules/interface'
@@ -178,6 +180,70 @@ describe('mongodb-atlas Realm compatibility', () => {
178
180
  )
179
181
  })
180
182
 
183
+ it('forwards bulkWrite to the underlying collection in system mode', async () => {
184
+ const operations = [
185
+ {
186
+ updateOne: {
187
+ filter: { done: false },
188
+ update: { $set: { done: true } }
189
+ }
190
+ }
191
+ ]
192
+ const bulkWriteResult = {
193
+ acknowledged: true,
194
+ matchedCount: 1,
195
+ modifiedCount: 1,
196
+ insertedCount: 0,
197
+ deletedCount: 0,
198
+ upsertedCount: 0,
199
+ insertedIds: {},
200
+ upsertedIds: {}
201
+ }
202
+ const bulkWrite = jest.fn().mockResolvedValue(bulkWriteResult)
203
+ const collection = {
204
+ collectionName: 'todos',
205
+ bulkWrite
206
+ }
207
+
208
+ const operators = MongoDbAtlas(createAppWithCollection(collection) as any, {
209
+ run_as_system: true
210
+ }).db('db').collection('todos')
211
+
212
+ const result = await operators.bulkWrite(operations as any, { ordered: false } as any)
213
+
214
+ expect(result).toEqual(bulkWriteResult)
215
+ expect(bulkWrite).toHaveBeenCalledWith(operations, { ordered: false })
216
+ })
217
+
218
+ it('rejects bulkWrite outside run_as_system mode', async () => {
219
+ const bulkWrite = jest.fn()
220
+ const collection = {
221
+ collectionName: 'todos',
222
+ bulkWrite
223
+ }
224
+
225
+ const operators = MongoDbAtlas(createAppWithCollection(collection) as any, {
226
+ rules: createRules(),
227
+ user: { id: 'user-1' }
228
+ }).db('db').collection('todos')
229
+
230
+ await expect(
231
+ operators.bulkWrite(
232
+ [
233
+ {
234
+ updateOne: {
235
+ filter: { done: false },
236
+ update: { $set: { done: true } }
237
+ }
238
+ }
239
+ ] as any,
240
+ { ordered: false } as any
241
+ )
242
+ ).rejects.toThrow('bulkWrite is available only when run_as_system is enabled')
243
+
244
+ expect(bulkWrite).not.toHaveBeenCalled()
245
+ })
246
+
181
247
  it('supports operator updates in updateMany without using invalid aggregate stages', async () => {
182
248
  const id = new ObjectId()
183
249
  const find = jest.fn().mockReturnValue({
@@ -513,6 +579,56 @@ describe('mongodb-atlas Realm compatibility', () => {
513
579
  expect(result.insertedIds).toEqual([id0, id1])
514
580
  })
515
581
 
582
+ it('computes distinct values from readable documents only', async () => {
583
+ const find = jest.fn().mockReturnValue({
584
+ toArray: jest.fn().mockResolvedValue([
585
+ { _id: new ObjectId(), visible: 'A', secret: 'internal-1' },
586
+ { _id: new ObjectId(), visible: 'A', secret: 'internal-2' },
587
+ { _id: new ObjectId(), visible: 'B', secret: 'internal-1' }
588
+ ])
589
+ })
590
+ const collection = {
591
+ collectionName: 'todos',
592
+ find
593
+ }
594
+ const operators = MongoDbAtlas(createAppWithCollection(collection) as any, {
595
+ rules: createRules({
596
+ roles: [
597
+ {
598
+ name: 'reader',
599
+ apply_when: {},
600
+ insert: true,
601
+ delete: true,
602
+ search: true,
603
+ read: true,
604
+ write: true,
605
+ fields: {
606
+ visible: { read: true },
607
+ secret: { read: false, write: false }
608
+ }
609
+ } as any
610
+ ]
611
+ }),
612
+ user: { id: 'user-1' }
613
+ }).db('db').collection('todos')
614
+
615
+ const visibleResult = await operators.distinct('visible', { archived: false })
616
+ const secretResult = await operators.distinct('secret', { archived: false })
617
+
618
+ expect(visibleResult).toEqual(['A', 'B'])
619
+ expect(secretResult).toEqual([])
620
+ expect(find).toHaveBeenNthCalledWith(
621
+ 1,
622
+ { $and: [{ archived: false }] },
623
+ { projection: { _id: 1, visible: 1 } }
624
+ )
625
+ expect(find).toHaveBeenNthCalledWith(
626
+ 2,
627
+ { $and: [{ archived: false }] },
628
+ { projection: { _id: 1, secret: 1 } }
629
+ )
630
+ })
631
+
516
632
  it('exposes startSession and delegates to the underlying MongoClient', async () => {
517
633
  const mockSession = { withTransaction: jest.fn() }
518
634
  const startSession = jest.fn().mockReturnValue(mockSession)
@@ -492,6 +492,26 @@ const areUpdatedFieldsAllowed = (
492
492
  return updatedPaths.every((path) => isEqual(get(filtered, path), get(updated, path)))
493
493
  }
494
494
 
495
+ const appendDistinctValue = (values: unknown[], candidate: unknown) => {
496
+ if (typeof candidate === 'undefined') return
497
+ if (!values.some((entry) => isEqual(entry, candidate))) {
498
+ values.push(candidate)
499
+ }
500
+ }
501
+
502
+ const collectDistinctValues = (documents: Document[], key: string) =>
503
+ documents.reduce<unknown[]>((values, document) => {
504
+ const currentValue = get(document, key)
505
+
506
+ if (Array.isArray(currentValue)) {
507
+ currentValue.forEach((entry) => appendDistinctValue(values, entry))
508
+ return values
509
+ }
510
+
511
+ appendDistinctValue(values, currentValue)
512
+ return values
513
+ }, [])
514
+
495
515
  const getOperators: GetOperatorsFunction = (
496
516
  mongo,
497
517
  { rules, dbName, collName, user, run_as_system, monitoringOrigin }
@@ -535,7 +555,48 @@ const getOperators: GetOperatorsFunction = (
535
555
  })
536
556
  }
537
557
 
538
- return {
558
+ const validateReadableDocument = async (currentDoc: Document) => {
559
+ const winningRole = await getWinningRoleAsync(currentDoc, user, roles)
560
+
561
+ logDebug('find winningRole', {
562
+ collection: collName,
563
+ userId: getUserId(user),
564
+ winningRoleName: winningRole?.name ?? null,
565
+ rolesLength: roles.length
566
+ })
567
+
568
+ const { status, document } = winningRole
569
+ ? await checkValidation(
570
+ winningRole,
571
+ {
572
+ type: 'read',
573
+ roles,
574
+ cursor: currentDoc,
575
+ expansions: getValidationExpansions(currentDoc)
576
+ },
577
+ user
578
+ )
579
+ : fallbackAccess(currentDoc)
580
+
581
+ return status ? document : undefined
582
+ }
583
+
584
+ const getScopedQuery = (query: MongoFilter<Document> = {}) => {
585
+ const formattedQuery = getFormattedQuery(filters, query, user)
586
+ const currentQuery = formattedQuery.length ? { $and: formattedQuery } : {}
587
+ const safeQuery = Array.isArray(formattedQuery)
588
+ ? normalizeQuery(formattedQuery)
589
+ : formattedQuery
590
+
591
+ return {
592
+ formattedQuery,
593
+ currentQuery,
594
+ safeQuery,
595
+ builtQuery: buildAndQuery(safeQuery)
596
+ }
597
+ }
598
+
599
+ const operators: ReturnType<GetOperatorsFunction> = {
539
600
  /**
540
601
  * Finds a single document in a MongoDB collection with optional role-based filtering and validation.
541
602
  *
@@ -1068,30 +1129,7 @@ const getOperators: GetOperatorsFunction = (
1068
1129
  const response = await originalToArray()
1069
1130
 
1070
1131
  const filteredResponse = await Promise.all(
1071
- response.map(async (currentDoc) => {
1072
- const winningRole = await getWinningRoleAsync(currentDoc, user, roles)
1073
-
1074
- logDebug('find winningRole', {
1075
- collection: collName,
1076
- userId: getUserId(user),
1077
- winningRoleName: winningRole?.name ?? null,
1078
- rolesLength: roles.length
1079
- })
1080
- const { status, document } = winningRole
1081
- ? await checkValidation(
1082
- winningRole,
1083
- {
1084
- type: 'read',
1085
- roles,
1086
- cursor: currentDoc,
1087
- expansions: getValidationExpansions(currentDoc)
1088
- },
1089
- user
1090
- )
1091
- : fallbackAccess(currentDoc)
1092
-
1093
- return status ? document : undefined
1094
- })
1132
+ response.map((currentDoc) => validateReadableDocument(currentDoc))
1095
1133
  )
1096
1134
 
1097
1135
  return filteredResponse.filter(Boolean) as WithId<Document>[]
@@ -1164,6 +1202,45 @@ const getOperators: GetOperatorsFunction = (
1164
1202
  throw error
1165
1203
  }
1166
1204
  },
1205
+ distinct: async (key, query = {}, options) => {
1206
+ try {
1207
+ if (!key) {
1208
+ throw new Error('distinct key is required')
1209
+ }
1210
+
1211
+ if (!run_as_system) {
1212
+ checkDenyOperation(
1213
+ normalizedRules,
1214
+ collection.collectionName,
1215
+ CRUD_OPERATIONS.READ
1216
+ )
1217
+
1218
+ const { currentQuery } = getScopedQuery(query)
1219
+ const projectedOptions = {
1220
+ ...(options ?? {}),
1221
+ projection: { _id: 1, [key]: 1 }
1222
+ } as FindOptions
1223
+ const documents = await collection.find(currentQuery, projectedOptions).toArray()
1224
+ const readableDocuments = (
1225
+ await Promise.all(documents.map((currentDoc) => validateReadableDocument(currentDoc)))
1226
+ ).filter(Boolean) as Document[]
1227
+ const result = collectDistinctValues(readableDocuments, key)
1228
+
1229
+ emitMongoEvent('distinct')
1230
+ return result
1231
+ }
1232
+
1233
+ const result =
1234
+ typeof options === 'undefined'
1235
+ ? await collection.distinct(key, query)
1236
+ : await collection.distinct(key, query, options)
1237
+ emitMongoEvent('distinct')
1238
+ return result
1239
+ } catch (error) {
1240
+ emitMongoEvent('distinct', undefined, error)
1241
+ throw error
1242
+ }
1243
+ },
1167
1244
  /**
1168
1245
  * Watches changes on a MongoDB collection with optional role-based filtering of change events.
1169
1246
  *
@@ -1450,6 +1527,24 @@ const getOperators: GetOperatorsFunction = (
1450
1527
  throw error
1451
1528
  }
1452
1529
  },
1530
+ bulkWrite: async (operations, options) => {
1531
+ try {
1532
+ if (!run_as_system) {
1533
+ throw new Error('bulkWrite is available only when run_as_system is enabled')
1534
+ }
1535
+
1536
+ const result = await collection.bulkWrite(operations, options)
1537
+ emitMongoEvent('bulkWrite', { operations: operations.length })
1538
+ return result
1539
+ } catch (error) {
1540
+ emitMongoEvent(
1541
+ 'bulkWrite',
1542
+ { operations: Array.isArray(operations) ? operations.length : 0 },
1543
+ error
1544
+ )
1545
+ throw error
1546
+ }
1547
+ },
1453
1548
  updateMany: async (query, data, options) => {
1454
1549
  try {
1455
1550
  const normalizedData = normalizeUpdatePayload(data as Document)
@@ -1602,6 +1697,8 @@ const getOperators: GetOperatorsFunction = (
1602
1697
  }
1603
1698
  }
1604
1699
  }
1700
+
1701
+ return operators
1605
1702
  }
1606
1703
 
1607
1704
  const MongodbAtlas: MongodbAtlasFunction = (
@@ -93,6 +93,14 @@ export type GetOperatorsFunction = (
93
93
  aggregate: (
94
94
  ...params: [...Parameters<Method<'aggregate'>>, isClient: boolean]
95
95
  ) => ReturnType<Method<'aggregate'>>
96
+ distinct: (
97
+ key: Parameters<Method<'distinct'>>[0],
98
+ filter?: Parameters<Method<'distinct'>>[1],
99
+ options?: Parameters<Method<'distinct'>>[2]
100
+ ) => ReturnType<Method<'distinct'>>
101
+ bulkWrite: (
102
+ ...params: Parameters<Method<'bulkWrite'>>
103
+ ) => ReturnType<Method<'bulkWrite'>>
96
104
  insertMany: (
97
105
  ...params: Parameters<Method<'insertMany'>>
98
106
  ) => ReturnType<Method<'insertMany'>>
package/tsconfig.json CHANGED
@@ -8,13 +8,8 @@
8
8
  "declarationMap": true,
9
9
  "noImplicitAny": true,
10
10
  "strict": true,
11
- "moduleResolution": "node",
12
11
  "esModuleInterop": true,
13
12
  "skipLibCheck": true,
14
- "baseUrl": ".",
15
- "paths": {
16
- "*": ["../../node_modules/*"]
17
- },
18
13
  "lib": ["ES2021", "DOM"]
19
14
  },
20
15
  "include": ["src/**/*"],
@@ -3,5 +3,6 @@
3
3
  "compilerOptions": {
4
4
  "types": ["node", "jest"]
5
5
  },
6
- "include": ["src/**/*.test.ts", "src/**/*.spec.ts"]
6
+ "include": ["src/**/*.test.ts", "src/**/*.spec.ts"],
7
+ "exclude": ["node_modules", "dist"]
7
8
  }