@opra/mongodb 0.33.13 → 1.0.0-alpha.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,19 +1,20 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.MongoService = void 0;
4
+ const mongodb_1 = require("mongodb");
4
5
  const common_1 = require("@opra/common");
5
6
  const core_1 = require("@opra/core");
6
7
  /**
7
8
  * Class representing a MongoDB service for interacting with a collection.
8
- * @extends ApiService
9
+ * @extends ServiceBase
9
10
  * @template T - The type of the documents in the collection.
10
11
  */
11
- class MongoService extends core_1.ApiService {
12
+ class MongoService extends core_1.ServiceBase {
12
13
  /**
13
14
  * Constructs a new instance
14
15
  *
15
- * @param dataType - The data type of the array elements.
16
- * @param [options] - The options for the array service.
16
+ * @param dataType - The data type of the returning results
17
+ * @param [options] - The options for the service
17
18
  * @constructor
18
19
  */
19
20
  constructor(dataType, options) {
@@ -21,26 +22,56 @@ class MongoService extends core_1.ApiService {
21
22
  this._encoders = {};
22
23
  this._dataType = dataType;
23
24
  this.db = options?.db;
24
- this.collectionName = options?.collectionName;
25
- if (!this.collectionName) {
25
+ this.$documentFilter = this.$documentFilter || options?.documentFilter;
26
+ this.$interceptor = this.$interceptor || options?.interceptor;
27
+ this.$collectionName = options?.collectionName;
28
+ if (!this.$collectionName) {
26
29
  if (typeof dataType === 'string')
27
- this.collectionName = dataType;
30
+ this.$collectionName = dataType;
28
31
  if (typeof dataType === 'function') {
29
32
  const metadata = Reflect.getMetadata(common_1.DATATYPE_METADATA, dataType);
30
33
  if (metadata)
31
- this.collectionName = metadata.name;
34
+ this.$collectionName = metadata.name;
32
35
  }
33
36
  }
34
- this.resourceName = options?.resourceName;
37
+ this.$resourceName = options?.resourceName;
35
38
  this.$idGenerator = options?.idGenerator;
36
39
  }
40
+ /**
41
+ * Retrieves the collection name.
42
+ *
43
+ * @protected
44
+ * @returns The collection name.
45
+ * @throws {Error} If the collection name is not defined.
46
+ */
47
+ getCollectionName() {
48
+ const out = typeof this.$collectionName === 'function' ? this.$collectionName(this) : this.$collectionName;
49
+ if (out)
50
+ return out;
51
+ throw new Error('collectionName is not defined');
52
+ }
53
+ /**
54
+ * Retrieves the resource name.
55
+ *
56
+ * @protected
57
+ * @returns {string} The resource name.
58
+ * @throws {Error} If the collection name is not defined.
59
+ */
60
+ getResourceName() {
61
+ const out = typeof this.$resourceName === 'function'
62
+ ? this.$resourceName(this)
63
+ : this.$resourceName || this.getCollectionName();
64
+ if (out)
65
+ return out;
66
+ throw new Error('resourceName is not defined');
67
+ }
37
68
  /**
38
69
  * Retrieves the data type of the document
39
70
  *
40
71
  * @throws {NotAcceptableError} If the data type is not a ComplexType.
41
72
  */
42
73
  getDataType() {
43
- return this.context.api.getComplexType(this._dataType);
74
+ return this.context.document.node.getComplexType(this._dataType);
44
75
  }
45
76
  /**
46
77
  * Retrieves the encoder for the specified operation.
@@ -51,7 +82,11 @@ class MongoService extends core_1.ApiService {
51
82
  let encoder = this._encoders[operation];
52
83
  if (encoder)
53
84
  return encoder;
54
- encoder = this._generateEncoder(operation);
85
+ const options = { projection: '*' };
86
+ if (operation === 'update')
87
+ options.partial = true;
88
+ const dataType = this.getDataType();
89
+ encoder = dataType.generateCodec('encode', options);
55
90
  this._encoders[operation] = encoder;
56
91
  return encoder;
57
92
  }
@@ -62,7 +97,9 @@ class MongoService extends core_1.ApiService {
62
97
  let decoder = this._decoder;
63
98
  if (decoder)
64
99
  return decoder;
65
- decoder = this._generateDecoder();
100
+ const options = { projection: '*', partial: true };
101
+ const dataType = this.getDataType();
102
+ decoder = dataType.generateCodec('decode', options);
66
103
  this._decoder = decoder;
67
104
  return decoder;
68
105
  }
@@ -107,30 +144,6 @@ class MongoService extends core_1.ApiService {
107
144
  await session.endSession();
108
145
  }
109
146
  }
110
- /**
111
- * Inserts a single document into MongoDB. If documents passed in do not contain the **_id** field,
112
- * one will be added to each of the documents missing it by the driver, mutating the document. This behavior
113
- * can be overridden by setting the **forceServerObjectId** flag.
114
- *
115
- * @param doc - The document to insert
116
- * @param options - Optional settings for the command
117
- * @protected
118
- */
119
- async __insertOne(doc, options) {
120
- const db = this.getDatabase();
121
- const collection = await this.getCollection(db);
122
- options = {
123
- ...options,
124
- session: options?.session || this.getSession()
125
- };
126
- try {
127
- return await collection.insertOne(doc, options);
128
- }
129
- catch (e) {
130
- await this.$onError?.(e, this);
131
- throw e;
132
- }
133
- }
134
147
  /**
135
148
  * Gets the number of documents matching the filter.
136
149
  *
@@ -138,188 +151,156 @@ class MongoService extends core_1.ApiService {
138
151
  * @param options - The options for counting documents.
139
152
  * @protected
140
153
  */
141
- async __countDocuments(filter, options) {
154
+ async _dbCountDocuments(filter, options) {
142
155
  const db = this.getDatabase();
143
156
  const collection = await this.getCollection(db);
144
157
  options = {
145
158
  ...options,
146
159
  limit: undefined,
147
- session: options?.session || this.getSession()
160
+ session: options?.session || this.getSession(),
148
161
  };
149
- try {
150
- return await collection.countDocuments(filter || {}, options) || 0;
151
- }
152
- catch (e) {
153
- await this.$onError?.(e, this);
154
- throw e;
155
- }
162
+ return (await collection.countDocuments(filter || {}, options)) || 0;
156
163
  }
157
164
  /**
158
- * Delete a document from a collection
165
+ * Acquires a connection and performs Collection.deleteOne operation
159
166
  *
160
167
  * @param filter - The filter used to select the document to remove
161
168
  * @param options - Optional settings for the command
162
169
  * @protected
163
170
  */
164
- async __deleteOne(filter, options) {
171
+ async _dbDeleteOne(filter, options) {
165
172
  const db = this.getDatabase();
166
173
  const collection = await this.getCollection(db);
167
174
  options = {
168
175
  ...options,
169
- session: options?.session || this.getSession()
176
+ session: options?.session || this.getSession(),
170
177
  };
171
- try {
172
- return await collection.deleteOne(filter || {}, options);
173
- }
174
- catch (e) {
175
- await this.$onError?.(e, this);
176
- throw e;
177
- }
178
+ return await collection.deleteOne(filter || {}, options);
178
179
  }
179
180
  /**
180
- * Delete multiple documents from a collection
181
+ * Acquires a connection and performs Collection.deleteMany operation
181
182
  *
182
183
  * @param filter - The filter used to select the documents to remove
183
184
  * @param options - Optional settings for the command
184
185
  * @protected
185
186
  */
186
- async __deleteMany(filter, options) {
187
+ async _dbDeleteMany(filter, options) {
187
188
  const db = this.getDatabase();
188
189
  const collection = await this.getCollection(db);
189
190
  options = {
190
191
  ...options,
191
- session: options?.session || this.getSession()
192
+ session: options?.session || this.getSession(),
192
193
  };
193
- try {
194
- return await collection.deleteMany(filter || {}, options);
195
- }
196
- catch (e) {
197
- await this.$onError?.(e, this);
198
- throw e;
199
- }
194
+ return await collection.deleteMany(filter || {}, options);
200
195
  }
201
196
  /**
202
- * Gets the number of documents matching the filter.
197
+ * Acquires a connection and performs Collection.distinct operation
203
198
  *
204
199
  * @param field - Field of the document to find distinct values for
205
200
  * @param filter - The filter for filtering the set of documents to which we apply the distinct filter.
206
201
  * @param options - Optional settings for the command
207
202
  * @protected
208
203
  */
209
- async __distinct(field, filter, options) {
204
+ async _dbDistinct(field, filter, options) {
210
205
  const db = this.getDatabase();
211
206
  const collection = await this.getCollection(db);
212
207
  options = {
213
208
  ...options,
214
- session: options?.session || this.getSession()
209
+ session: options?.session || this.getSession(),
215
210
  };
216
- try {
217
- return await collection.distinct(field, filter || {}, options);
218
- }
219
- catch (e) {
220
- await this.$onError?.(e, this);
221
- throw e;
222
- }
211
+ return await collection.distinct(field, filter || {}, options);
223
212
  }
224
213
  /**
225
- * Execute an aggregation framework pipeline against the collection, needs MongoDB \>= 2.2
214
+ * Acquires a connection and performs Collection.aggregate operation
226
215
  *
227
216
  * @param pipeline - An array of aggregation pipelines to execute
228
217
  * @param options - Optional settings for the command
229
218
  * @protected
230
219
  */
231
- async __aggregate(pipeline, options) {
220
+ async _dbAggregate(pipeline, options) {
232
221
  const db = this.getDatabase();
233
222
  const collection = await this.getCollection(db);
234
223
  options = {
235
224
  ...options,
236
- session: options?.session || this.getSession()
225
+ session: options?.session || this.getSession(),
237
226
  };
238
- try {
239
- return await collection.aggregate(pipeline, options);
240
- }
241
- catch (e) {
242
- await this.$onError?.(e, this);
243
- throw e;
244
- }
227
+ return await collection.aggregate(pipeline, options);
245
228
  }
246
229
  /**
247
- * Fetches the first document that matches the filter
230
+ * Acquires a connection and performs Collection.findOne operation
248
231
  *
249
232
  * @param filter - Query for find Operation
250
233
  * @param options - Optional settings for the command
251
234
  * @protected
252
235
  */
253
- async __findOne(filter, options) {
236
+ async _dbFindOne(filter, options) {
254
237
  const db = this.getDatabase();
255
238
  const collection = await this.getCollection(db);
256
239
  options = {
257
240
  ...options,
258
- session: options?.session || this.getSession()
241
+ session: options?.session || this.getSession(),
259
242
  };
260
- try {
261
- return await collection.findOne(filter || {}, options);
262
- }
263
- catch (e) {
264
- await this.$onError?.(e, this);
265
- throw e;
266
- }
243
+ return (await collection.findOne(filter || {}, options));
267
244
  }
268
245
  /**
269
- * Creates a cursor for a filter that can be used to iterate over results from MongoDB
246
+ * Acquires a connection and performs Collection.find operation
270
247
  *
271
248
  * @param filter - The filter predicate. If unspecified,
272
249
  * then all documents in the collection will match the predicate
273
250
  * @param options - Optional settings for the command
274
251
  * @protected
275
252
  */
276
- async __find(filter, options) {
253
+ async _dbFind(filter, options) {
277
254
  const db = this.getDatabase();
278
255
  const collection = await this.getCollection(db);
279
256
  options = {
280
257
  ...options,
281
- session: options?.session || this.getSession()
258
+ session: options?.session || this.getSession(),
282
259
  };
283
- try {
284
- return collection.find(filter || {}, options);
285
- }
286
- catch (e) {
287
- await this.$onError?.(e, this);
288
- throw e;
289
- }
260
+ return collection.find(filter || {}, options);
290
261
  }
291
262
  /**
292
- * Update a single document in a collection
263
+ * Acquires a connection and performs Collection.insertOne operation
264
+ *
265
+ * @param doc - The document to insert
266
+ * @param options - Optional settings for the command
267
+ * @protected
268
+ */
269
+ async _dbInsertOne(doc, options) {
270
+ const db = this.getDatabase();
271
+ const collection = await this.getCollection(db);
272
+ options = {
273
+ ...options,
274
+ session: options?.session || this.getSession(),
275
+ };
276
+ return await collection.insertOne(doc, options);
277
+ }
278
+ /**
279
+ * Acquires a connection and performs Collection.updateOne operation
293
280
  *
294
281
  * @param filter - The filter used to select the document to update
295
282
  * @param update - The update operations to be applied to the document
296
283
  * @param options - Optional settings for the command
297
284
  * @protected
298
285
  */
299
- async __updateOne(filter, update, options) {
286
+ async _dbUpdateOne(filter, update, options) {
300
287
  const db = this.getDatabase();
301
288
  const collection = await this.getCollection(db);
302
289
  options = {
303
290
  ...options,
304
291
  session: options?.session || this.getSession(),
305
292
  };
306
- try {
307
- return collection.updateOne(filter || {}, update, options);
308
- }
309
- catch (e) {
310
- await this.$onError?.(e, this);
311
- throw e;
312
- }
293
+ return collection.updateOne(filter || {}, update, options);
313
294
  }
314
295
  /**
315
- * Find a document and update it in one atomic operation. Requires a write lock for the duration of the operation.
296
+ * Acquires a connection and performs Collection.findOneAndUpdate operation
316
297
  *
317
298
  * @param filter - The filter used to select the document to update
318
299
  * @param update - Update operations to be performed on the document
319
300
  * @param options - Optional settings for the command
320
301
  * @protected
321
302
  */
322
- async __findOneAndUpdate(filter, update, options) {
303
+ async _dbFindOneAndUpdate(filter, update, options) {
323
304
  const db = this.getDatabase();
324
305
  const collection = await this.getCollection(db);
325
306
  const opts = {
@@ -328,37 +309,25 @@ class MongoService extends core_1.ApiService {
328
309
  ...options,
329
310
  session: options?.session || this.getSession(),
330
311
  };
331
- try {
332
- return await collection.findOneAndUpdate(filter || {}, update, opts);
333
- }
334
- catch (e) {
335
- await this.$onError?.(e, this);
336
- throw e;
337
- }
312
+ return await collection.findOneAndUpdate(filter || {}, update, opts);
338
313
  }
339
314
  /**
340
- * Update multiple documents in a collection
315
+ * Acquires a connection and performs Collection.updateMany operation
341
316
  *
342
317
  * @param filter - The filter used to select the documents to update
343
318
  * @param update - The update operations to be applied to the documents
344
319
  * @param options - Optional settings for the command
345
320
  * @protected
346
321
  */
347
- async __updateMany(filter, update, options) {
322
+ async _dbUpdateMany(filter, update, options) {
348
323
  const db = this.getDatabase();
349
324
  const collection = await this.getCollection(db);
350
325
  options = {
351
326
  ...options,
352
327
  session: options?.session || this.getSession(),
353
- upsert: false
328
+ upsert: false,
354
329
  };
355
- try {
356
- return await collection.updateMany(filter || {}, update, options);
357
- }
358
- catch (e) {
359
- await this.$onError?.(e, this);
360
- throw e;
361
- }
330
+ return await collection.updateMany(filter || {}, update, options);
362
331
  }
363
332
  /**
364
333
  * Retrieves the database connection.
@@ -368,9 +337,7 @@ class MongoService extends core_1.ApiService {
368
337
  * @throws {Error} If the context or database is not set.
369
338
  */
370
339
  getDatabase() {
371
- const db = typeof this.db === 'function'
372
- ? this.db(this)
373
- : this.db;
340
+ const db = typeof this.db === 'function' ? this.db(this) : this.db;
374
341
  if (!db)
375
342
  throw new Error(`Database not set!`);
376
343
  return db;
@@ -383,9 +350,7 @@ class MongoService extends core_1.ApiService {
383
350
  * @throws {Error} If the context or database is not set.
384
351
  */
385
352
  getSession() {
386
- return typeof this.session === 'function'
387
- ? this.session(this)
388
- : this.session;
353
+ return typeof this.session === 'function' ? this.session(this) : this.session;
389
354
  }
390
355
  /**
391
356
  * Retrieves a MongoDB collection from the given database.
@@ -397,60 +362,36 @@ class MongoService extends core_1.ApiService {
397
362
  return db.collection(this.getCollectionName());
398
363
  }
399
364
  /**
400
- * Retrieves the collection name.
365
+ * Generates an ID.
401
366
  *
402
367
  * @protected
403
- * @returns The collection name.
404
- * @throws {Error} If the collection name is not defined.
368
+ * @returns {MongoAdapter.AnyId} The generated ID.
405
369
  */
406
- getCollectionName() {
407
- const out = typeof this.collectionName === 'function'
408
- ? this.collectionName(this)
409
- : this.collectionName;
410
- if (out)
411
- return out;
412
- throw new Error('collectionName is not defined');
370
+ _generateId() {
371
+ return typeof this.$idGenerator === 'function' ? this.$idGenerator(this) : new mongodb_1.ObjectId();
413
372
  }
414
373
  /**
415
- * Retrieves the resource name.
374
+ * Retrieves the common filter used for querying documents.
375
+ * This method is mostly used for security issues like securing multi-tenant applications.
416
376
  *
417
377
  * @protected
418
- * @returns {string} The resource name.
419
- * @throws {Error} If the collection name is not defined.
378
+ * @returns {FilterInput | Promise<FilterInput> | undefined} The common filter or a Promise
379
+ * that resolves to the common filter, or undefined if not available.
420
380
  */
421
- getResourceName() {
422
- const out = typeof this.resourceName === 'function'
423
- ? this.resourceName(this)
424
- : this.resourceName || this.getCollectionName();
425
- if (out)
426
- return out;
427
- throw new Error('resourceName is not defined');
381
+ _getDocumentFilter(info) {
382
+ return typeof this.$documentFilter === 'function' ? this.$documentFilter(info, this) : this.$documentFilter;
428
383
  }
429
- /**
430
- * Generates an encoder for the specified operation.
431
- *
432
- * @param operation - The operation to generate the encoder for. Must be either 'create' or 'update'.
433
- * @protected
434
- * @returns - The generated encoder for the specified operation.
435
- */
436
- _generateEncoder(operation) {
437
- const dataType = this.getDataType();
438
- const options = {};
439
- if (operation === 'update') {
440
- options.omit = ['_id'];
441
- options.partial = true;
384
+ async _intercept(callback, info) {
385
+ try {
386
+ if (this.$interceptor)
387
+ return this.$interceptor(callback, info, this);
388
+ return callback();
389
+ }
390
+ catch (e) {
391
+ Error.captureStackTrace(e, this._intercept);
392
+ await this.$onError?.(e, this);
393
+ throw e;
442
394
  }
443
- return dataType.generateCodec('encode', options);
444
- }
445
- /**
446
- * Generates an encoder for the specified operation.
447
- *
448
- * @protected
449
- * @returns - The generated encoder for the specified operation.
450
- */
451
- _generateDecoder() {
452
- const dataType = this.getDataType();
453
- return dataType.generateCodec('decode', { partial: true });
454
395
  }
455
396
  }
456
397
  exports.MongoService = MongoService;