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
			| 
											3 years ago
										 | '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); | ||
|  |   } | ||
|  | } |