| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140 |
- 'use strict';
- /*!
- * ignore
- */
- module.exports = applyHooks;
- /*!
- * ignore
- */
- applyHooks.middlewareFunctions = [
- 'deleteOne',
- 'remove',
- 'save',
- 'updateOne',
- 'validate',
- 'init'
- ];
- /*!
- * ignore
- */
- const alreadyHookedFunctions = new Set(applyHooks.middlewareFunctions.flatMap(fn => ([fn, `$__${fn}`])));
- /**
- * Register hooks for this model
- *
- * @param {Model} model
- * @param {Schema} schema
- * @param {Object} options
- * @api private
- */
- function applyHooks(model, schema, options) {
- options = options || {};
- const kareemOptions = {
- useErrorHandlers: true,
- numCallbackParams: 1,
- nullResultByDefault: true,
- contextParameter: true
- };
- const objToDecorate = options.decorateDoc ? model : model.prototype;
- model.$appliedHooks = true;
- for (const key of Object.keys(schema.paths)) {
- let type = schema.paths[key];
- let childModel = null;
- const result = findChildModel(type);
- if (result) {
- childModel = result.childModel;
- type = result.type;
- } else {
- continue;
- }
- if (childModel.$appliedHooks) {
- continue;
- }
- applyHooks(childModel, type.schema, {
- ...options,
- decorateDoc: false,
- isChildSchema: true
- });
- if (childModel.discriminators != null) {
- const keys = Object.keys(childModel.discriminators);
- for (const key of keys) {
- applyHooks(childModel.discriminators[key],
- childModel.discriminators[key].schema, options);
- }
- }
- }
- // Built-in hooks rely on hooking internal functions in order to support
- // promises and make it so that `doc.save.toString()` provides meaningful
- // information.
- const middleware = schema._getDocumentMiddleware();
- model._middleware = middleware;
- objToDecorate.$__init = middleware.
- createWrapperSync('init', objToDecorate.$__init, null, kareemOptions);
- // Support hooks for custom methods
- const customMethods = Object.keys(schema.methods);
- const customMethodOptions = Object.assign({}, kareemOptions, {
- // Only use `checkForPromise` for custom methods, because mongoose
- // query thunks are not as consistent as I would like about returning
- // a nullish value rather than the query. If a query thunk returns
- // a query, `checkForPromise` causes infinite recursion
- checkForPromise: true
- });
- for (const method of customMethods) {
- if (alreadyHookedFunctions.has(method)) {
- continue;
- }
- if (!middleware.hasHooks(method)) {
- // Don't wrap if there are no hooks for the custom method to avoid
- // surprises. Also, `createWrapper()` enforces consistent async,
- // so wrapping a sync method would break it.
- continue;
- }
- const originalMethod = objToDecorate[method];
- objToDecorate[`$__${method}`] = objToDecorate[method];
- objToDecorate[method] = middleware.
- createWrapper(method, originalMethod, null, customMethodOptions);
- }
- }
- /**
- * Check if there is an embedded schematype in the given schematype. Handles drilling down into primitive
- * arrays and maps in case of array of array of subdocs or map of subdocs.
- *
- * @param {SchemaType} curType
- * @returns {{ childModel: Model | typeof Subdocument, curType: SchemaType } | null}
- */
- function findChildModel(curType) {
- if (curType.$isSingleNested || curType.$isMongooseDocumentArray) {
- return { childModel: curType.Constructor, type: curType };
- }
- if (curType.instance === 'Array') {
- const embedded = curType.getEmbeddedSchemaType();
- if (embedded) {
- return findChildModel(embedded);
- }
- }
- if (curType.instance === 'Map') {
- const mapType = curType.getEmbeddedSchemaType();
- if (mapType) {
- return findChildModel(mapType);
- }
- }
- return null;
- }
|