From cf62382207607e3191e3f58e0744c24217c1c0d0 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 13 Mar 2024 13:52:22 -0400 Subject: [PATCH 1/5] write test --- lib/schemaType.js | 6 ++++++ test/schematype.test.js | 33 +++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/lib/schemaType.js b/lib/schemaType.js index e3caa4ff03..c4c922372c 100644 --- a/lib/schemaType.js +++ b/lib/schemaType.js @@ -803,6 +803,12 @@ SchemaType.prototype.get = function(fn) { return this; }; +SchemaType.prototype.validateAll = function(approvalSeekers) { + for (let i = 0; i < approvalSeekers.length; i++) { + this.validate(approvalSeekers[i]); + } +} + /** * Adds validator(s) for this document path. * diff --git a/test/schematype.test.js b/test/schematype.test.js index 582a135c09..f4186ed038 100644 --- a/test/schematype.test.js +++ b/test/schematype.test.js @@ -281,4 +281,37 @@ describe('schematype', function() { }); }); }); + it('demonstrates the `validateAll()` function (gh-6910)', async function() { + const m = new mongoose.Mongoose(); + await m.connect(start.uri); + const validateSchema = new m.Schema({ name: String, password: String }); + validateSchema.path('name').validate({ + validator: function(v) { + return v.length > 5; + }, + message: 'name must be longer than 5 characters' + }) + validateSchema.path('password').validateAll([ + { + validator: function(v) { + return this.name == v; + }, + message: `password must not equal name` + }, + { + validator: function(v) { + return v.length > 5 + }, + message: `password must be at least six characters` + } + ]); + + const Test = m.model('Test', validateSchema); + await Test.deleteMany({}); + const check = new Test(); + check.name = 'Test'; + check.password = 'test'; + const test = check.validateSync(); + assert.ok(true); + }); }); From 82aee6673a73c7700475b6f55b5a0b613bad5940 Mon Sep 17 00:00:00 2001 From: Daniel Diaz <39510674+IslandRhythms@users.noreply.github.com> Date: Wed, 13 Mar 2024 14:40:11 -0400 Subject: [PATCH 2/5] feat: `validateAll` function on `SchemaType` --- lib/schemaType.js | 2 +- test/schematype.test.js | 24 ++++++++---------------- 2 files changed, 9 insertions(+), 17 deletions(-) diff --git a/lib/schemaType.js b/lib/schemaType.js index c4c922372c..f21a8406df 100644 --- a/lib/schemaType.js +++ b/lib/schemaType.js @@ -807,7 +807,7 @@ SchemaType.prototype.validateAll = function(approvalSeekers) { for (let i = 0; i < approvalSeekers.length; i++) { this.validate(approvalSeekers[i]); } -} +}; /** * Adds validator(s) for this document path. diff --git a/test/schematype.test.js b/test/schematype.test.js index f4186ed038..e0e2339dd9 100644 --- a/test/schematype.test.js +++ b/test/schematype.test.js @@ -282,36 +282,28 @@ describe('schematype', function() { }); }); it('demonstrates the `validateAll()` function (gh-6910)', async function() { - const m = new mongoose.Mongoose(); - await m.connect(start.uri); - const validateSchema = new m.Schema({ name: String, password: String }); + + const validateSchema = new Schema({ name: String, password: String }); validateSchema.path('name').validate({ validator: function(v) { return v.length > 5; }, message: 'name must be longer than 5 characters' - }) + }); validateSchema.path('password').validateAll([ - { + { validator: function(v) { return this.name == v; }, - message: `password must not equal name` + message: 'password must not equal name' }, { validator: function(v) { - return v.length > 5 + return v.length > 5; }, - message: `password must be at least six characters` + message: 'password must be at least six characters' } ]); - - const Test = m.model('Test', validateSchema); - await Test.deleteMany({}); - const check = new Test(); - check.name = 'Test'; - check.password = 'test'; - const test = check.validateSync(); - assert.ok(true); + assert.equal(validateSchema.path('password').validators.length, 2); }); }); From cab97cca5c0e61b2ae14e5a75c6caa165f35ae6f Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 14 Mar 2024 14:41:57 -0400 Subject: [PATCH 3/5] types+docs(schematype): add types and jsdoc for SchemaType.prototype.validateAll() --- lib/schemaType.js | 19 +++++++++++++++---- test/schematype.test.js | 13 +++++++++++-- types/schematypes.d.ts | 13 ++++++++++--- 3 files changed, 36 insertions(+), 9 deletions(-) diff --git a/lib/schemaType.js b/lib/schemaType.js index f21a8406df..74d713795d 100644 --- a/lib/schemaType.js +++ b/lib/schemaType.js @@ -803,10 +803,19 @@ SchemaType.prototype.get = function(fn) { return this; }; -SchemaType.prototype.validateAll = function(approvalSeekers) { - for (let i = 0; i < approvalSeekers.length; i++) { - this.validate(approvalSeekers[i]); +/** + * Adds multiple validators for this document path. + * Calls `validate()` for every element in validators. + * + * @param {Array} validators + * @returns this + */ + +SchemaType.prototype.validateAll = function(validators) { + for (let i = 0; i < validators.length; i++) { + this.validate(validators[i]); } + return this; }; /** @@ -1291,6 +1300,9 @@ SchemaType.prototype.select = function select(val) { SchemaType.prototype.doValidate = function(value, fn, scope, options) { let err = false; const path = this.path; + if (typeof fn !== 'function') { + throw new TypeError(`Must pass callback function to doValidate(), got ${typeof fn}`); + } // Avoid non-object `validators` const validators = this.validators. @@ -1425,7 +1437,6 @@ SchemaType.prototype.doValidateSync = function(value, scope, options) { let i = 0; const len = validators.length; for (i = 0; i < len; ++i) { - const v = validators[i]; if (v === null || typeof v !== 'object') { diff --git a/test/schematype.test.js b/test/schematype.test.js index e0e2339dd9..b2b0ba5d89 100644 --- a/test/schematype.test.js +++ b/test/schematype.test.js @@ -282,7 +282,6 @@ describe('schematype', function() { }); }); it('demonstrates the `validateAll()` function (gh-6910)', async function() { - const validateSchema = new Schema({ name: String, password: String }); validateSchema.path('name').validate({ validator: function(v) { @@ -293,7 +292,7 @@ describe('schematype', function() { validateSchema.path('password').validateAll([ { validator: function(v) { - return this.name == v; + return this.name !== v; }, message: 'password must not equal name' }, @@ -305,5 +304,15 @@ describe('schematype', function() { } ]); assert.equal(validateSchema.path('password').validators.length, 2); + + const passwordPath = validateSchema.path('password'); + assert.throws( + () => { throw passwordPath.doValidateSync('john', { name: 'john' }); }, + /password must not equal name/ + ); + assert.throws( + () => { throw passwordPath.doValidateSync('short', { name: 'john' }); }, + /password must be at least six characters/ + ); }); }); diff --git a/types/schematypes.d.ts b/types/schematypes.d.ts index 088bc27c59..656fcdd30c 100644 --- a/types/schematypes.d.ts +++ b/types/schematypes.d.ts @@ -192,10 +192,14 @@ declare module 'mongoose' { [other: string]: any; } - interface Validator { - message?: string; type?: string; validator?: Function + interface Validator { + message?: string; + type?: string; + validator?: ValidatorFunction; } + type ValidatorFunction = (this: DocType, value: any, validatorProperties?: Validator) => any; + class SchemaType { /** SchemaType constructor */ constructor(path: string, options?: AnyObject, instance?: string); @@ -281,7 +285,10 @@ declare module 'mongoose' { validators: Validator[]; /** Adds validator(s) for this document path. */ - validate(obj: RegExp | ((this: DocType, value: any, validatorProperties?: Validator) => any), errorMsg?: string, type?: string): this; + validate(obj: RegExp | ValidatorFunction | Validator, errorMsg?: string, type?: string): this; + + /** Adds multiple validators for this document path. */ + validateAll(validators: Array): this; /** Default options for this SchemaType */ defaultOptions?: Record; From 55774c6da9fb93944396fdfc3e018bbe6e560e36 Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 14 Mar 2024 14:42:18 -0400 Subject: [PATCH 4/5] Update test/schematype.test.js Co-authored-by: hasezoey --- test/schematype.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/schematype.test.js b/test/schematype.test.js index b2b0ba5d89..ad8367d0f6 100644 --- a/test/schematype.test.js +++ b/test/schematype.test.js @@ -281,7 +281,7 @@ describe('schematype', function() { }); }); }); - it('demonstrates the `validateAll()` function (gh-6910)', async function() { + it('demonstrates the `validateAll()` function (gh-6910)', function() { const validateSchema = new Schema({ name: String, password: String }); validateSchema.path('name').validate({ validator: function(v) { From 399531b0feac66aba6e0fb6defeed317089451af Mon Sep 17 00:00:00 2001 From: Valeri Karpov Date: Thu, 14 Mar 2024 14:54:57 -0400 Subject: [PATCH 5/5] types: propagate DocType down to validators for validate() and validateAll() --- types/schematypes.d.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/types/schematypes.d.ts b/types/schematypes.d.ts index 656fcdd30c..9cb4834300 100644 --- a/types/schematypes.d.ts +++ b/types/schematypes.d.ts @@ -285,10 +285,10 @@ declare module 'mongoose' { validators: Validator[]; /** Adds validator(s) for this document path. */ - validate(obj: RegExp | ValidatorFunction | Validator, errorMsg?: string, type?: string): this; + validate(obj: RegExp | ValidatorFunction | Validator, errorMsg?: string, type?: string): this; /** Adds multiple validators for this document path. */ - validateAll(validators: Array): this; + validateAll(validators: Array | Validator>): this; /** Default options for this SchemaType */ defaultOptions?: Record;