diff --git a/lib/schema/buffer.js b/lib/schema/buffer.js index b1f9e887c5..af28f13d53 100644 --- a/lib/schema/buffer.js +++ b/lib/schema/buffer.js @@ -26,6 +26,7 @@ const CastError = SchemaType.CastError; */ function SchemaBuffer(key, options, _schemaOptions, parentSchema) { + this.enumValues = []; SchemaType.call(this, key, options, 'Buffer', parentSchema); } @@ -336,6 +337,60 @@ SchemaBuffer.prototype.autoEncryptionType = function autoEncryptionType() { return 'binData'; }; +SchemaBuffer.prototype.enum = function(values, message) { + if (this.enumValidator) { + this.validators = this.validators.filter(function(v) { + return v.validator !== this.enumValidator; + }, this); + this.enumValidator = false; + } + + if (values === void 0 || values === false) { + return this; + } + + if (!Array.isArray(values)) { + const isObjectSyntax = utils.isPOJO(values) && values.values != null; + if (isObjectSyntax) { + message = values.message; + values = values.values; + } else if (Buffer.isBuffer(values)) { + values = Array.prototype.slice.call(arguments); + message = null; + } + + if (utils.isPOJO(values)) { + values = Object.values(values); + } + + message = message || 'Buffer enum failed for path `' + this.path + '`'; + } + + message = message == null ? 'Buffer enum failed for path `' + this.path + '`' : message; + + for (const value of values) { + if (value !== undefined) { + this.enumValues.push(this.cast(value)); + } + } + + const vals = this.enumValues; + + this.enumValidator = v => { + if (v == null) return true; + return vals.some(buf => Buffer.compare(buf, v) === 0); + }; + + this.validators.push({ + validator: this.enumValidator, + message: message, + type: 'enum', + enumValues: vals + }); + + return this; +}; + /*! * Module exports. */ diff --git a/test/schema.validation.test.js b/test/schema.validation.test.js index b0e629dfca..27519a53ee 100644 --- a/test/schema.validation.test.js +++ b/test/schema.validation.test.js @@ -147,6 +147,63 @@ describe('schema', function() { await Test.path('status').doValidate(2); }); + it('buffer enum', async function() { + const Test = new Schema({ + buf: { type: Buffer, enum: [Buffer.from('a'), Buffer.from('b'), null] }, + data: { type: Buffer } + }); + + assert.ok(Test.path('buf') instanceof SchemaTypes.Buffer); + const actualBufs = Test.path('buf').enumValues.map(v => v ? Buffer.from(v) : v); + assert.deepEqual(actualBufs, [Buffer.from('a'), Buffer.from('b'), null]); + assert.equal(Test.path('buf').validators.length, 1); + + Test.path('buf').enum(Buffer.from('c'), Buffer.from('d')); + + const updatedBufs = Test.path('buf').enumValues.map(v => v ? Buffer.from(v) : v); + assert.deepEqual( + updatedBufs, + [Buffer.from('a'), Buffer.from('b'), null, Buffer.from('c'), Buffer.from('d')] + ); + + // with SchemaTypes validate method + Test.path('data').enum({ + values: [Buffer.from('x'), Buffer.from('y')], + message: 'enum validator failed for path `{PATH}`: test' + }); + + assert.equal(Test.path('data').validators.length, 1); + const dataBufs = Test.path('data').enumValues.map(v => v ? Buffer.from(v) : v) + assert.deepEqual(dataBufs, [Buffer.from('x'), Buffer.from('y')]); + + await assert.rejects(Test.path('buf').doValidate(Buffer.from('zzz')), ValidatorError); + + // allow unsetting enums + await Test.path('buf').doValidate(undefined); + + await Test.path('buf').doValidate(null); + + await assert.rejects( + Test.path('buf').doValidate(Buffer.from('nope')), + ValidatorError + ); + + await assert.rejects( + Test.path('data').doValidate(Buffer.from('bad')), + err => { + assert.ok(err instanceof ValidatorError); + assert.equal(err.message, + 'enum validator failed for path `data`: test'); + return true; + } + ); + + await Test.path('buf').doValidate(Buffer.from('a')); + await Test.path('buf').doValidate(Buffer.from('b')); + await Test.path('data').doValidate(Buffer.from('x')); + await Test.path('data').doValidate(Buffer.from('y')); + }); + it('string regexp', async function() { const Test = new Schema({ simple: { type: String, match: /[a-z]/ }