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.

string.js 16KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616
  1. 'use strict';
  2. /*!
  3. * Module dependencies.
  4. */
  5. const SchemaType = require('../schematype');
  6. const CastError = SchemaType.CastError;
  7. const MongooseError = require('../error');
  8. const castString = require('../cast/string');
  9. const utils = require('../utils');
  10. const populateModelSymbol = require('../helpers/symbols').populateModelSymbol;
  11. let Document;
  12. /**
  13. * String SchemaType constructor.
  14. *
  15. * @param {String} key
  16. * @param {Object} options
  17. * @inherits SchemaType
  18. * @api public
  19. */
  20. function SchemaString(key, options) {
  21. this.enumValues = [];
  22. this.regExp = null;
  23. SchemaType.call(this, key, options, 'String');
  24. }
  25. /**
  26. * This schema type's name, to defend against minifiers that mangle
  27. * function names.
  28. *
  29. * @api public
  30. */
  31. SchemaString.schemaName = 'String';
  32. /*!
  33. * Inherits from SchemaType.
  34. */
  35. SchemaString.prototype = Object.create(SchemaType.prototype);
  36. SchemaString.prototype.constructor = SchemaString;
  37. /*!
  38. * ignore
  39. */
  40. SchemaString._cast = castString;
  41. /**
  42. * Get/set the function used to cast arbitrary values to strings.
  43. *
  44. * ####Example:
  45. *
  46. * // Throw an error if you pass in an object. Normally, Mongoose allows
  47. * // objects with custom `toString()` functions.
  48. * const original = mongoose.Schema.Types.String.cast();
  49. * mongoose.Schema.Types.String.cast(v => {
  50. * assert.ok(v == null || typeof v !== 'object');
  51. * return original(v);
  52. * });
  53. *
  54. * // Or disable casting entirely
  55. * mongoose.Schema.Types.String.cast(false);
  56. *
  57. * @param {Function} caster
  58. * @return {Function}
  59. * @function get
  60. * @static
  61. * @api public
  62. */
  63. SchemaString.cast = function cast(caster) {
  64. if (arguments.length === 0) {
  65. return this._cast;
  66. }
  67. if (caster === false) {
  68. caster = v => {
  69. if (v != null && typeof v !== 'string') {
  70. throw new Error();
  71. }
  72. return v;
  73. };
  74. }
  75. this._cast = caster;
  76. return this._cast;
  77. };
  78. /**
  79. * Attaches a getter for all String instances.
  80. *
  81. * ####Example:
  82. *
  83. * // Make all numbers round down
  84. * mongoose.Schema.String.get(v => v.toLowerCase());
  85. *
  86. * const Model = mongoose.model('Test', new Schema({ test: String }));
  87. * new Model({ test: 'FOO' }).test; // 'foo'
  88. *
  89. * @param {Function} getter
  90. * @return {this}
  91. * @function get
  92. * @static
  93. * @api public
  94. */
  95. SchemaString.get = SchemaType.get;
  96. /*!
  97. * ignore
  98. */
  99. SchemaString._checkRequired = v => (v instanceof String || typeof v === 'string') && v.length;
  100. /**
  101. * Override the function the required validator uses to check whether a string
  102. * passes the `required` check.
  103. *
  104. * ####Example:
  105. *
  106. * // Allow empty strings to pass `required` check
  107. * mongoose.Schema.Types.String.checkRequired(v => v != null);
  108. *
  109. * const M = mongoose.model({ str: { type: String, required: true } });
  110. * new M({ str: '' }).validateSync(); // `null`, validation passes!
  111. *
  112. * @param {Function} fn
  113. * @return {Function}
  114. * @function checkRequired
  115. * @static
  116. * @api public
  117. */
  118. SchemaString.checkRequired = SchemaType.checkRequired;
  119. /**
  120. * Adds an enum validator
  121. *
  122. * ####Example:
  123. *
  124. * var states = ['opening', 'open', 'closing', 'closed']
  125. * var s = new Schema({ state: { type: String, enum: states }})
  126. * var M = db.model('M', s)
  127. * var m = new M({ state: 'invalid' })
  128. * m.save(function (err) {
  129. * console.error(String(err)) // ValidationError: `invalid` is not a valid enum value for path `state`.
  130. * m.state = 'open'
  131. * m.save(callback) // success
  132. * })
  133. *
  134. * // or with custom error messages
  135. * var enum = {
  136. * values: ['opening', 'open', 'closing', 'closed'],
  137. * message: 'enum validator failed for path `{PATH}` with value `{VALUE}`'
  138. * }
  139. * var s = new Schema({ state: { type: String, enum: enum })
  140. * var M = db.model('M', s)
  141. * var m = new M({ state: 'invalid' })
  142. * m.save(function (err) {
  143. * console.error(String(err)) // ValidationError: enum validator failed for path `state` with value `invalid`
  144. * m.state = 'open'
  145. * m.save(callback) // success
  146. * })
  147. *
  148. * @param {String|Object} [args...] enumeration values
  149. * @return {SchemaType} this
  150. * @see Customized Error Messages #error_messages_MongooseError-messages
  151. * @api public
  152. */
  153. SchemaString.prototype.enum = function() {
  154. if (this.enumValidator) {
  155. this.validators = this.validators.filter(function(v) {
  156. return v.validator !== this.enumValidator;
  157. }, this);
  158. this.enumValidator = false;
  159. }
  160. if (arguments[0] === void 0 || arguments[0] === false) {
  161. return this;
  162. }
  163. let values;
  164. let errorMessage;
  165. if (utils.isObject(arguments[0])) {
  166. values = arguments[0].values;
  167. errorMessage = arguments[0].message;
  168. } else {
  169. values = arguments;
  170. errorMessage = MongooseError.messages.String.enum;
  171. }
  172. for (let i = 0; i < values.length; i++) {
  173. if (undefined !== values[i]) {
  174. this.enumValues.push(this.cast(values[i]));
  175. }
  176. }
  177. const vals = this.enumValues;
  178. this.enumValidator = function(v) {
  179. return undefined === v || ~vals.indexOf(v);
  180. };
  181. this.validators.push({
  182. validator: this.enumValidator,
  183. message: errorMessage,
  184. type: 'enum',
  185. enumValues: vals
  186. });
  187. return this;
  188. };
  189. /**
  190. * Adds a lowercase [setter](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set).
  191. *
  192. * ####Example:
  193. *
  194. * var s = new Schema({ email: { type: String, lowercase: true }})
  195. * var M = db.model('M', s);
  196. * var m = new M({ email: 'SomeEmail@example.COM' });
  197. * console.log(m.email) // someemail@example.com
  198. * M.find({ email: 'SomeEmail@example.com' }); // Queries by 'someemail@example.com'
  199. *
  200. * @api public
  201. * @return {SchemaType} this
  202. */
  203. SchemaString.prototype.lowercase = function(shouldApply) {
  204. if (arguments.length > 0 && !shouldApply) {
  205. return this;
  206. }
  207. return this.set(function(v, self) {
  208. if (typeof v !== 'string') {
  209. v = self.cast(v);
  210. }
  211. if (v) {
  212. return v.toLowerCase();
  213. }
  214. return v;
  215. });
  216. };
  217. /**
  218. * Adds an uppercase [setter](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set).
  219. *
  220. * ####Example:
  221. *
  222. * var s = new Schema({ caps: { type: String, uppercase: true }})
  223. * var M = db.model('M', s);
  224. * var m = new M({ caps: 'an example' });
  225. * console.log(m.caps) // AN EXAMPLE
  226. * M.find({ caps: 'an example' }) // Matches documents where caps = 'AN EXAMPLE'
  227. *
  228. * @api public
  229. * @return {SchemaType} this
  230. */
  231. SchemaString.prototype.uppercase = function(shouldApply) {
  232. if (arguments.length > 0 && !shouldApply) {
  233. return this;
  234. }
  235. return this.set(function(v, self) {
  236. if (typeof v !== 'string') {
  237. v = self.cast(v);
  238. }
  239. if (v) {
  240. return v.toUpperCase();
  241. }
  242. return v;
  243. });
  244. };
  245. /**
  246. * Adds a trim [setter](http://mongoosejs.com/docs/api.html#schematype_SchemaType-set).
  247. *
  248. * The string value will be trimmed when set.
  249. *
  250. * ####Example:
  251. *
  252. * var s = new Schema({ name: { type: String, trim: true }})
  253. * var M = db.model('M', s)
  254. * var string = ' some name '
  255. * console.log(string.length) // 11
  256. * var m = new M({ name: string })
  257. * console.log(m.name.length) // 9
  258. *
  259. * @api public
  260. * @return {SchemaType} this
  261. */
  262. SchemaString.prototype.trim = function(shouldTrim) {
  263. if (arguments.length > 0 && !shouldTrim) {
  264. return this;
  265. }
  266. return this.set(function(v, self) {
  267. if (typeof v !== 'string') {
  268. v = self.cast(v);
  269. }
  270. if (v) {
  271. return v.trim();
  272. }
  273. return v;
  274. });
  275. };
  276. /**
  277. * Sets a minimum length validator.
  278. *
  279. * ####Example:
  280. *
  281. * var schema = new Schema({ postalCode: { type: String, minlength: 5 })
  282. * var Address = db.model('Address', schema)
  283. * var address = new Address({ postalCode: '9512' })
  284. * address.save(function (err) {
  285. * console.error(err) // validator error
  286. * address.postalCode = '95125';
  287. * address.save() // success
  288. * })
  289. *
  290. * // custom error messages
  291. * // We can also use the special {MINLENGTH} token which will be replaced with the minimum allowed length
  292. * var minlength = [5, 'The value of path `{PATH}` (`{VALUE}`) is shorter than the minimum allowed length ({MINLENGTH}).'];
  293. * var schema = new Schema({ postalCode: { type: String, minlength: minlength })
  294. * var Address = mongoose.model('Address', schema);
  295. * var address = new Address({ postalCode: '9512' });
  296. * address.validate(function (err) {
  297. * console.log(String(err)) // ValidationError: The value of path `postalCode` (`9512`) is shorter than the minimum length (5).
  298. * })
  299. *
  300. * @param {Number} value minimum string length
  301. * @param {String} [message] optional custom error message
  302. * @return {SchemaType} this
  303. * @see Customized Error Messages #error_messages_MongooseError-messages
  304. * @api public
  305. */
  306. SchemaString.prototype.minlength = function(value, message) {
  307. if (this.minlengthValidator) {
  308. this.validators = this.validators.filter(function(v) {
  309. return v.validator !== this.minlengthValidator;
  310. }, this);
  311. }
  312. if (value !== null && value !== undefined) {
  313. let msg = message || MongooseError.messages.String.minlength;
  314. msg = msg.replace(/{MINLENGTH}/, value);
  315. this.validators.push({
  316. validator: this.minlengthValidator = function(v) {
  317. return v === null || v.length >= value;
  318. },
  319. message: msg,
  320. type: 'minlength',
  321. minlength: value
  322. });
  323. }
  324. return this;
  325. };
  326. /**
  327. * Sets a maximum length validator.
  328. *
  329. * ####Example:
  330. *
  331. * var schema = new Schema({ postalCode: { type: String, maxlength: 9 })
  332. * var Address = db.model('Address', schema)
  333. * var address = new Address({ postalCode: '9512512345' })
  334. * address.save(function (err) {
  335. * console.error(err) // validator error
  336. * address.postalCode = '95125';
  337. * address.save() // success
  338. * })
  339. *
  340. * // custom error messages
  341. * // We can also use the special {MAXLENGTH} token which will be replaced with the maximum allowed length
  342. * var maxlength = [9, 'The value of path `{PATH}` (`{VALUE}`) exceeds the maximum allowed length ({MAXLENGTH}).'];
  343. * var schema = new Schema({ postalCode: { type: String, maxlength: maxlength })
  344. * var Address = mongoose.model('Address', schema);
  345. * var address = new Address({ postalCode: '9512512345' });
  346. * address.validate(function (err) {
  347. * console.log(String(err)) // ValidationError: The value of path `postalCode` (`9512512345`) exceeds the maximum allowed length (9).
  348. * })
  349. *
  350. * @param {Number} value maximum string length
  351. * @param {String} [message] optional custom error message
  352. * @return {SchemaType} this
  353. * @see Customized Error Messages #error_messages_MongooseError-messages
  354. * @api public
  355. */
  356. SchemaString.prototype.maxlength = function(value, message) {
  357. if (this.maxlengthValidator) {
  358. this.validators = this.validators.filter(function(v) {
  359. return v.validator !== this.maxlengthValidator;
  360. }, this);
  361. }
  362. if (value !== null && value !== undefined) {
  363. let msg = message || MongooseError.messages.String.maxlength;
  364. msg = msg.replace(/{MAXLENGTH}/, value);
  365. this.validators.push({
  366. validator: this.maxlengthValidator = function(v) {
  367. return v === null || v.length <= value;
  368. },
  369. message: msg,
  370. type: 'maxlength',
  371. maxlength: value
  372. });
  373. }
  374. return this;
  375. };
  376. /**
  377. * Sets a regexp validator.
  378. *
  379. * Any value that does not pass `regExp`.test(val) will fail validation.
  380. *
  381. * ####Example:
  382. *
  383. * var s = new Schema({ name: { type: String, match: /^a/ }})
  384. * var M = db.model('M', s)
  385. * var m = new M({ name: 'I am invalid' })
  386. * m.validate(function (err) {
  387. * console.error(String(err)) // "ValidationError: Path `name` is invalid (I am invalid)."
  388. * m.name = 'apples'
  389. * m.validate(function (err) {
  390. * assert.ok(err) // success
  391. * })
  392. * })
  393. *
  394. * // using a custom error message
  395. * var match = [ /\.html$/, "That file doesn't end in .html ({VALUE})" ];
  396. * var s = new Schema({ file: { type: String, match: match }})
  397. * var M = db.model('M', s);
  398. * var m = new M({ file: 'invalid' });
  399. * m.validate(function (err) {
  400. * console.log(String(err)) // "ValidationError: That file doesn't end in .html (invalid)"
  401. * })
  402. *
  403. * Empty strings, `undefined`, and `null` values always pass the match validator. If you require these values, enable the `required` validator also.
  404. *
  405. * var s = new Schema({ name: { type: String, match: /^a/, required: true }})
  406. *
  407. * @param {RegExp} regExp regular expression to test against
  408. * @param {String} [message] optional custom error message
  409. * @return {SchemaType} this
  410. * @see Customized Error Messages #error_messages_MongooseError-messages
  411. * @api public
  412. */
  413. SchemaString.prototype.match = function match(regExp, message) {
  414. // yes, we allow multiple match validators
  415. const msg = message || MongooseError.messages.String.match;
  416. const matchValidator = function(v) {
  417. if (!regExp) {
  418. return false;
  419. }
  420. const ret = ((v != null && v !== '')
  421. ? regExp.test(v)
  422. : true);
  423. return ret;
  424. };
  425. this.validators.push({
  426. validator: matchValidator,
  427. message: msg,
  428. type: 'regexp',
  429. regexp: regExp
  430. });
  431. return this;
  432. };
  433. /**
  434. * Check if the given value satisfies the `required` validator. The value is
  435. * considered valid if it is a string (that is, not `null` or `undefined`) and
  436. * has positive length. The `required` validator **will** fail for empty
  437. * strings.
  438. *
  439. * @param {Any} value
  440. * @param {Document} doc
  441. * @return {Boolean}
  442. * @api public
  443. */
  444. SchemaString.prototype.checkRequired = function checkRequired(value, doc) {
  445. if (SchemaType._isRef(this, value, doc, true)) {
  446. return !!value;
  447. }
  448. // `require('util').inherits()` does **not** copy static properties, and
  449. // plugins like mongoose-float use `inherits()` for pre-ES6.
  450. const _checkRequired = typeof this.constructor.checkRequired == 'function' ?
  451. this.constructor.checkRequired() :
  452. SchemaString.checkRequired();
  453. return _checkRequired(value);
  454. };
  455. /**
  456. * Casts to String
  457. *
  458. * @api private
  459. */
  460. SchemaString.prototype.cast = function(value, doc, init) {
  461. if (SchemaType._isRef(this, value, doc, init)) {
  462. // wait! we may need to cast this to a document
  463. if (value === null || value === undefined) {
  464. return value;
  465. }
  466. // lazy load
  467. Document || (Document = require('./../document'));
  468. if (value instanceof Document) {
  469. value.$__.wasPopulated = true;
  470. return value;
  471. }
  472. // setting a populated path
  473. if (typeof value === 'string') {
  474. return value;
  475. } else if (Buffer.isBuffer(value) || !utils.isObject(value)) {
  476. throw new CastError('string', value, this.path);
  477. }
  478. // Handle the case where user directly sets a populated
  479. // path to a plain object; cast to the Model used in
  480. // the population query.
  481. const path = doc.$__fullPath(this.path);
  482. const owner = doc.ownerDocument ? doc.ownerDocument() : doc;
  483. const pop = owner.populated(path, true);
  484. const ret = new pop.options[populateModelSymbol](value);
  485. ret.$__.wasPopulated = true;
  486. return ret;
  487. }
  488. const castString = typeof this.constructor.cast === 'function' ?
  489. this.constructor.cast() :
  490. SchemaString.cast();
  491. try {
  492. return castString(value);
  493. } catch (error) {
  494. throw new CastError('string', value, this.path);
  495. }
  496. };
  497. /*!
  498. * ignore
  499. */
  500. function handleSingle(val) {
  501. return this.castForQuery(val);
  502. }
  503. function handleArray(val) {
  504. const _this = this;
  505. if (!Array.isArray(val)) {
  506. return [this.castForQuery(val)];
  507. }
  508. return val.map(function(m) {
  509. return _this.castForQuery(m);
  510. });
  511. }
  512. SchemaString.prototype.$conditionalHandlers =
  513. utils.options(SchemaType.prototype.$conditionalHandlers, {
  514. $all: handleArray,
  515. $gt: handleSingle,
  516. $gte: handleSingle,
  517. $lt: handleSingle,
  518. $lte: handleSingle,
  519. $options: String,
  520. $regex: handleSingle,
  521. $not: handleSingle
  522. });
  523. /**
  524. * Casts contents for queries.
  525. *
  526. * @param {String} $conditional
  527. * @param {any} [val]
  528. * @api private
  529. */
  530. SchemaString.prototype.castForQuery = function($conditional, val) {
  531. let handler;
  532. if (arguments.length === 2) {
  533. handler = this.$conditionalHandlers[$conditional];
  534. if (!handler) {
  535. throw new Error('Can\'t use ' + $conditional + ' with String.');
  536. }
  537. return handler.call(this, val);
  538. }
  539. val = $conditional;
  540. if (Object.prototype.toString.call(val) === '[object RegExp]') {
  541. return val;
  542. }
  543. return this._castForQuery(val);
  544. };
  545. /*!
  546. * Module exports.
  547. */
  548. module.exports = SchemaString;