Ohm-Management - Projektarbeit B-ME
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.

updateValidators.js 7.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. const Mixed = require('../schema/mixed');
  6. const ValidationError = require('../error/validation');
  7. const flatten = require('./common').flatten;
  8. const modifiedPaths = require('./common').modifiedPaths;
  9. const parallel = require('async/parallel');
  10. /**
  11. * Applies validators and defaults to update and findOneAndUpdate operations,
  12. * specifically passing a null doc as `this` to validators and defaults
  13. *
  14. * @param {Query} query
  15. * @param {Schema} schema
  16. * @param {Object} castedDoc
  17. * @param {Object} options
  18. * @method runValidatorsOnUpdate
  19. * @api private
  20. */
  21. module.exports = function(query, schema, castedDoc, options) {
  22. let _keys;
  23. const keys = Object.keys(castedDoc || {});
  24. let updatedKeys = {};
  25. let updatedValues = {};
  26. const isPull = {};
  27. const arrayAtomicUpdates = {};
  28. const numKeys = keys.length;
  29. let hasDollarUpdate = false;
  30. const modified = {};
  31. let currentUpdate;
  32. let key;
  33. let i;
  34. for (i = 0; i < numKeys; ++i) {
  35. if (keys[i].charAt(0) === '$') {
  36. hasDollarUpdate = true;
  37. if (keys[i] === '$push' || keys[i] === '$addToSet') {
  38. _keys = Object.keys(castedDoc[keys[i]]);
  39. for (let ii = 0; ii < _keys.length; ++ii) {
  40. currentUpdate = castedDoc[keys[i]][_keys[ii]];
  41. if (currentUpdate && currentUpdate.$each) {
  42. arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []).
  43. concat(currentUpdate.$each);
  44. } else {
  45. arrayAtomicUpdates[_keys[ii]] = (arrayAtomicUpdates[_keys[ii]] || []).
  46. concat([currentUpdate]);
  47. }
  48. }
  49. continue;
  50. }
  51. modifiedPaths(castedDoc[keys[i]], '', modified);
  52. const flat = flatten(castedDoc[keys[i]]);
  53. const paths = Object.keys(flat);
  54. const numPaths = paths.length;
  55. for (let j = 0; j < numPaths; ++j) {
  56. let updatedPath = paths[j].replace('.$.', '.0.');
  57. updatedPath = updatedPath.replace(/\.\$$/, '.0');
  58. key = keys[i];
  59. // With `$pull` we might flatten `$in`. Skip stuff nested under `$in`
  60. // for the rest of the logic, it will get handled later.
  61. if (updatedPath.indexOf('$') !== -1) {
  62. continue;
  63. }
  64. if (key === '$set' || key === '$setOnInsert' ||
  65. key === '$pull' || key === '$pullAll') {
  66. updatedValues[updatedPath] = flat[paths[j]];
  67. isPull[updatedPath] = key === '$pull' || key === '$pullAll';
  68. } else if (key === '$unset') {
  69. updatedValues[updatedPath] = undefined;
  70. }
  71. updatedKeys[updatedPath] = true;
  72. }
  73. }
  74. }
  75. if (!hasDollarUpdate) {
  76. modifiedPaths(castedDoc, '', modified);
  77. updatedValues = flatten(castedDoc);
  78. updatedKeys = Object.keys(updatedValues);
  79. }
  80. const updates = Object.keys(updatedValues);
  81. const numUpdates = updates.length;
  82. const validatorsToExecute = [];
  83. const validationErrors = [];
  84. const alreadyValidated = [];
  85. const context = options && options.context === 'query' ? query : null;
  86. function iter(i, v) {
  87. const schemaPath = schema._getSchema(updates[i]);
  88. if (schemaPath) {
  89. // gh-4305: `_getSchema()` will report all sub-fields of a 'Mixed' path
  90. // as 'Mixed', so avoid double validating them.
  91. if (schemaPath instanceof Mixed && schemaPath.$fullPath !== updates[i]) {
  92. return;
  93. }
  94. if (v && Array.isArray(v.$in)) {
  95. v.$in.forEach((v, i) => {
  96. validatorsToExecute.push(function(callback) {
  97. schemaPath.doValidate(
  98. v,
  99. function(err) {
  100. if (err) {
  101. err.path = updates[i] + '.$in.' + i;
  102. validationErrors.push(err);
  103. }
  104. callback(null);
  105. },
  106. context,
  107. {updateValidator: true});
  108. });
  109. });
  110. } else {
  111. if (isPull[updates[i]] &&
  112. !Array.isArray(v) &&
  113. schemaPath.$isMongooseArray) {
  114. v = [v];
  115. }
  116. if (schemaPath.$isMongooseDocumentArrayElement && v != null && v.$__ != null) {
  117. alreadyValidated.push(updates[i]);
  118. validatorsToExecute.push(function(callback) {
  119. schemaPath.doValidate(v, function(err) {
  120. if (err) {
  121. err.path = updates[i];
  122. validationErrors.push(err);
  123. return callback(null);
  124. }
  125. v.validate(function(err) {
  126. if (err) {
  127. if (err.errors) {
  128. for (const key of Object.keys(err.errors)) {
  129. const _err = err.errors[key];
  130. _err.path = updates[i] + '.' + key;
  131. validationErrors.push(_err);
  132. }
  133. }
  134. }
  135. callback(null);
  136. });
  137. }, context, { updateValidator: true });
  138. });
  139. } else {
  140. validatorsToExecute.push(function(callback) {
  141. for (const path of alreadyValidated) {
  142. if (updates[i].startsWith(path + '.')) {
  143. return callback(null);
  144. }
  145. }
  146. schemaPath.doValidate(v, function(err) {
  147. if (err) {
  148. err.path = updates[i];
  149. validationErrors.push(err);
  150. }
  151. callback(null);
  152. }, context, { updateValidator: true });
  153. });
  154. }
  155. }
  156. }
  157. }
  158. for (i = 0; i < numUpdates; ++i) {
  159. iter(i, updatedValues[updates[i]]);
  160. }
  161. const arrayUpdates = Object.keys(arrayAtomicUpdates);
  162. const numArrayUpdates = arrayUpdates.length;
  163. for (i = 0; i < numArrayUpdates; ++i) {
  164. (function(i) {
  165. let schemaPath = schema._getSchema(arrayUpdates[i]);
  166. if (schemaPath && schemaPath.$isMongooseDocumentArray) {
  167. validatorsToExecute.push(function(callback) {
  168. schemaPath.doValidate(
  169. arrayAtomicUpdates[arrayUpdates[i]],
  170. function(err) {
  171. if (err) {
  172. err.path = arrayUpdates[i];
  173. validationErrors.push(err);
  174. }
  175. callback(null);
  176. },
  177. options && options.context === 'query' ? query : null);
  178. });
  179. } else {
  180. schemaPath = schema._getSchema(arrayUpdates[i] + '.0');
  181. for (let j = 0; j < arrayAtomicUpdates[arrayUpdates[i]].length; ++j) {
  182. (function(j) {
  183. validatorsToExecute.push(function(callback) {
  184. schemaPath.doValidate(
  185. arrayAtomicUpdates[arrayUpdates[i]][j],
  186. function(err) {
  187. if (err) {
  188. err.path = arrayUpdates[i];
  189. validationErrors.push(err);
  190. }
  191. callback(null);
  192. },
  193. options && options.context === 'query' ? query : null,
  194. { updateValidator: true });
  195. });
  196. })(j);
  197. }
  198. }
  199. })(i);
  200. }
  201. return function(callback) {
  202. parallel(validatorsToExecute, function() {
  203. if (validationErrors.length) {
  204. const err = new ValidationError(null);
  205. for (let i = 0; i < validationErrors.length; ++i) {
  206. err.addError(validationErrors[i].path, validationErrors[i]);
  207. }
  208. return callback(err);
  209. }
  210. callback(null);
  211. });
  212. };
  213. };