Skip to content

Commit aa65645

Browse files
committed
feat: add middleware for bulkWrite() and createCollection()
Fix #7893 Fix #14263
1 parent 7732ce2 commit aa65645

File tree

5 files changed

+312
-61
lines changed

5 files changed

+312
-61
lines changed

docs/middleware.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,13 +67,17 @@ Model middleware is supported for the following model functions.
6767
Don't confuse model middleware and document middleware: model middleware hooks into *static* functions on a `Model` class, document middleware hooks into *methods* on a `Model` class.
6868
In model middleware functions, `this` refers to the model.
6969

70+
* [bulkWrite](api/model.html#model_Model-bulkWrite)
71+
* [createCollection](api/model.html#model_Model-createCollection)
7072
* [insertMany](api/model.html#model_Model-insertMany)
7173

7274
Here are the possible strings that can be passed to `pre()`
7375

7476
* aggregate
77+
* bulkWrite
7578
* count
7679
* countDocuments
80+
* createCollection
7781
* deleteOne
7882
* deleteMany
7983
* estimatedDocumentCount

lib/model.js

Lines changed: 126 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1415,6 +1415,18 @@ Model.createCollection = async function createCollection(options) {
14151415
throw new MongooseError('Model.createCollection() no longer accepts a callback');
14161416
}
14171417

1418+
const shouldSkip = await new Promise((resolve, reject) => {
1419+
this.hooks.execPre('createCollection', this, [options], (err) => {
1420+
if (err != null) {
1421+
if (err instanceof Kareem.skipWrappedFunction) {
1422+
return resolve(true);
1423+
}
1424+
return reject(err);
1425+
}
1426+
resolve();
1427+
});
1428+
});
1429+
14181430
const collectionOptions = this &&
14191431
this.schema &&
14201432
this.schema.options &&
@@ -1468,13 +1480,32 @@ Model.createCollection = async function createCollection(options) {
14681480
}
14691481

14701482
try {
1471-
await this.db.createCollection(this.$__collection.collectionName, options);
1483+
if (!shouldSkip) {
1484+
await this.db.createCollection(this.$__collection.collectionName, options);
1485+
}
14721486
} catch (err) {
1473-
14741487
if (err != null && (err.name !== 'MongoServerError' || err.code !== 48)) {
1475-
throw err;
1488+
await new Promise((resolve, reject) => {
1489+
const _opts = { error: err };
1490+
this.hooks.execPost('createCollection', this, [null], _opts, (err) => {
1491+
if (err != null) {
1492+
return reject(err);
1493+
}
1494+
resolve();
1495+
});
1496+
});
14761497
}
14771498
}
1499+
1500+
await new Promise((resolve, reject) => {
1501+
this.hooks.execPost('createCollection', this, [this.$__collection], (err) => {
1502+
if (err != null) {
1503+
return reject(err);
1504+
}
1505+
resolve();
1506+
});
1507+
});
1508+
14781509
return this.$__collection;
14791510
};
14801511

@@ -3428,44 +3459,62 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
34283459
throw new MongooseError('Model.bulkWrite() no longer accepts a callback');
34293460
}
34303461
options = options || {};
3462+
3463+
const shouldSkip = await new Promise((resolve, reject) => {
3464+
this.hooks.execPre('bulkWrite', this, [ops, options], (err) => {
3465+
if (err != null) {
3466+
if (err instanceof Kareem.skipWrappedFunction) {
3467+
return resolve(err);
3468+
}
3469+
return reject(err);
3470+
}
3471+
resolve();
3472+
});
3473+
});
3474+
3475+
if (shouldSkip) {
3476+
return shouldSkip.args[0];
3477+
}
3478+
34313479
const ordered = options.ordered == null ? true : options.ordered;
34323480

3481+
if (ops.length === 0) {
3482+
return getDefaultBulkwriteResult();
3483+
}
3484+
34333485
const validations = ops.map(op => castBulkWrite(this, op, options));
34343486

3435-
return new Promise((resolve, reject) => {
3436-
if (ordered) {
3487+
let res = null;
3488+
if (ordered) {
3489+
await new Promise((resolve, reject) => {
34373490
each(validations, (fn, cb) => fn(cb), error => {
34383491
if (error) {
34393492
return reject(error);
34403493
}
34413494

3442-
if (ops.length === 0) {
3443-
return resolve(getDefaultBulkwriteResult());
3444-
}
3445-
3446-
try {
3447-
this.$__collection.bulkWrite(ops, options, (error, res) => {
3448-
if (error) {
3449-
return reject(error);
3450-
}
3451-
3452-
resolve(res);
3453-
});
3454-
} catch (err) {
3455-
return reject(err);
3456-
}
3495+
resolve();
34573496
});
3497+
});
34583498

3459-
return;
3499+
try {
3500+
res = await this.$__collection.bulkWrite(ops, options);
3501+
} catch (error) {
3502+
await new Promise((resolve, reject) => {
3503+
const _opts = { error: error };
3504+
this.hooks.execPost('bulkWrite', this, [null], _opts, (err) => {
3505+
if (err != null) {
3506+
return reject(err);
3507+
}
3508+
resolve();
3509+
});
3510+
});
34603511
}
3461-
3512+
} else {
34623513
let remaining = validations.length;
34633514
let validOps = [];
34643515
let validationErrors = [];
34653516
const results = [];
3466-
if (remaining === 0) {
3467-
completeUnorderedValidation.call(this);
3468-
} else {
3517+
await new Promise((resolve) => {
34693518
for (let i = 0; i < validations.length; ++i) {
34703519
validations[i]((err) => {
34713520
if (err == null) {
@@ -3475,56 +3524,74 @@ Model.bulkWrite = async function bulkWrite(ops, options) {
34753524
results[i] = err;
34763525
}
34773526
if (--remaining <= 0) {
3478-
completeUnorderedValidation.call(this);
3527+
resolve();
34793528
}
34803529
});
34813530
}
3482-
}
3531+
});
34833532

34843533
validationErrors = validationErrors.
34853534
sort((v1, v2) => v1.index - v2.index).
34863535
map(v => v.error);
34873536

3488-
function completeUnorderedValidation() {
3489-
const validOpIndexes = validOps;
3490-
validOps = validOps.sort().map(index => ops[index]);
3537+
const validOpIndexes = validOps;
3538+
validOps = validOps.sort().map(index => ops[index]);
34913539

3492-
if (validOps.length === 0) {
3493-
return resolve(getDefaultBulkwriteResult());
3494-
}
3540+
if (validOps.length === 0) {
3541+
return getDefaultBulkwriteResult();
3542+
}
34953543

3496-
this.$__collection.bulkWrite(validOps, options, (error, res) => {
3497-
if (error) {
3498-
if (validationErrors.length > 0) {
3499-
error.mongoose = error.mongoose || {};
3500-
error.mongoose.validationErrors = validationErrors;
3501-
}
3544+
let error;
3545+
[res, error] = await this.$__collection.bulkWrite(validOps, options).
3546+
then(res => ([res, null])).
3547+
catch(err => ([null, err]));
35023548

3503-
return reject(error);
3504-
}
3549+
if (error) {
3550+
if (validationErrors.length > 0) {
3551+
error.mongoose = error.mongoose || {};
3552+
error.mongoose.validationErrors = validationErrors;
3553+
}
35053554

3506-
for (let i = 0; i < validOpIndexes.length; ++i) {
3507-
results[validOpIndexes[i]] = null;
3508-
}
3509-
if (validationErrors.length > 0) {
3510-
if (options.throwOnValidationError) {
3511-
return reject(new MongooseBulkWriteError(
3512-
validationErrors,
3513-
results,
3514-
res,
3515-
'bulkWrite'
3516-
));
3517-
} else {
3518-
res.mongoose = res.mongoose || {};
3519-
res.mongoose.validationErrors = validationErrors;
3520-
res.mongoose.results = results;
3555+
await new Promise((resolve, reject) => {
3556+
const _opts = { error: error };
3557+
this.hooks.execPost('bulkWrite', this, [null], _opts, (err) => {
3558+
if (err != null) {
3559+
return reject(err);
35213560
}
3522-
}
3523-
3524-
resolve(res);
3561+
resolve();
3562+
});
35253563
});
35263564
}
3565+
3566+
for (let i = 0; i < validOpIndexes.length; ++i) {
3567+
results[validOpIndexes[i]] = null;
3568+
}
3569+
if (validationErrors.length > 0) {
3570+
if (options.throwOnValidationError) {
3571+
throw new MongooseBulkWriteError(
3572+
validationErrors,
3573+
results,
3574+
res,
3575+
'bulkWrite'
3576+
);
3577+
} else {
3578+
res.mongoose = res.mongoose || {};
3579+
res.mongoose.validationErrors = validationErrors;
3580+
res.mongoose.results = results;
3581+
}
3582+
}
3583+
}
3584+
3585+
await new Promise((resolve, reject) => {
3586+
this.hooks.execPost('bulkWrite', this, [res], (err) => {
3587+
if (err != null) {
3588+
return reject(err);
3589+
}
3590+
resolve();
3591+
});
35273592
});
3593+
3594+
return res;
35283595
};
35293596

35303597
/**

0 commit comments

Comments
 (0)