You can not select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
					
					
						
							138 lines
						
					
					
						
							4.0 KiB
						
					
					
				
			
		
		
	
	
							138 lines
						
					
					
						
							4.0 KiB
						
					
					
				| 'use strict';
 | |
| 
 | |
| const symbols = require('../../schema/symbols');
 | |
| const promiseOrCallback = require('../promiseOrCallback');
 | |
| 
 | |
| /*!
 | |
|  * ignore
 | |
|  */
 | |
| 
 | |
| module.exports = applyHooks;
 | |
| 
 | |
| /*!
 | |
|  * ignore
 | |
|  */
 | |
| 
 | |
| applyHooks.middlewareFunctions = [
 | |
|   'deleteOne',
 | |
|   'save',
 | |
|   'validate',
 | |
|   'remove',
 | |
|   'updateOne',
 | |
|   'init'
 | |
| ];
 | |
| 
 | |
| /*!
 | |
|  * Register hooks for this model
 | |
|  *
 | |
|  * @param {Model} model
 | |
|  * @param {Schema} schema
 | |
|  */
 | |
| 
 | |
| 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)) {
 | |
|     const type = schema.paths[key];
 | |
|     let childModel = null;
 | |
|     if (type.$isSingleNested) {
 | |
|       childModel = type.caster;
 | |
|     } else if (type.$isMongooseDocumentArray) {
 | |
|       childModel = type.Constructor;
 | |
|     } else {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     if (childModel.$appliedHooks) {
 | |
|       continue;
 | |
|     }
 | |
| 
 | |
|     applyHooks(childModel, type.schema, options);
 | |
|     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.s.hooks.
 | |
|     filter(hook => {
 | |
|       if (hook.name === 'updateOne' || hook.name === 'deleteOne') {
 | |
|         return !!hook['document'];
 | |
|       }
 | |
|       if (hook.name === 'remove' || hook.name === 'init') {
 | |
|         return hook['document'] == null || !!hook['document'];
 | |
|       }
 | |
|       if (hook.query != null || hook.document != null) {
 | |
|         return hook.document !== false;
 | |
|       }
 | |
|       return true;
 | |
|     }).
 | |
|     filter(hook => {
 | |
|       // If user has overwritten the method, don't apply built-in middleware
 | |
|       if (schema.methods[hook.name]) {
 | |
|         return !hook.fn[symbols.builtInMiddleware];
 | |
|       }
 | |
| 
 | |
|       return true;
 | |
|     });
 | |
| 
 | |
|   model._middleware = middleware;
 | |
| 
 | |
|   objToDecorate.$__originalValidate = objToDecorate.$__originalValidate || objToDecorate.$__validate;
 | |
| 
 | |
|   for (const method of ['save', 'validate', 'remove', 'deleteOne']) {
 | |
|     const toWrap = method === 'validate' ? '$__originalValidate' : `$__${method}`;
 | |
|     const wrapped = middleware.
 | |
|       createWrapper(method, objToDecorate[toWrap], null, kareemOptions);
 | |
|     objToDecorate[`$__${method}`] = wrapped;
 | |
|   }
 | |
|   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 (!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] = function() {
 | |
|       const args = Array.prototype.slice.call(arguments);
 | |
|       const cb = args.slice(-1).pop();
 | |
|       const argsWithoutCallback = typeof cb === 'function' ?
 | |
|         args.slice(0, args.length - 1) : args;
 | |
|       return promiseOrCallback(cb, callback => {
 | |
|         return this[`$__${method}`].apply(this,
 | |
|           argsWithoutCallback.concat([callback]));
 | |
|       }, model.events);
 | |
|     };
 | |
|     objToDecorate[`$__${method}`] = middleware.
 | |
|       createWrapper(method, originalMethod, null, customMethodOptions);
 | |
|   }
 | |
| } |