UNPKG

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