buffer.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276
  1. /*!
  2. * Module dependencies.
  3. */
  4. 'use strict';
  5. const MongooseBuffer = require('../types/buffer');
  6. const SchemaBufferOptions = require('../options/SchemaBufferOptions');
  7. const SchemaType = require('../schematype');
  8. const handleBitwiseOperator = require('./operators/bitwise');
  9. const utils = require('../utils');
  10. const populateModelSymbol = require('../helpers/symbols').populateModelSymbol;
  11. const Binary = MongooseBuffer.Binary;
  12. const CastError = SchemaType.CastError;
  13. let Document;
  14. /**
  15. * Buffer SchemaType constructor
  16. *
  17. * @param {String} key
  18. * @param {Object} options
  19. * @inherits SchemaType
  20. * @api public
  21. */
  22. function SchemaBuffer(key, options) {
  23. SchemaType.call(this, key, options, 'Buffer');
  24. }
  25. /**
  26. * This schema type's name, to defend against minifiers that mangle
  27. * function names.
  28. *
  29. * @api public
  30. */
  31. SchemaBuffer.schemaName = 'Buffer';
  32. SchemaBuffer.defaultOptions = {};
  33. /*!
  34. * Inherits from SchemaType.
  35. */
  36. SchemaBuffer.prototype = Object.create(SchemaType.prototype);
  37. SchemaBuffer.prototype.constructor = SchemaBuffer;
  38. SchemaBuffer.prototype.OptionsConstructor = SchemaBufferOptions;
  39. /*!
  40. * ignore
  41. */
  42. SchemaBuffer._checkRequired = v => !!(v && v.length);
  43. /**
  44. * Sets a default option for all Buffer instances.
  45. *
  46. * ####Example:
  47. *
  48. * // Make all buffers have `required` of true by default.
  49. * mongoose.Schema.Buffer.set('required', true);
  50. *
  51. * const User = mongoose.model('User', new Schema({ test: Buffer }));
  52. * new User({ }).validateSync().errors.test.message; // Path `test` is required.
  53. *
  54. * @param {String} option - The option you'd like to set the value for
  55. * @param {*} value - value for option
  56. * @return {undefined}
  57. * @function set
  58. * @static
  59. * @api public
  60. */
  61. SchemaBuffer.set = SchemaType.set;
  62. /**
  63. * Override the function the required validator uses to check whether a string
  64. * passes the `required` check.
  65. *
  66. * ####Example:
  67. *
  68. * // Allow empty strings to pass `required` check
  69. * mongoose.Schema.Types.String.checkRequired(v => v != null);
  70. *
  71. * const M = mongoose.model({ buf: { type: Buffer, required: true } });
  72. * new M({ buf: Buffer.from('') }).validateSync(); // validation passes!
  73. *
  74. * @param {Function} fn
  75. * @return {Function}
  76. * @function checkRequired
  77. * @static
  78. * @api public
  79. */
  80. SchemaBuffer.checkRequired = SchemaType.checkRequired;
  81. /**
  82. * Check if the given value satisfies a required validator. To satisfy a
  83. * required validator, a buffer must not be null or undefined and have
  84. * non-zero length.
  85. *
  86. * @param {Any} value
  87. * @param {Document} doc
  88. * @return {Boolean}
  89. * @api public
  90. */
  91. SchemaBuffer.prototype.checkRequired = function(value, doc) {
  92. if (SchemaType._isRef(this, value, doc, true)) {
  93. return !!value;
  94. }
  95. return this.constructor._checkRequired(value);
  96. };
  97. /**
  98. * Casts contents
  99. *
  100. * @param {Object} value
  101. * @param {Document} doc document that triggers the casting
  102. * @param {Boolean} init
  103. * @api private
  104. */
  105. SchemaBuffer.prototype.cast = function(value, doc, init) {
  106. let ret;
  107. if (SchemaType._isRef(this, value, doc, init)) {
  108. // wait! we may need to cast this to a document
  109. if (value === null || value === undefined) {
  110. return value;
  111. }
  112. // lazy load
  113. Document || (Document = require('./../document'));
  114. if (value instanceof Document) {
  115. value.$__.wasPopulated = true;
  116. return value;
  117. }
  118. // setting a populated path
  119. if (Buffer.isBuffer(value)) {
  120. return value;
  121. } else if (!utils.isObject(value)) {
  122. throw new CastError('Buffer', value, this.path, null, this);
  123. }
  124. // Handle the case where user directly sets a populated
  125. // path to a plain object; cast to the Model used in
  126. // the population query.
  127. const path = doc.$__fullPath(this.path);
  128. const owner = doc.ownerDocument ? doc.ownerDocument() : doc;
  129. const pop = owner.populated(path, true);
  130. ret = new pop.options[populateModelSymbol](value);
  131. ret.$__.wasPopulated = true;
  132. return ret;
  133. }
  134. // documents
  135. if (value && value._id) {
  136. value = value._id;
  137. }
  138. if (value && value.isMongooseBuffer) {
  139. return value;
  140. }
  141. if (Buffer.isBuffer(value)) {
  142. if (!value || !value.isMongooseBuffer) {
  143. value = new MongooseBuffer(value, [this.path, doc]);
  144. if (this.options.subtype != null) {
  145. value._subtype = this.options.subtype;
  146. }
  147. }
  148. return value;
  149. }
  150. if (value instanceof Binary) {
  151. ret = new MongooseBuffer(value.value(true), [this.path, doc]);
  152. if (typeof value.sub_type !== 'number') {
  153. throw new CastError('Buffer', value, this.path, null, this);
  154. }
  155. ret._subtype = value.sub_type;
  156. return ret;
  157. }
  158. if (value === null) {
  159. return value;
  160. }
  161. const type = typeof value;
  162. if (
  163. type === 'string' || type === 'number' || Array.isArray(value) ||
  164. (type === 'object' && value.type === 'Buffer' && Array.isArray(value.data)) // gh-6863
  165. ) {
  166. if (type === 'number') {
  167. value = [value];
  168. }
  169. ret = new MongooseBuffer(value, [this.path, doc]);
  170. if (this.options.subtype != null) {
  171. ret._subtype = this.options.subtype;
  172. }
  173. return ret;
  174. }
  175. throw new CastError('Buffer', value, this.path, null, this);
  176. };
  177. /**
  178. * Sets the default [subtype](https://studio3t.com/whats-new/best-practices-uuid-mongodb/)
  179. * for this buffer. You can find a [list of allowed subtypes here](http://api.mongodb.com/python/current/api/bson/binary.html).
  180. *
  181. * ####Example:
  182. *
  183. * var s = new Schema({ uuid: { type: Buffer, subtype: 4 });
  184. * var M = db.model('M', s);
  185. * var m = new M({ uuid: 'test string' });
  186. * m.uuid._subtype; // 4
  187. *
  188. * @param {Number} subtype the default subtype
  189. * @return {SchemaType} this
  190. * @api public
  191. */
  192. SchemaBuffer.prototype.subtype = function(subtype) {
  193. this.options.subtype = subtype;
  194. return this;
  195. };
  196. /*!
  197. * ignore
  198. */
  199. function handleSingle(val) {
  200. return this.castForQuery(val);
  201. }
  202. SchemaBuffer.prototype.$conditionalHandlers =
  203. utils.options(SchemaType.prototype.$conditionalHandlers, {
  204. $bitsAllClear: handleBitwiseOperator,
  205. $bitsAnyClear: handleBitwiseOperator,
  206. $bitsAllSet: handleBitwiseOperator,
  207. $bitsAnySet: handleBitwiseOperator,
  208. $gt: handleSingle,
  209. $gte: handleSingle,
  210. $lt: handleSingle,
  211. $lte: handleSingle
  212. });
  213. /**
  214. * Casts contents for queries.
  215. *
  216. * @param {String} $conditional
  217. * @param {any} [value]
  218. * @api private
  219. */
  220. SchemaBuffer.prototype.castForQuery = function($conditional, val) {
  221. let handler;
  222. if (arguments.length === 2) {
  223. handler = this.$conditionalHandlers[$conditional];
  224. if (!handler) {
  225. throw new Error('Can\'t use ' + $conditional + ' with Buffer.');
  226. }
  227. return handler.call(this, val);
  228. }
  229. val = $conditional;
  230. const casted = this._castForQuery(val);
  231. return casted ? casted.toObject({ transform: false, virtuals: false }) : casted;
  232. };
  233. /*!
  234. * Module exports.
  235. */
  236. module.exports = SchemaBuffer;