@opra/mongodb 1.0.0-alpha.3 → 1.0.0-alpha.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/mongo-entity-service.js +27 -24
- package/cjs/mongo-nested-service.js +22 -22
- package/cjs/mongo-service.js +29 -26
- package/esm/mongo-entity-service.js +27 -24
- package/esm/mongo-nested-service.js +22 -22
- package/esm/mongo-service.js +29 -26
- package/package.json +5 -5
- package/types/mongo-nested-service.d.ts +7 -7
- package/types/mongo-service.d.ts +10 -9
|
@@ -30,8 +30,8 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
30
30
|
* @protected
|
|
31
31
|
*/
|
|
32
32
|
async _create(input, options) {
|
|
33
|
-
const
|
|
34
|
-
const doc =
|
|
33
|
+
const inputCodec = this.getInputCodec('create');
|
|
34
|
+
const doc = inputCodec(input);
|
|
35
35
|
assert.ok(doc._id, 'You must provide the "_id" field');
|
|
36
36
|
const r = await this._dbInsertOne(doc, options);
|
|
37
37
|
if (r.insertedId) {
|
|
@@ -99,14 +99,15 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
99
99
|
const filter = mongo_adapter_js_1.MongoAdapter.prepareFilter([mongo_adapter_js_1.MongoAdapter.prepareKeyValues(id, ['_id']), options?.filter]);
|
|
100
100
|
const mongoOptions = {
|
|
101
101
|
...options,
|
|
102
|
-
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.
|
|
102
|
+
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.dataType, options?.projection),
|
|
103
103
|
limit: undefined,
|
|
104
104
|
skip: undefined,
|
|
105
105
|
sort: undefined,
|
|
106
106
|
};
|
|
107
|
-
const decode = this.getDecoder();
|
|
108
107
|
const out = await this._dbFindOne(filter, mongoOptions);
|
|
109
|
-
|
|
108
|
+
const outputCodec = this.getOutputCodec('find');
|
|
109
|
+
if (out)
|
|
110
|
+
return outputCodec(out);
|
|
110
111
|
}
|
|
111
112
|
/**
|
|
112
113
|
* Finds a document in the collection that matches the specified options.
|
|
@@ -119,12 +120,13 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
119
120
|
const mongoOptions = {
|
|
120
121
|
...(0, lodash_omit_1.default)(options, 'filter'),
|
|
121
122
|
sort: options?.sort ? mongo_adapter_js_1.MongoAdapter.prepareSort(options.sort) : undefined,
|
|
122
|
-
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.
|
|
123
|
+
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.dataType, options?.projection),
|
|
123
124
|
limit: undefined,
|
|
124
125
|
};
|
|
125
|
-
const decode = this.getDecoder();
|
|
126
126
|
const out = await this._dbFindOne(filter, mongoOptions);
|
|
127
|
-
|
|
127
|
+
const outputCodec = this.getOutputCodec('find');
|
|
128
|
+
if (out)
|
|
129
|
+
return outputCodec(out);
|
|
128
130
|
}
|
|
129
131
|
/**
|
|
130
132
|
* Finds multiple documents in the MongoDB collection.
|
|
@@ -151,16 +153,16 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
151
153
|
stages.push({ $sort: sort });
|
|
152
154
|
}
|
|
153
155
|
stages.push({ $limit: limit });
|
|
154
|
-
const dataType = this.
|
|
156
|
+
const dataType = this.dataType;
|
|
155
157
|
const projection = mongo_adapter_js_1.MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
156
158
|
if (projection)
|
|
157
159
|
stages.push({ $project: projection });
|
|
158
|
-
const decode = this.getDecoder();
|
|
159
160
|
const cursor = await this._dbAggregate(stages, mongoOptions);
|
|
160
161
|
/** Execute db command */
|
|
161
162
|
try {
|
|
162
163
|
/** Fetch the cursor and decode the result objects */
|
|
163
|
-
|
|
164
|
+
const outputCodec = this.getOutputCodec('find');
|
|
165
|
+
return (await cursor.toArray()).map((r) => outputCodec(r));
|
|
164
166
|
}
|
|
165
167
|
finally {
|
|
166
168
|
if (!cursor.closed)
|
|
@@ -205,11 +207,11 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
205
207
|
dataStages.push({ $sort: sort });
|
|
206
208
|
}
|
|
207
209
|
dataStages.push({ $limit: limit });
|
|
208
|
-
const dataType = this.
|
|
210
|
+
const dataType = this.dataType;
|
|
209
211
|
const projection = mongo_adapter_js_1.MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
210
212
|
if (projection)
|
|
211
213
|
dataStages.push({ $project: projection });
|
|
212
|
-
const
|
|
214
|
+
const outputCodec = this.getOutputCodec('find');
|
|
213
215
|
/** Execute db command */
|
|
214
216
|
const cursor = await this._dbAggregate(stages, mongoOptions);
|
|
215
217
|
try {
|
|
@@ -217,7 +219,7 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
217
219
|
const facetResult = await cursor.toArray();
|
|
218
220
|
return {
|
|
219
221
|
count: facetResult[0].count[0]?.totalMatches || 0,
|
|
220
|
-
items: facetResult[0].data?.map((r) =>
|
|
222
|
+
items: facetResult[0].data?.map((r) => outputCodec(r)),
|
|
221
223
|
};
|
|
222
224
|
}
|
|
223
225
|
finally {
|
|
@@ -241,8 +243,8 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
241
243
|
throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
|
|
242
244
|
let update;
|
|
243
245
|
if (isDocument) {
|
|
244
|
-
const
|
|
245
|
-
const doc =
|
|
246
|
+
const inputCodec = this.getInputCodec('update');
|
|
247
|
+
const doc = inputCodec(input);
|
|
246
248
|
delete doc._id;
|
|
247
249
|
update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc);
|
|
248
250
|
update.$set = update.$set || {};
|
|
@@ -254,11 +256,12 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
254
256
|
...options,
|
|
255
257
|
includeResultMetadata: false,
|
|
256
258
|
upsert: undefined,
|
|
257
|
-
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.
|
|
259
|
+
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.dataType, options?.projection),
|
|
258
260
|
};
|
|
259
|
-
const decode = this.getDecoder();
|
|
260
261
|
const out = await this._dbFindOneAndUpdate(filter, update, mongoOptions);
|
|
261
|
-
|
|
262
|
+
const outputCodec = this.getOutputCodec('update');
|
|
263
|
+
if (out)
|
|
264
|
+
return outputCodec(out);
|
|
262
265
|
}
|
|
263
266
|
/**
|
|
264
267
|
* Updates a document in the collection with the specified ID.
|
|
@@ -275,8 +278,8 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
275
278
|
throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
|
|
276
279
|
let update;
|
|
277
280
|
if (isDocument) {
|
|
278
|
-
const
|
|
279
|
-
const doc =
|
|
281
|
+
const inputCodec = this.getInputCodec('update');
|
|
282
|
+
const doc = inputCodec(input);
|
|
280
283
|
delete doc._id;
|
|
281
284
|
update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc);
|
|
282
285
|
if (!Object.keys(doc).length)
|
|
@@ -289,7 +292,7 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
289
292
|
...options,
|
|
290
293
|
includeResultMetadata: false,
|
|
291
294
|
upsert: undefined,
|
|
292
|
-
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.
|
|
295
|
+
projection: mongo_adapter_js_1.MongoAdapter.prepareProjection(this.dataType, options?.projection),
|
|
293
296
|
};
|
|
294
297
|
const out = await this._dbUpdateOne(filter, update, mongoOptions);
|
|
295
298
|
return out.matchedCount;
|
|
@@ -308,8 +311,8 @@ class MongoEntityService extends mongo_service_js_1.MongoService {
|
|
|
308
311
|
throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
|
|
309
312
|
let update;
|
|
310
313
|
if (isDocument) {
|
|
311
|
-
const
|
|
312
|
-
const doc =
|
|
314
|
+
const inputCodec = this.getInputCodec('update');
|
|
315
|
+
const doc = inputCodec(input);
|
|
313
316
|
delete doc._id;
|
|
314
317
|
update = mongo_adapter_js_1.MongoAdapter.preparePatch(doc);
|
|
315
318
|
if (!Object.keys(doc).length)
|
|
@@ -27,6 +27,18 @@ class MongoNestedService extends mongo_service_js_1.MongoService {
|
|
|
27
27
|
this.defaultLimit = options?.defaultLimit || 10;
|
|
28
28
|
this.$nestedFilter = options?.$nestedFilter;
|
|
29
29
|
}
|
|
30
|
+
/**
|
|
31
|
+
* Retrieves the data type of the array field
|
|
32
|
+
*
|
|
33
|
+
* @returns {ComplexType} The complex data type of the field.
|
|
34
|
+
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
35
|
+
*/
|
|
36
|
+
get dataType() {
|
|
37
|
+
const t = super.dataType.getField(this.fieldName).type;
|
|
38
|
+
if (!(t instanceof common_1.ComplexType))
|
|
39
|
+
throw new common_1.NotAcceptableError(`Data type "${t.name}" is not a ComplexType`);
|
|
40
|
+
return t;
|
|
41
|
+
}
|
|
30
42
|
/**
|
|
31
43
|
* Asserts whether a resource with the specified parentId and id exists.
|
|
32
44
|
* Throws a ResourceNotFoundError if the resource does not exist.
|
|
@@ -66,8 +78,8 @@ class MongoNestedService extends mongo_service_js_1.MongoService {
|
|
|
66
78
|
return this._intercept(() => this._create(documentId, input, options), info);
|
|
67
79
|
}
|
|
68
80
|
async _create(documentId, input, options) {
|
|
69
|
-
const
|
|
70
|
-
const doc =
|
|
81
|
+
const inputCodec = this.getInputCodec('create');
|
|
82
|
+
const doc = inputCodec(input);
|
|
71
83
|
doc._id = doc._id || this._generateId();
|
|
72
84
|
const docFilter = mongo_adapter_js_1.MongoAdapter.prepareKeyValues(documentId, ['_id']);
|
|
73
85
|
const r = await this._dbUpdateOne(docFilter, {
|
|
@@ -340,14 +352,14 @@ class MongoNestedService extends mongo_service_js_1.MongoService {
|
|
|
340
352
|
stages.push({ $sort: sort });
|
|
341
353
|
}
|
|
342
354
|
stages.push({ $limit: limit });
|
|
343
|
-
const dataType = this.
|
|
355
|
+
const dataType = this.dataType;
|
|
344
356
|
const projection = mongo_adapter_js_1.MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
345
357
|
if (projection)
|
|
346
358
|
stages.push({ $project: projection });
|
|
347
|
-
const decode = this.getDecoder();
|
|
348
359
|
const cursor = await this._dbAggregate(stages, mongoOptions);
|
|
349
360
|
try {
|
|
350
|
-
const
|
|
361
|
+
const outputCodec = this.getOutputCodec('find');
|
|
362
|
+
const out = await (await cursor.toArray()).map((r) => outputCodec(r));
|
|
351
363
|
return out;
|
|
352
364
|
}
|
|
353
365
|
finally {
|
|
@@ -414,19 +426,19 @@ class MongoNestedService extends mongo_service_js_1.MongoService {
|
|
|
414
426
|
dataStages.push({ $sort: sort });
|
|
415
427
|
}
|
|
416
428
|
dataStages.push({ $limit: limit });
|
|
417
|
-
const dataType = this.
|
|
429
|
+
const dataType = this.dataType;
|
|
418
430
|
const projection = mongo_adapter_js_1.MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
419
431
|
if (projection)
|
|
420
432
|
dataStages.push({ $project: projection });
|
|
421
|
-
const decode = this.getDecoder();
|
|
422
433
|
const cursor = await this._dbAggregate(stages, {
|
|
423
434
|
...mongoOptions,
|
|
424
435
|
});
|
|
425
436
|
try {
|
|
426
437
|
const facetResult = await cursor.toArray();
|
|
438
|
+
const outputCodec = this.getOutputCodec('find');
|
|
427
439
|
return {
|
|
428
440
|
count: facetResult[0].count[0].totalMatches || 0,
|
|
429
|
-
items: facetResult[0].data.map((r) =>
|
|
441
|
+
items: facetResult[0].data.map((r) => outputCodec(r)),
|
|
430
442
|
};
|
|
431
443
|
}
|
|
432
444
|
finally {
|
|
@@ -525,8 +537,8 @@ class MongoNestedService extends mongo_service_js_1.MongoService {
|
|
|
525
537
|
}, info);
|
|
526
538
|
}
|
|
527
539
|
async _updateMany(documentId, input, options) {
|
|
528
|
-
const
|
|
529
|
-
const doc =
|
|
540
|
+
const inputCodec = this.getInputCodec('update');
|
|
541
|
+
const doc = inputCodec(input);
|
|
530
542
|
if (!Object.keys(doc).length)
|
|
531
543
|
return 0;
|
|
532
544
|
const matchFilter = mongo_adapter_js_1.MongoAdapter.prepareFilter([
|
|
@@ -547,18 +559,6 @@ class MongoNestedService extends mongo_service_js_1.MongoService {
|
|
|
547
559
|
return await this._count(documentId, options);
|
|
548
560
|
return r.modifiedCount || 0;
|
|
549
561
|
}
|
|
550
|
-
/**
|
|
551
|
-
* Retrieves the data type of the array field
|
|
552
|
-
*
|
|
553
|
-
* @returns {ComplexType} The complex data type of the field.
|
|
554
|
-
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
555
|
-
*/
|
|
556
|
-
getDataType() {
|
|
557
|
-
const t = super.getDataType().getField(this.fieldName).type;
|
|
558
|
-
if (!(t instanceof common_1.ComplexType))
|
|
559
|
-
throw new common_1.NotAcceptableError(`Data type "${t.name}" is not a ComplexType`);
|
|
560
|
-
return t;
|
|
561
|
-
}
|
|
562
562
|
/**
|
|
563
563
|
* Retrieves the common filter used for querying array elements.
|
|
564
564
|
* This method is mostly used for security issues like securing multi-tenant applications.
|
package/cjs/mongo-service.js
CHANGED
|
@@ -19,8 +19,9 @@ class MongoService extends core_1.ServiceBase {
|
|
|
19
19
|
*/
|
|
20
20
|
constructor(dataType, options) {
|
|
21
21
|
super();
|
|
22
|
-
this.
|
|
23
|
-
this.
|
|
22
|
+
this._inputCodecs = {};
|
|
23
|
+
this._outputCodecs = {};
|
|
24
|
+
this._dataType_ = dataType;
|
|
24
25
|
this.db = options?.db;
|
|
25
26
|
this.$documentFilter = this.$documentFilter || options?.documentFilter;
|
|
26
27
|
this.$interceptor = this.$interceptor || options?.interceptor;
|
|
@@ -66,42 +67,44 @@ class MongoService extends core_1.ServiceBase {
|
|
|
66
67
|
throw new Error('resourceName is not defined');
|
|
67
68
|
}
|
|
68
69
|
/**
|
|
69
|
-
* Retrieves the data type
|
|
70
|
+
* Retrieves the OPRA data type
|
|
70
71
|
*
|
|
71
72
|
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
72
73
|
*/
|
|
73
|
-
|
|
74
|
-
|
|
74
|
+
get dataType() {
|
|
75
|
+
if (!this._dataType)
|
|
76
|
+
this._dataType = this.context.document.node.getComplexType(this._dataType_);
|
|
77
|
+
return this._dataType;
|
|
75
78
|
}
|
|
76
79
|
/**
|
|
77
|
-
* Retrieves the
|
|
80
|
+
* Retrieves the codec for the specified operation.
|
|
78
81
|
*
|
|
79
82
|
* @param operation - The operation to retrieve the encoder for. Valid values are 'create' and 'update'.
|
|
80
83
|
*/
|
|
81
|
-
|
|
82
|
-
let
|
|
83
|
-
if (
|
|
84
|
-
return
|
|
85
|
-
const options = { projection: '*' };
|
|
84
|
+
getInputCodec(operation) {
|
|
85
|
+
let validator = this._inputCodecs[operation];
|
|
86
|
+
if (validator)
|
|
87
|
+
return validator;
|
|
88
|
+
const options = { projection: '*', ignoreReadonlyFields: true };
|
|
86
89
|
if (operation === 'update')
|
|
87
|
-
options.partial =
|
|
88
|
-
const dataType = this.
|
|
89
|
-
|
|
90
|
-
this.
|
|
91
|
-
return
|
|
90
|
+
options.partial = 'deep';
|
|
91
|
+
const dataType = this.dataType;
|
|
92
|
+
validator = dataType.generateCodec('decode', options);
|
|
93
|
+
this._inputCodecs[operation] = validator;
|
|
94
|
+
return validator;
|
|
92
95
|
}
|
|
93
96
|
/**
|
|
94
|
-
* Retrieves the
|
|
97
|
+
* Retrieves the codec.
|
|
95
98
|
*/
|
|
96
|
-
|
|
97
|
-
let
|
|
98
|
-
if (
|
|
99
|
-
return
|
|
100
|
-
const options = { projection: '*', partial: true };
|
|
101
|
-
const dataType = this.
|
|
102
|
-
|
|
103
|
-
this.
|
|
104
|
-
return
|
|
99
|
+
getOutputCodec(operation) {
|
|
100
|
+
let validator = this._outputCodecs[operation];
|
|
101
|
+
if (validator)
|
|
102
|
+
return validator;
|
|
103
|
+
const options = { projection: '*', partial: 'deep', ignoreWriteonlyFields: true };
|
|
104
|
+
const dataType = this.dataType;
|
|
105
|
+
validator = dataType.generateCodec('decode', options);
|
|
106
|
+
this._outputCodecs[operation] = validator;
|
|
107
|
+
return validator;
|
|
105
108
|
}
|
|
106
109
|
/**
|
|
107
110
|
* Executes the provided function within a transaction.
|
|
@@ -26,8 +26,8 @@ export class MongoEntityService extends MongoService {
|
|
|
26
26
|
* @protected
|
|
27
27
|
*/
|
|
28
28
|
async _create(input, options) {
|
|
29
|
-
const
|
|
30
|
-
const doc =
|
|
29
|
+
const inputCodec = this.getInputCodec('create');
|
|
30
|
+
const doc = inputCodec(input);
|
|
31
31
|
assert.ok(doc._id, 'You must provide the "_id" field');
|
|
32
32
|
const r = await this._dbInsertOne(doc, options);
|
|
33
33
|
if (r.insertedId) {
|
|
@@ -95,14 +95,15 @@ export class MongoEntityService extends MongoService {
|
|
|
95
95
|
const filter = MongoAdapter.prepareFilter([MongoAdapter.prepareKeyValues(id, ['_id']), options?.filter]);
|
|
96
96
|
const mongoOptions = {
|
|
97
97
|
...options,
|
|
98
|
-
projection: MongoAdapter.prepareProjection(this.
|
|
98
|
+
projection: MongoAdapter.prepareProjection(this.dataType, options?.projection),
|
|
99
99
|
limit: undefined,
|
|
100
100
|
skip: undefined,
|
|
101
101
|
sort: undefined,
|
|
102
102
|
};
|
|
103
|
-
const decode = this.getDecoder();
|
|
104
103
|
const out = await this._dbFindOne(filter, mongoOptions);
|
|
105
|
-
|
|
104
|
+
const outputCodec = this.getOutputCodec('find');
|
|
105
|
+
if (out)
|
|
106
|
+
return outputCodec(out);
|
|
106
107
|
}
|
|
107
108
|
/**
|
|
108
109
|
* Finds a document in the collection that matches the specified options.
|
|
@@ -115,12 +116,13 @@ export class MongoEntityService extends MongoService {
|
|
|
115
116
|
const mongoOptions = {
|
|
116
117
|
...omit(options, 'filter'),
|
|
117
118
|
sort: options?.sort ? MongoAdapter.prepareSort(options.sort) : undefined,
|
|
118
|
-
projection: MongoAdapter.prepareProjection(this.
|
|
119
|
+
projection: MongoAdapter.prepareProjection(this.dataType, options?.projection),
|
|
119
120
|
limit: undefined,
|
|
120
121
|
};
|
|
121
|
-
const decode = this.getDecoder();
|
|
122
122
|
const out = await this._dbFindOne(filter, mongoOptions);
|
|
123
|
-
|
|
123
|
+
const outputCodec = this.getOutputCodec('find');
|
|
124
|
+
if (out)
|
|
125
|
+
return outputCodec(out);
|
|
124
126
|
}
|
|
125
127
|
/**
|
|
126
128
|
* Finds multiple documents in the MongoDB collection.
|
|
@@ -147,16 +149,16 @@ export class MongoEntityService extends MongoService {
|
|
|
147
149
|
stages.push({ $sort: sort });
|
|
148
150
|
}
|
|
149
151
|
stages.push({ $limit: limit });
|
|
150
|
-
const dataType = this.
|
|
152
|
+
const dataType = this.dataType;
|
|
151
153
|
const projection = MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
152
154
|
if (projection)
|
|
153
155
|
stages.push({ $project: projection });
|
|
154
|
-
const decode = this.getDecoder();
|
|
155
156
|
const cursor = await this._dbAggregate(stages, mongoOptions);
|
|
156
157
|
/** Execute db command */
|
|
157
158
|
try {
|
|
158
159
|
/** Fetch the cursor and decode the result objects */
|
|
159
|
-
|
|
160
|
+
const outputCodec = this.getOutputCodec('find');
|
|
161
|
+
return (await cursor.toArray()).map((r) => outputCodec(r));
|
|
160
162
|
}
|
|
161
163
|
finally {
|
|
162
164
|
if (!cursor.closed)
|
|
@@ -201,11 +203,11 @@ export class MongoEntityService extends MongoService {
|
|
|
201
203
|
dataStages.push({ $sort: sort });
|
|
202
204
|
}
|
|
203
205
|
dataStages.push({ $limit: limit });
|
|
204
|
-
const dataType = this.
|
|
206
|
+
const dataType = this.dataType;
|
|
205
207
|
const projection = MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
206
208
|
if (projection)
|
|
207
209
|
dataStages.push({ $project: projection });
|
|
208
|
-
const
|
|
210
|
+
const outputCodec = this.getOutputCodec('find');
|
|
209
211
|
/** Execute db command */
|
|
210
212
|
const cursor = await this._dbAggregate(stages, mongoOptions);
|
|
211
213
|
try {
|
|
@@ -213,7 +215,7 @@ export class MongoEntityService extends MongoService {
|
|
|
213
215
|
const facetResult = await cursor.toArray();
|
|
214
216
|
return {
|
|
215
217
|
count: facetResult[0].count[0]?.totalMatches || 0,
|
|
216
|
-
items: facetResult[0].data?.map((r) =>
|
|
218
|
+
items: facetResult[0].data?.map((r) => outputCodec(r)),
|
|
217
219
|
};
|
|
218
220
|
}
|
|
219
221
|
finally {
|
|
@@ -237,8 +239,8 @@ export class MongoEntityService extends MongoService {
|
|
|
237
239
|
throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
|
|
238
240
|
let update;
|
|
239
241
|
if (isDocument) {
|
|
240
|
-
const
|
|
241
|
-
const doc =
|
|
242
|
+
const inputCodec = this.getInputCodec('update');
|
|
243
|
+
const doc = inputCodec(input);
|
|
242
244
|
delete doc._id;
|
|
243
245
|
update = MongoAdapter.preparePatch(doc);
|
|
244
246
|
update.$set = update.$set || {};
|
|
@@ -250,11 +252,12 @@ export class MongoEntityService extends MongoService {
|
|
|
250
252
|
...options,
|
|
251
253
|
includeResultMetadata: false,
|
|
252
254
|
upsert: undefined,
|
|
253
|
-
projection: MongoAdapter.prepareProjection(this.
|
|
255
|
+
projection: MongoAdapter.prepareProjection(this.dataType, options?.projection),
|
|
254
256
|
};
|
|
255
|
-
const decode = this.getDecoder();
|
|
256
257
|
const out = await this._dbFindOneAndUpdate(filter, update, mongoOptions);
|
|
257
|
-
|
|
258
|
+
const outputCodec = this.getOutputCodec('update');
|
|
259
|
+
if (out)
|
|
260
|
+
return outputCodec(out);
|
|
258
261
|
}
|
|
259
262
|
/**
|
|
260
263
|
* Updates a document in the collection with the specified ID.
|
|
@@ -271,8 +274,8 @@ export class MongoEntityService extends MongoService {
|
|
|
271
274
|
throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
|
|
272
275
|
let update;
|
|
273
276
|
if (isDocument) {
|
|
274
|
-
const
|
|
275
|
-
const doc =
|
|
277
|
+
const inputCodec = this.getInputCodec('update');
|
|
278
|
+
const doc = inputCodec(input);
|
|
276
279
|
delete doc._id;
|
|
277
280
|
update = MongoAdapter.preparePatch(doc);
|
|
278
281
|
if (!Object.keys(doc).length)
|
|
@@ -285,7 +288,7 @@ export class MongoEntityService extends MongoService {
|
|
|
285
288
|
...options,
|
|
286
289
|
includeResultMetadata: false,
|
|
287
290
|
upsert: undefined,
|
|
288
|
-
projection: MongoAdapter.prepareProjection(this.
|
|
291
|
+
projection: MongoAdapter.prepareProjection(this.dataType, options?.projection),
|
|
289
292
|
};
|
|
290
293
|
const out = await this._dbUpdateOne(filter, update, mongoOptions);
|
|
291
294
|
return out.matchedCount;
|
|
@@ -304,8 +307,8 @@ export class MongoEntityService extends MongoService {
|
|
|
304
307
|
throw new TypeError('You must pass one of MongoDB UpdateFilter or a partial document, not both');
|
|
305
308
|
let update;
|
|
306
309
|
if (isDocument) {
|
|
307
|
-
const
|
|
308
|
-
const doc =
|
|
310
|
+
const inputCodec = this.getInputCodec('update');
|
|
311
|
+
const doc = inputCodec(input);
|
|
309
312
|
delete doc._id;
|
|
310
313
|
update = MongoAdapter.preparePatch(doc);
|
|
311
314
|
if (!Object.keys(doc).length)
|
|
@@ -23,6 +23,18 @@ export class MongoNestedService extends MongoService {
|
|
|
23
23
|
this.defaultLimit = options?.defaultLimit || 10;
|
|
24
24
|
this.$nestedFilter = options?.$nestedFilter;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Retrieves the data type of the array field
|
|
28
|
+
*
|
|
29
|
+
* @returns {ComplexType} The complex data type of the field.
|
|
30
|
+
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
31
|
+
*/
|
|
32
|
+
get dataType() {
|
|
33
|
+
const t = super.dataType.getField(this.fieldName).type;
|
|
34
|
+
if (!(t instanceof ComplexType))
|
|
35
|
+
throw new NotAcceptableError(`Data type "${t.name}" is not a ComplexType`);
|
|
36
|
+
return t;
|
|
37
|
+
}
|
|
26
38
|
/**
|
|
27
39
|
* Asserts whether a resource with the specified parentId and id exists.
|
|
28
40
|
* Throws a ResourceNotFoundError if the resource does not exist.
|
|
@@ -62,8 +74,8 @@ export class MongoNestedService extends MongoService {
|
|
|
62
74
|
return this._intercept(() => this._create(documentId, input, options), info);
|
|
63
75
|
}
|
|
64
76
|
async _create(documentId, input, options) {
|
|
65
|
-
const
|
|
66
|
-
const doc =
|
|
77
|
+
const inputCodec = this.getInputCodec('create');
|
|
78
|
+
const doc = inputCodec(input);
|
|
67
79
|
doc._id = doc._id || this._generateId();
|
|
68
80
|
const docFilter = MongoAdapter.prepareKeyValues(documentId, ['_id']);
|
|
69
81
|
const r = await this._dbUpdateOne(docFilter, {
|
|
@@ -336,14 +348,14 @@ export class MongoNestedService extends MongoService {
|
|
|
336
348
|
stages.push({ $sort: sort });
|
|
337
349
|
}
|
|
338
350
|
stages.push({ $limit: limit });
|
|
339
|
-
const dataType = this.
|
|
351
|
+
const dataType = this.dataType;
|
|
340
352
|
const projection = MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
341
353
|
if (projection)
|
|
342
354
|
stages.push({ $project: projection });
|
|
343
|
-
const decode = this.getDecoder();
|
|
344
355
|
const cursor = await this._dbAggregate(stages, mongoOptions);
|
|
345
356
|
try {
|
|
346
|
-
const
|
|
357
|
+
const outputCodec = this.getOutputCodec('find');
|
|
358
|
+
const out = await (await cursor.toArray()).map((r) => outputCodec(r));
|
|
347
359
|
return out;
|
|
348
360
|
}
|
|
349
361
|
finally {
|
|
@@ -410,19 +422,19 @@ export class MongoNestedService extends MongoService {
|
|
|
410
422
|
dataStages.push({ $sort: sort });
|
|
411
423
|
}
|
|
412
424
|
dataStages.push({ $limit: limit });
|
|
413
|
-
const dataType = this.
|
|
425
|
+
const dataType = this.dataType;
|
|
414
426
|
const projection = MongoAdapter.prepareProjection(dataType, options?.projection);
|
|
415
427
|
if (projection)
|
|
416
428
|
dataStages.push({ $project: projection });
|
|
417
|
-
const decode = this.getDecoder();
|
|
418
429
|
const cursor = await this._dbAggregate(stages, {
|
|
419
430
|
...mongoOptions,
|
|
420
431
|
});
|
|
421
432
|
try {
|
|
422
433
|
const facetResult = await cursor.toArray();
|
|
434
|
+
const outputCodec = this.getOutputCodec('find');
|
|
423
435
|
return {
|
|
424
436
|
count: facetResult[0].count[0].totalMatches || 0,
|
|
425
|
-
items: facetResult[0].data.map((r) =>
|
|
437
|
+
items: facetResult[0].data.map((r) => outputCodec(r)),
|
|
426
438
|
};
|
|
427
439
|
}
|
|
428
440
|
finally {
|
|
@@ -521,8 +533,8 @@ export class MongoNestedService extends MongoService {
|
|
|
521
533
|
}, info);
|
|
522
534
|
}
|
|
523
535
|
async _updateMany(documentId, input, options) {
|
|
524
|
-
const
|
|
525
|
-
const doc =
|
|
536
|
+
const inputCodec = this.getInputCodec('update');
|
|
537
|
+
const doc = inputCodec(input);
|
|
526
538
|
if (!Object.keys(doc).length)
|
|
527
539
|
return 0;
|
|
528
540
|
const matchFilter = MongoAdapter.prepareFilter([
|
|
@@ -543,18 +555,6 @@ export class MongoNestedService extends MongoService {
|
|
|
543
555
|
return await this._count(documentId, options);
|
|
544
556
|
return r.modifiedCount || 0;
|
|
545
557
|
}
|
|
546
|
-
/**
|
|
547
|
-
* Retrieves the data type of the array field
|
|
548
|
-
*
|
|
549
|
-
* @returns {ComplexType} The complex data type of the field.
|
|
550
|
-
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
551
|
-
*/
|
|
552
|
-
getDataType() {
|
|
553
|
-
const t = super.getDataType().getField(this.fieldName).type;
|
|
554
|
-
if (!(t instanceof ComplexType))
|
|
555
|
-
throw new NotAcceptableError(`Data type "${t.name}" is not a ComplexType`);
|
|
556
|
-
return t;
|
|
557
|
-
}
|
|
558
558
|
/**
|
|
559
559
|
* Retrieves the common filter used for querying array elements.
|
|
560
560
|
* This method is mostly used for security issues like securing multi-tenant applications.
|
package/esm/mongo-service.js
CHANGED
|
@@ -16,8 +16,9 @@ export class MongoService extends ServiceBase {
|
|
|
16
16
|
*/
|
|
17
17
|
constructor(dataType, options) {
|
|
18
18
|
super();
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
19
|
+
this._inputCodecs = {};
|
|
20
|
+
this._outputCodecs = {};
|
|
21
|
+
this._dataType_ = dataType;
|
|
21
22
|
this.db = options?.db;
|
|
22
23
|
this.$documentFilter = this.$documentFilter || options?.documentFilter;
|
|
23
24
|
this.$interceptor = this.$interceptor || options?.interceptor;
|
|
@@ -63,42 +64,44 @@ export class MongoService extends ServiceBase {
|
|
|
63
64
|
throw new Error('resourceName is not defined');
|
|
64
65
|
}
|
|
65
66
|
/**
|
|
66
|
-
* Retrieves the data type
|
|
67
|
+
* Retrieves the OPRA data type
|
|
67
68
|
*
|
|
68
69
|
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
69
70
|
*/
|
|
70
|
-
|
|
71
|
-
|
|
71
|
+
get dataType() {
|
|
72
|
+
if (!this._dataType)
|
|
73
|
+
this._dataType = this.context.document.node.getComplexType(this._dataType_);
|
|
74
|
+
return this._dataType;
|
|
72
75
|
}
|
|
73
76
|
/**
|
|
74
|
-
* Retrieves the
|
|
77
|
+
* Retrieves the codec for the specified operation.
|
|
75
78
|
*
|
|
76
79
|
* @param operation - The operation to retrieve the encoder for. Valid values are 'create' and 'update'.
|
|
77
80
|
*/
|
|
78
|
-
|
|
79
|
-
let
|
|
80
|
-
if (
|
|
81
|
-
return
|
|
82
|
-
const options = { projection: '*' };
|
|
81
|
+
getInputCodec(operation) {
|
|
82
|
+
let validator = this._inputCodecs[operation];
|
|
83
|
+
if (validator)
|
|
84
|
+
return validator;
|
|
85
|
+
const options = { projection: '*', ignoreReadonlyFields: true };
|
|
83
86
|
if (operation === 'update')
|
|
84
|
-
options.partial =
|
|
85
|
-
const dataType = this.
|
|
86
|
-
|
|
87
|
-
this.
|
|
88
|
-
return
|
|
87
|
+
options.partial = 'deep';
|
|
88
|
+
const dataType = this.dataType;
|
|
89
|
+
validator = dataType.generateCodec('decode', options);
|
|
90
|
+
this._inputCodecs[operation] = validator;
|
|
91
|
+
return validator;
|
|
89
92
|
}
|
|
90
93
|
/**
|
|
91
|
-
* Retrieves the
|
|
94
|
+
* Retrieves the codec.
|
|
92
95
|
*/
|
|
93
|
-
|
|
94
|
-
let
|
|
95
|
-
if (
|
|
96
|
-
return
|
|
97
|
-
const options = { projection: '*', partial: true };
|
|
98
|
-
const dataType = this.
|
|
99
|
-
|
|
100
|
-
this.
|
|
101
|
-
return
|
|
96
|
+
getOutputCodec(operation) {
|
|
97
|
+
let validator = this._outputCodecs[operation];
|
|
98
|
+
if (validator)
|
|
99
|
+
return validator;
|
|
100
|
+
const options = { projection: '*', partial: 'deep', ignoreWriteonlyFields: true };
|
|
101
|
+
const dataType = this.dataType;
|
|
102
|
+
validator = dataType.generateCodec('decode', options);
|
|
103
|
+
this._outputCodecs[operation] = validator;
|
|
104
|
+
return validator;
|
|
102
105
|
}
|
|
103
106
|
/**
|
|
104
107
|
* Executes the provided function within a transaction.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@opra/mongodb",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.4",
|
|
4
4
|
"description": "Opra MongoDB adapter package",
|
|
5
5
|
"author": "Panates",
|
|
6
6
|
"license": "MIT",
|
|
@@ -18,8 +18,8 @@
|
|
|
18
18
|
"postbuild": "cp README.md package.json ../../LICENSE ../../build/mongodb && cp ../../package.cjs.json ../../build/mongodb/cjs/package.json",
|
|
19
19
|
"lint": "eslint . --max-warnings=0",
|
|
20
20
|
"format": "prettier . --write --log-level=warn",
|
|
21
|
-
"test": "jest",
|
|
22
|
-
"cover": "jest --collect-coverage",
|
|
21
|
+
"test": "jest --passWithNoTests",
|
|
22
|
+
"cover": "jest --passWithNoTests --collect-coverage",
|
|
23
23
|
"clean": "npm run clean:src && npm run clean:dist && npm run clean:cover",
|
|
24
24
|
"clean:src": "ts-cleanup -s src --all && ts-cleanup -s test --all",
|
|
25
25
|
"clean:dist": "rimraf ../../build/mongodb",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"ts-gems": "^3.4.0"
|
|
32
32
|
},
|
|
33
33
|
"peerDependencies": {
|
|
34
|
-
"@opra/common": "^1.0.0-alpha.
|
|
35
|
-
"@opra/core": "^1.0.0-alpha.
|
|
34
|
+
"@opra/common": "^1.0.0-alpha.4",
|
|
35
|
+
"@opra/core": "^1.0.0-alpha.4",
|
|
36
36
|
"mongodb": ">= 6.0.0"
|
|
37
37
|
},
|
|
38
38
|
"type": "module",
|
|
@@ -87,6 +87,13 @@ export declare class MongoNestedService<T extends mongodb.Document> extends Mong
|
|
|
87
87
|
* @constructor
|
|
88
88
|
*/
|
|
89
89
|
constructor(dataType: Type | string, fieldName: string, options?: MongoNestedService.Options);
|
|
90
|
+
/**
|
|
91
|
+
* Retrieves the data type of the array field
|
|
92
|
+
*
|
|
93
|
+
* @returns {ComplexType} The complex data type of the field.
|
|
94
|
+
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
95
|
+
*/
|
|
96
|
+
get dataType(): ComplexType;
|
|
90
97
|
/**
|
|
91
98
|
* Asserts whether a resource with the specified parentId and id exists.
|
|
92
99
|
* Throws a ResourceNotFoundError if the resource does not exist.
|
|
@@ -239,13 +246,6 @@ export declare class MongoNestedService<T extends mongodb.Document> extends Mong
|
|
|
239
246
|
*/
|
|
240
247
|
updateMany(documentId: MongoAdapter.AnyId, input: PatchDTO<T>, options?: MongoNestedService.UpdateManyOptions<T>): Promise<number>;
|
|
241
248
|
protected _updateMany(documentId: MongoAdapter.AnyId, input: PatchDTO<T>, options?: MongoNestedService.UpdateManyOptions<T>): Promise<number>;
|
|
242
|
-
/**
|
|
243
|
-
* Retrieves the data type of the array field
|
|
244
|
-
*
|
|
245
|
-
* @returns {ComplexType} The complex data type of the field.
|
|
246
|
-
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
247
|
-
*/
|
|
248
|
-
getDataType(): ComplexType;
|
|
249
249
|
/**
|
|
250
250
|
* Retrieves the common filter used for querying array elements.
|
|
251
251
|
* This method is mostly used for security issues like securing multi-tenant applications.
|
package/types/mongo-service.d.ts
CHANGED
|
@@ -138,9 +138,10 @@ export declare namespace MongoService {
|
|
|
138
138
|
* @template T - The type of the documents in the collection.
|
|
139
139
|
*/
|
|
140
140
|
export declare class MongoService<T extends mongodb.Document = mongodb.Document> extends ServiceBase {
|
|
141
|
-
protected
|
|
142
|
-
protected
|
|
143
|
-
protected
|
|
141
|
+
protected _dataType_: Type | string;
|
|
142
|
+
protected _dataType: ComplexType;
|
|
143
|
+
protected _inputCodecs: Record<string, IsObject.Validator<T>>;
|
|
144
|
+
protected _outputCodecs: Record<string, IsObject.Validator<T>>;
|
|
144
145
|
/**
|
|
145
146
|
* Represents the name of a collection in MongoDB
|
|
146
147
|
*/
|
|
@@ -210,21 +211,21 @@ export declare class MongoService<T extends mongodb.Document = mongodb.Document>
|
|
|
210
211
|
*/
|
|
211
212
|
getResourceName(): string;
|
|
212
213
|
/**
|
|
213
|
-
* Retrieves the data type
|
|
214
|
+
* Retrieves the OPRA data type
|
|
214
215
|
*
|
|
215
216
|
* @throws {NotAcceptableError} If the data type is not a ComplexType.
|
|
216
217
|
*/
|
|
217
|
-
|
|
218
|
+
get dataType(): ComplexType;
|
|
218
219
|
/**
|
|
219
|
-
* Retrieves the
|
|
220
|
+
* Retrieves the codec for the specified operation.
|
|
220
221
|
*
|
|
221
222
|
* @param operation - The operation to retrieve the encoder for. Valid values are 'create' and 'update'.
|
|
222
223
|
*/
|
|
223
|
-
|
|
224
|
+
getInputCodec(operation: string): IsObject.Validator<T>;
|
|
224
225
|
/**
|
|
225
|
-
* Retrieves the
|
|
226
|
+
* Retrieves the codec.
|
|
226
227
|
*/
|
|
227
|
-
|
|
228
|
+
getOutputCodec(operation: string): IsObject.Validator<T>;
|
|
228
229
|
/**
|
|
229
230
|
* Executes the provided function within a transaction.
|
|
230
231
|
*
|