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);
 | 
						|
  }
 | 
						|
} |