applyHooks.js 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122
  1. 'use strict';
  2. const symbols = require('../../schema/symbols');
  3. const utils = require('../../utils');
  4. /*!
  5. * ignore
  6. */
  7. module.exports = applyHooks;
  8. /*!
  9. * ignore
  10. */
  11. applyHooks.middlewareFunctions = [
  12. 'save',
  13. 'validate',
  14. 'remove',
  15. 'updateOne',
  16. 'init'
  17. ];
  18. /*!
  19. * Register hooks for this model
  20. *
  21. * @param {Model} model
  22. * @param {Schema} schema
  23. */
  24. function applyHooks(model, schema, options) {
  25. options = options || {};
  26. const kareemOptions = {
  27. useErrorHandlers: true,
  28. numCallbackParams: 1,
  29. nullResultByDefault: true,
  30. contextParameter: true
  31. };
  32. const objToDecorate = options.decorateDoc ? model : model.prototype;
  33. model.$appliedHooks = true;
  34. for (let i = 0; i < schema.childSchemas.length; ++i) {
  35. const childModel = schema.childSchemas[i].model;
  36. if (childModel.$appliedHooks) {
  37. continue;
  38. }
  39. applyHooks(childModel, schema.childSchemas[i].schema, options);
  40. if (childModel.discriminators != null) {
  41. const keys = Object.keys(childModel.discriminators);
  42. for (let j = 0; j < keys.length; ++j) {
  43. applyHooks(childModel.discriminators[keys[j]],
  44. childModel.discriminators[keys[j]].schema, options);
  45. }
  46. }
  47. }
  48. // Built-in hooks rely on hooking internal functions in order to support
  49. // promises and make it so that `doc.save.toString()` provides meaningful
  50. // information.
  51. const middleware = schema.s.hooks.
  52. filter(hook => {
  53. if (hook.name === 'updateOne') {
  54. return !!hook['document'];
  55. }
  56. if (hook.name === 'remove') {
  57. return hook['document'] == null || !!hook['document'];
  58. }
  59. return true;
  60. }).
  61. filter(hook => {
  62. // If user has overwritten the method, don't apply built-in middleware
  63. if (schema.methods[hook.name]) {
  64. return !hook.fn[symbols.builtInMiddleware];
  65. }
  66. return true;
  67. });
  68. model._middleware = middleware;
  69. objToDecorate.$__save = middleware.
  70. createWrapper('save', objToDecorate.$__save, null, kareemOptions);
  71. objToDecorate.$__validate = middleware.
  72. createWrapper('validate', objToDecorate.$__validate, null, kareemOptions);
  73. objToDecorate.$__remove = middleware.
  74. createWrapper('remove', objToDecorate.$__remove, null, kareemOptions);
  75. objToDecorate.$__init = middleware.
  76. createWrapperSync('init', objToDecorate.$__init, null, kareemOptions);
  77. // Support hooks for custom methods
  78. const customMethods = Object.keys(schema.methods);
  79. const customMethodOptions = Object.assign({}, kareemOptions, {
  80. // Only use `checkForPromise` for custom methods, because mongoose
  81. // query thunks are not as consistent as I would like about returning
  82. // a nullish value rather than the query. If a query thunk returns
  83. // a query, `checkForPromise` causes infinite recursion
  84. checkForPromise: true
  85. });
  86. for (const method of customMethods) {
  87. if (!middleware.hasHooks(method)) {
  88. // Don't wrap if there are no hooks for the custom method to avoid
  89. // surprises. Also, `createWrapper()` enforces consistent async,
  90. // so wrapping a sync method would break it.
  91. continue;
  92. }
  93. const originalMethod = objToDecorate[method];
  94. objToDecorate[method] = function() {
  95. const args = Array.prototype.slice.call(arguments);
  96. const cb = utils.last(args);
  97. const argsWithoutCallback = typeof cb === 'function' ?
  98. args.slice(0, args.length - 1) : args;
  99. return utils.promiseOrCallback(cb, callback => {
  100. return this[`$__${method}`].apply(this,
  101. argsWithoutCallback.concat([callback]));
  102. }, model.events);
  103. };
  104. objToDecorate[`$__${method}`] = middleware.
  105. createWrapper(method, originalMethod, null, customMethodOptions);
  106. }
  107. }