Просмотр исходного кода

chore: do not validate an empty values through property validators

e22m4u 1 год назад
Родитель
Сommit
71ed4959ab

+ 3 - 2
README.md

@@ -302,8 +302,9 @@ schema.defineModel({
 ## Валидаторы
 ## Валидаторы
 
 
 Кроме проверки типа, дополнительные условия можно задать с помощью
 Кроме проверки типа, дополнительные условия можно задать с помощью
-валидаторов, через которые значение свойства будет проходить перед
-записью в базу.
+валидаторов, через которые будет проходить значение свойства перед
+записью в базу. Исключением являются [пустые значения](#Пустое-значение),
+которые не проходят проверку.
 
 
 - `minLength: number` минимальная длинна строки или массива
 - `minLength: number` минимальная длинна строки или массива
 - `maxLength: number` максимальная длинна строки или массива
 - `maxLength: number` максимальная длинна строки или массива

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
docs/index.html


+ 13 - 10
src/definition/model/model-data-validator.js

@@ -53,15 +53,6 @@ export class ModelDataValidator extends Service {
    * @returns {undefined}
    * @returns {undefined}
    */
    */
   _validatePropertyValue(modelName, propName, propDef, propValue) {
   _validatePropertyValue(modelName, propName, propDef, propValue) {
-    // property validators
-    this._validateValueByPropertyValidators(
-      modelName,
-      propName,
-      propDef,
-      propValue,
-    );
-    // a required property should
-    // not have an empty property
     const propType =
     const propType =
       this.getService(ModelDefinitionUtils).getDataTypeFromPropertyDefinition(
       this.getService(ModelDefinitionUtils).getDataTypeFromPropertyDefinition(
         propDef,
         propDef,
@@ -71,9 +62,13 @@ export class ModelDataValidator extends Service {
       propValue,
       propValue,
     );
     );
     if (isEmpty) {
     if (isEmpty) {
+      // skips validation
+      // for an empty value
       const isRequired =
       const isRequired =
         typeof propDef === 'string' ? false : Boolean(propDef.required);
         typeof propDef === 'string' ? false : Boolean(propDef.required);
       if (!isRequired) return;
       if (!isRequired) return;
+      // a required property should
+      // not have an empty value
       throw new InvalidArgumentError(
       throw new InvalidArgumentError(
         'The property %v of the model %v is required, but %v given.',
         'The property %v of the model %v is required, but %v given.',
         propName,
         propName,
@@ -81,7 +76,15 @@ export class ModelDataValidator extends Service {
         propValue,
         propValue,
       );
       );
     }
     }
-    // property type
+    // passes the property value
+    // through property validators
+    this._validateValueByPropertyValidators(
+      modelName,
+      propName,
+      propDef,
+      propValue,
+    );
+    // type checking of the property value
     this._validateValueByPropertyType(modelName, propName, propDef, propValue);
     this._validateValueByPropertyType(modelName, propName, propDef, propValue);
   }
   }
 
 

+ 74 - 50
src/definition/model/model-data-validator.spec.js

@@ -192,9 +192,47 @@ describe('ModelDataValidator', function () {
           'The property "foo" of the model "model" is required, but null given.',
           'The property "foo" of the model "model" is required, but null given.',
         );
         );
       });
       });
+
+      it('throws an error if a required property has an empty value', function () {
+        const schema = new Schema();
+        schema.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+              required: true,
+            },
+          },
+        });
+        schema
+          .getService(EmptyValuesDefiner)
+          .setEmptyValuesOf(DataType.STRING, [5]);
+        const throwable = () =>
+          schema
+            .getService(ModelDataValidator)
+            .validate('model', {foo: 5}, true);
+        expect(throwable).to.throw(
+          'The property "foo" of the model "model" is required, but 5 given.',
+        );
+      });
     });
     });
 
 
     describe('validate by property type', function () {
     describe('validate by property type', function () {
+      it('skips validation for an empty value', function () {
+        const S = new Schema();
+        S.defineModel({
+          name: 'model',
+          datasource: 'datasource',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+            },
+          },
+        });
+        S.getService(EmptyValuesDefiner).setEmptyValuesOf(DataType.STRING, [5]);
+        S.getService(ModelDataValidator).validate('model', {foo: 5});
+      });
+
       describe('DataType.ANY', function () {
       describe('DataType.ANY', function () {
         describe('ShortPropertyDefinition', function () {
         describe('ShortPropertyDefinition', function () {
           it('does not throw an error if an undefined given', function () {
           it('does not throw an error if an undefined given', function () {
@@ -2021,8 +2059,27 @@ describe('ModelDataValidator', function () {
     });
     });
 
 
     describe('validate by property validators', function () {
     describe('validate by property validators', function () {
+      it('skips validation for an empty value', function () {
+        const S = new Schema();
+        S.getService(PropertyValidatorRegistry).addValidator(
+          'myValidator',
+          () => false,
+        );
+        S.defineModel({
+          name: 'model',
+          properties: {
+            foo: {
+              type: DataType.STRING,
+              validate: 'myValidator',
+            },
+          },
+        });
+        S.getService(EmptyValuesDefiner).setEmptyValuesOf(DataType.STRING, [5]);
+        S.getService(ModelDataValidator).validate('model', {foo: 5});
+      });
+
       describe('the option "validate" with the string value', function () {
       describe('the option "validate" with the string value', function () {
-        it('validates a property value even is not provided', function () {
+        it('does not validate a property value if it is not provided', function () {
           const S = new Schema();
           const S = new Schema();
           S.getService(PropertyValidatorRegistry).addValidator(
           S.getService(PropertyValidatorRegistry).addValidator(
             'myValidator',
             'myValidator',
@@ -2038,14 +2095,10 @@ describe('ModelDataValidator', function () {
             },
             },
           });
           });
           const validator = S.getService(ModelDataValidator);
           const validator = S.getService(ModelDataValidator);
-          const throwable = () => validator.validate('model', {});
-          expect(throwable).to.throw(
-            'The property "foo" of the model "model" has an invalid value undefined ' +
-              'that caught by the validator "myValidator".',
-          );
+          validator.validate('model', {});
         });
         });
 
 
-        it('validates undefined and null values', function () {
+        it('does not validate undefined and null values', function () {
           const S = new Schema();
           const S = new Schema();
           S.getService(PropertyValidatorRegistry).addValidator(
           S.getService(PropertyValidatorRegistry).addValidator(
             'myValidator',
             'myValidator',
@@ -2061,15 +2114,8 @@ describe('ModelDataValidator', function () {
             },
             },
           });
           });
           const validator = S.getService(ModelDataValidator);
           const validator = S.getService(ModelDataValidator);
-          const throwable = v => () => validator.validate('model', {foo: v});
-          const error = v =>
-            format(
-              'The property "foo" of the model "model" has an invalid value %s ' +
-                'that caught by the validator "myValidator".',
-              v,
-            );
-          expect(throwable(undefined)).to.throw(error('undefined'));
-          expect(throwable(null)).to.throw(error('null'));
+          validator.validate('model', {foo: undefined});
+          validator.validate('model', {foo: null});
         });
         });
 
 
         it('throws an error from the validator', function () {
         it('throws an error from the validator', function () {
@@ -2256,7 +2302,7 @@ describe('ModelDataValidator', function () {
           });
           });
         });
         });
 
 
-        it('validates a property value even is not provided', function () {
+        it('does not validate a property value if it is not provided', function () {
           const S = new Schema();
           const S = new Schema();
           S.getService(PropertyValidatorRegistry).addValidator(
           S.getService(PropertyValidatorRegistry).addValidator(
             'myValidator',
             'myValidator',
@@ -2272,14 +2318,10 @@ describe('ModelDataValidator', function () {
             },
             },
           });
           });
           const validator = S.getService(ModelDataValidator);
           const validator = S.getService(ModelDataValidator);
-          const throwable = () => validator.validate('model', {});
-          expect(throwable).to.throw(
-            'The property "foo" of the model "model" has an invalid value undefined ' +
-              'that caught by the validator "myValidator".',
-          );
+          validator.validate('model', {});
         });
         });
 
 
-        it('validates undefined and null values', function () {
+        it('does not validate undefined and null values', function () {
           const S = new Schema();
           const S = new Schema();
           S.getService(PropertyValidatorRegistry).addValidator(
           S.getService(PropertyValidatorRegistry).addValidator(
             'myValidator',
             'myValidator',
@@ -2295,15 +2337,8 @@ describe('ModelDataValidator', function () {
             },
             },
           });
           });
           const validator = S.getService(ModelDataValidator);
           const validator = S.getService(ModelDataValidator);
-          const throwable = v => () => validator.validate('model', {foo: v});
-          const error = v =>
-            format(
-              'The property "foo" of the model "model" has an invalid value %s ' +
-                'that caught by the validator "myValidator".',
-              v,
-            );
-          expect(throwable(undefined)).to.throw(error('undefined'));
-          expect(throwable(null)).to.throw(error('null'));
+          validator.validate('model', {foo: undefined});
+          validator.validate('model', {foo: null});
         });
         });
 
 
         it('throws an error from the validator', function () {
         it('throws an error from the validator', function () {
@@ -2491,7 +2526,7 @@ describe('ModelDataValidator', function () {
           });
           });
         });
         });
 
 
-        it('validates a property value even is not provided', function () {
+        it('does not validate a property value if it is not provided', function () {
           const S = new Schema();
           const S = new Schema();
           S.getService(PropertyValidatorRegistry).addValidator(
           S.getService(PropertyValidatorRegistry).addValidator(
             'myValidator',
             'myValidator',
@@ -2509,14 +2544,10 @@ describe('ModelDataValidator', function () {
             },
             },
           });
           });
           const validator = S.getService(ModelDataValidator);
           const validator = S.getService(ModelDataValidator);
-          const throwable = () => validator.validate('model', {});
-          expect(throwable).to.throw(
-            'The property "foo" of the model "model" has an invalid value undefined ' +
-              'that caught by the validator "myValidator".',
-          );
+          validator.validate('model', {});
         });
         });
 
 
-        it('validates undefined and null values', function () {
+        it('does not validate undefined and null values', function () {
           const S = new Schema();
           const S = new Schema();
           S.getService(PropertyValidatorRegistry).addValidator(
           S.getService(PropertyValidatorRegistry).addValidator(
             'myValidator',
             'myValidator',
@@ -2534,15 +2565,8 @@ describe('ModelDataValidator', function () {
             },
             },
           });
           });
           const validator = S.getService(ModelDataValidator);
           const validator = S.getService(ModelDataValidator);
-          const throwable = v => () => validator.validate('model', {foo: v});
-          const error = v =>
-            format(
-              'The property "foo" of the model "model" has an invalid value %s ' +
-                'that caught by the validator "myValidator".',
-              v,
-            );
-          expect(throwable(undefined)).to.throw(error('undefined'));
-          expect(throwable(null)).to.throw(error('null'));
+          validator.validate('model', {foo: undefined});
+          validator.validate('model', {foo: null});
         });
         });
 
 
         it('throws an error from the validator', function () {
         it('throws an error from the validator', function () {
@@ -2771,7 +2795,7 @@ describe('ModelDataValidator', function () {
           name: 'model',
           name: 'model',
           properties: {
           properties: {
             foo: {
             foo: {
-              type: DataType.STRING,
+              type: DataType.ANY,
               validate: undefined,
               validate: undefined,
             },
             },
           },
           },
@@ -2780,7 +2804,7 @@ describe('ModelDataValidator', function () {
         const throwable = v => () => {
         const throwable = v => () => {
           const models = schema.getService(DefinitionRegistry)['_models'];
           const models = schema.getService(DefinitionRegistry)['_models'];
           models.model.properties.foo.validate = v;
           models.model.properties.foo.validate = v;
-          V.validate('model', {});
+          V.validate('model', {foo: 'bar'});
         };
         };
         const error = v =>
         const error = v =>
           format(
           format(

+ 1 - 1
src/definition/model/properties/property-validator/builtin/max-length-validator.js

@@ -9,7 +9,7 @@ import {InvalidArgumentError} from '../../../../../errors/index.js';
  * @returns {boolean}
  * @returns {boolean}
  */
  */
 export function maxLengthValidator(value, options, context) {
 export function maxLengthValidator(value, options, context) {
-  if (value == null || options === false) return true;
+  if (options === false) return true;
   if (typeof options !== 'number')
   if (typeof options !== 'number')
     throw new InvalidArgumentError(
     throw new InvalidArgumentError(
       'The validator %v requires the "options" argument ' +
       'The validator %v requires the "options" argument ' +

+ 2 - 9
src/definition/model/properties/property-validator/builtin/max-length-validator.spec.js

@@ -8,13 +8,6 @@ describe('maxLengthValidator', function () {
     expect(res).to.be.true;
     expect(res).to.be.true;
   });
   });
 
 
-  it('returns true for undefined and null values', function () {
-    const res1 = maxLengthValidator(undefined, 10, {});
-    const res2 = maxLengthValidator(null, 10, {});
-    expect(res1).to.be.true;
-    expect(res2).to.be.true;
-  });
-
   it('requires the "value" argument as a String or an Array', function () {
   it('requires the "value" argument as a String or an Array', function () {
     const throwable = v => () =>
     const throwable = v => () =>
       maxLengthValidator(v, 10, {
       maxLengthValidator(v, 10, {
@@ -30,14 +23,14 @@ describe('maxLengthValidator', function () {
     expect(throwable(0)).to.throw(error('0'));
     expect(throwable(0)).to.throw(error('0'));
     expect(throwable(true)).to.throw(error('true'));
     expect(throwable(true)).to.throw(error('true'));
     expect(throwable(false)).to.throw(error('false'));
     expect(throwable(false)).to.throw(error('false'));
+    expect(throwable(undefined)).to.throw(error('undefined'));
+    expect(throwable(null)).to.throw(error('null'));
     expect(throwable({})).to.throw(error('Object'));
     expect(throwable({})).to.throw(error('Object'));
     expect(throwable(() => undefined)).to.throw(error('Function'));
     expect(throwable(() => undefined)).to.throw(error('Function'));
     throwable('str')();
     throwable('str')();
     throwable('')();
     throwable('')();
     throwable([1, 2, 3])();
     throwable([1, 2, 3])();
     throwable([])();
     throwable([])();
-    throwable(undefined)();
-    throwable(null)();
   });
   });
 
 
   it('requires the "options" argument to be a number', function () {
   it('requires the "options" argument to be a number', function () {

+ 1 - 1
src/definition/model/properties/property-validator/builtin/min-length-validator.js

@@ -9,7 +9,7 @@ import {InvalidArgumentError} from '../../../../../errors/index.js';
  * @returns {boolean}
  * @returns {boolean}
  */
  */
 export function minLengthValidator(value, options, context) {
 export function minLengthValidator(value, options, context) {
-  if (value == null || options === false) return true;
+  if (options === false) return true;
   if (typeof options !== 'number')
   if (typeof options !== 'number')
     throw new InvalidArgumentError(
     throw new InvalidArgumentError(
       'The validator %v requires the "options" argument ' +
       'The validator %v requires the "options" argument ' +

+ 2 - 9
src/definition/model/properties/property-validator/builtin/min-length-validator.spec.js

@@ -8,13 +8,6 @@ describe('minLengthValidator', function () {
     expect(res).to.be.true;
     expect(res).to.be.true;
   });
   });
 
 
-  it('returns true for undefined and null values', function () {
-    const res1 = minLengthValidator(undefined, 10, {});
-    const res2 = minLengthValidator(null, 10, {});
-    expect(res1).to.be.true;
-    expect(res2).to.be.true;
-  });
-
   it('requires the "value" argument as a String or an Array', function () {
   it('requires the "value" argument as a String or an Array', function () {
     const throwable = v => () =>
     const throwable = v => () =>
       minLengthValidator(v, 0, {
       minLengthValidator(v, 0, {
@@ -30,14 +23,14 @@ describe('minLengthValidator', function () {
     expect(throwable(0)).to.throw(error('0'));
     expect(throwable(0)).to.throw(error('0'));
     expect(throwable(true)).to.throw(error('true'));
     expect(throwable(true)).to.throw(error('true'));
     expect(throwable(false)).to.throw(error('false'));
     expect(throwable(false)).to.throw(error('false'));
+    expect(throwable(undefined)).to.throw(error('undefined'));
+    expect(throwable(null)).to.throw(error('null'));
     expect(throwable({})).to.throw(error('Object'));
     expect(throwable({})).to.throw(error('Object'));
     expect(throwable(() => undefined)).to.throw(error('Function'));
     expect(throwable(() => undefined)).to.throw(error('Function'));
     throwable('str')();
     throwable('str')();
     throwable('')();
     throwable('')();
     throwable([1, 2, 3])();
     throwable([1, 2, 3])();
     throwable([])();
     throwable([])();
-    throwable(undefined)();
-    throwable(null)();
   });
   });
 
 
   it('requires the "options" argument to be a number', function () {
   it('requires the "options" argument to be a number', function () {

+ 1 - 1
src/definition/model/properties/property-validator/builtin/regexp-validator.js

@@ -10,7 +10,7 @@ import {InvalidArgumentError} from '../../../../../errors/index.js';
  * @returns {boolean}
  * @returns {boolean}
  */
  */
 export function regexpValidator(value, options, context) {
 export function regexpValidator(value, options, context) {
-  if (value == null || options === false) return true;
+  if (options === false) return true;
   if (typeof options !== 'string' && !(options instanceof RegExp))
   if (typeof options !== 'string' && !(options instanceof RegExp))
     throw new InvalidArgumentError(
     throw new InvalidArgumentError(
       'The validator %v requires the "options" argument ' +
       'The validator %v requires the "options" argument ' +

+ 2 - 9
src/definition/model/properties/property-validator/builtin/regexp-validator.spec.js

@@ -8,13 +8,6 @@ describe('regexpValidator', function () {
     expect(res).to.be.true;
     expect(res).to.be.true;
   });
   });
 
 
-  it('returns true for undefined and null values', function () {
-    const res1 = regexpValidator(undefined, '.*', {});
-    const res2 = regexpValidator(null, '.*', {});
-    expect(res1).to.be.true;
-    expect(res2).to.be.true;
-  });
-
   it('requires the "value" argument to be a string', function () {
   it('requires the "value" argument to be a string', function () {
     const throwable = v => () =>
     const throwable = v => () =>
       regexpValidator(v, '.*', {
       regexpValidator(v, '.*', {
@@ -28,13 +21,13 @@ describe('regexpValidator', function () {
       );
       );
     expect(throwable(true)).to.throw(error('true'));
     expect(throwable(true)).to.throw(error('true'));
     expect(throwable(false)).to.throw(error('false'));
     expect(throwable(false)).to.throw(error('false'));
+    expect(throwable(undefined)).to.throw(error('undefined'));
+    expect(throwable(null)).to.throw(error('null'));
     expect(throwable({})).to.throw(error('Object'));
     expect(throwable({})).to.throw(error('Object'));
     expect(throwable([])).to.throw(error('Array'));
     expect(throwable([])).to.throw(error('Array'));
     expect(throwable(() => undefined)).to.throw(error('Function'));
     expect(throwable(() => undefined)).to.throw(error('Function'));
     throwable('str')();
     throwable('str')();
     throwable('')();
     throwable('')();
-    throwable(undefined)();
-    throwable(null)();
   });
   });
 
 
   it('requires the "options" argument to be a string or RegExp', function () {
   it('requires the "options" argument to be a string or RegExp', function () {

Некоторые файлы не были показаны из-за большого количества измененных файлов