bmi3-schema-validator
Advanced tools
Comparing version 1.0.3 to 1.1.0
@@ -10,3 +10,4 @@ const typesMap = new Map([ | ||
[ Array, 'an Array' ], | ||
[ NaN, 'a NaN' ] | ||
[ NaN, 'a NaN' ], | ||
[ Function, 'a Function' ] | ||
]); | ||
@@ -23,6 +24,7 @@ | ||
class Validator { | ||
constructor(schema, strict = false) { | ||
constructor(schema, strict = false, def = false) { | ||
this.schema = schema; | ||
this.types = typesMap; | ||
this.strict = strict; | ||
this.default = def; | ||
} | ||
@@ -35,2 +37,17 @@ | ||
/** | ||
* Поле — тип, или что-то сложнее? | ||
* @param {Mixed} property поле | ||
* @return {Boolean} результат проверки типа. Будет true, если тип один из спика, или если он валидатор, или если он конструктор | ||
*/ | ||
isType(property) { | ||
return this.types.has(property) || (typeof property === 'function'); | ||
} | ||
getTypeName(property) { | ||
const name = this.types.get(property); | ||
if (name) return name; | ||
return property.name; | ||
} | ||
/** | ||
* Определяет, является узел схемы плоским или нет | ||
@@ -41,3 +58,3 @@ * @param {Mixed} property узел для провеки | ||
isFlat(property, recursive = false) { | ||
if (this.types.has(property)) { | ||
if (this.isType(property)) { | ||
return { | ||
@@ -53,4 +70,4 @@ required: false, | ||
} | ||
if (property.hasOwnProperty('type') && this.types.has(property.type)) { | ||
return { | ||
if (property.hasOwnProperty('type') && this.isType(property.type)) { | ||
const flat = { | ||
type: property.type, | ||
@@ -63,3 +80,7 @@ required: !!property.required, | ||
exclude: property.exclude || null | ||
}; | ||
} | ||
if (property.hasOwnProperty('default')) { | ||
flat.default = property.default | ||
} | ||
return flat; | ||
} | ||
@@ -152,3 +173,2 @@ if (Array.isArray(property)) { | ||
if (isRequiredExclude) required = isRequiredExclude; | ||
if (required) { | ||
@@ -159,2 +179,6 @@ if (type !== null && type !== undefined) { | ||
} else if (value === undefined) { | ||
if (this.default && options.hasOwnProperty('default')) { | ||
// Это хак, дальше в коде на основе этого мы зададим default в свойство | ||
return false; | ||
} | ||
return true; | ||
@@ -190,5 +214,12 @@ } | ||
if (type === Boolean) return typeof value === 'boolean'; | ||
if (type === Function) return typeof value === 'function'; | ||
if (type === Date) return value instanceof Date; | ||
if (type === Object) return value instanceof Object; | ||
if (type === Array) return Array.isArray(value); | ||
if (typeof type === 'function') { | ||
if (type.isValidator) { | ||
return type(value, level, options); | ||
} | ||
return value instanceof type; | ||
} | ||
// Такие правила | ||
@@ -237,2 +268,10 @@ if (isNaN(type)) return typeof value !== 'number'; | ||
} | ||
if (this.default && !isValid && flat.hasOwnProperty('default')) { | ||
if (typeof flat.default === 'function') { | ||
level[key] = flat.default(); | ||
} else { | ||
level[key] = flat.default; | ||
} | ||
isValid = true; | ||
} | ||
if (!isValid) { | ||
@@ -242,5 +281,5 @@ const isInclude = (flat.include ? ` Required properties: ${flat.include.join(', ')};` : ``); | ||
const isEnum = (flat.enum ? ` Property is enumerable [${flat.enum.join(', ')}];` : ``); | ||
let isRequired = (flat.required ? ` Property is required; Type of property should be ${this.types.get(flat.type)};` : ` Type of property should be ${this.types.get(flat.type)};`); | ||
let isRequired = (flat.required ? ` Property is required; Type of property should be ${this.getTypeName(flat.type)};` : ` Type of property should be ${this.getTypeName(flat.type)};`); | ||
if (Array.isArray(schema)) { | ||
isRequired = (flat.required ? ` Property is required; Type of property should be an Array of ${this.types.get(flat.type)};` : ` Type of property should be an Array of ${this.types.get(flat.type)};`); | ||
isRequired = (flat.required ? ` Property is required; Type of property should be an Array of ${this.getTypeName(flat.type)};` : ` Type of property should be an Array of ${this.getTypeName(flat.type)};`); | ||
} | ||
@@ -247,0 +286,0 @@ const isRequiredInclude = (flat.requiredExclude ? ` Property is required if object level include ${flat.requiredInclude}` : ``); |
{ | ||
"name": "bmi3-schema-validator", | ||
"version": "1.0.3", | ||
"version": "1.1.0", | ||
"description": "Simple validation library for bmi3 interpreter", | ||
@@ -5,0 +5,0 @@ "main": "index.js", |
105
README.md
@@ -100,3 +100,4 @@ # bmi3-schema-validator | ||
[ Array, 'an Array' ], | ||
[ NaN, 'a NaN' ] | ||
[ NaN, 'a NaN' ], | ||
[ Function, 'a Function' ] | ||
]); | ||
@@ -153,6 +154,64 @@ ``` | ||
## Расширение валидации | ||
## Расширение функционала | ||
Если вы хотите добавить дополнительную логику в ваш валидатор, можно просто применить простое наследование: | ||
### Экземпляр класса | ||
Вы можете указать любую функцию-конструктор в качестве типа. По умолчанию валидатор проверит соответствие через `instanceof`, так что для экземпляров наследников конструктора валидация тоже будет проходить: | ||
```javascript | ||
class User { | ||
constructor(name, lastname) { | ||
this.name = name; | ||
this.lastname = lastname; | ||
} | ||
} | ||
const validator = new Validator({ | ||
user: { | ||
type: User, | ||
required: true | ||
}, | ||
orderId: { | ||
type: Number, | ||
required: true | ||
} | ||
}); | ||
validator.validateSync({ | ||
user: new User('Вася', 'Петров'), | ||
orderId: 12 | ||
}); | ||
``` | ||
### Тип-валидатор | ||
Иногда валидировать по тому, является поле наследником класса или нет — недостаточно, тогда можно реализовать тип-валидатор. Это просто функция, для которой мы дополнительно задаем поле `isValidator` как true. | ||
Допустим нам нужно, чтобы у User всегда было задано поле name: | ||
```javascript | ||
function UserType(value, level, options) { | ||
return value instanceof User && value.name && typeof value.name === 'string'; | ||
} | ||
``` | ||
В `level` лежит тот узел, который мы сейчас рассматриваем — т. е. текущее поле в окружении соседних. | ||
```javascript | ||
{ | ||
sublevel: { | ||
age: Number, | ||
gender: { | ||
type: String, | ||
enum: ['male', 'female'] | ||
}, | ||
hair: String | ||
}, | ||
name: String | ||
} | ||
Для `name` это будет `sublevel`, а для `age` — `gender` и `hair`, т. е. это тот объект или подобъект, поле которого сейчас проверяется. | ||
``` | ||
В `options` будут лежать модификаторы, для узла, такие как `required` и т. п. | ||
### Наследование | ||
Допустим этого не достаточно и вы хотите добавить дополнительную логику в валидатор, можно просто применить простое наследование нового валидатора от старого: | ||
```javascript | ||
class MyValidator extends Validator { | ||
@@ -173,3 +232,43 @@ validateSync(level, schema, depth = 0, path = '', buffer = []) { | ||
## Значения по-умолчанию | ||
Третьим параметром в конструктор можно передать флаг «Проверяй модификатор `default` и наполняй проверяемый объект». | ||
Т. е. если поставить его в `true` или записать `validator.default = true` при проверке будет происходить подстановка значений из модификатора `default`, если валидация не прошла или поля вообще нет. | ||
Пример: | ||
```javascript | ||
const validator = Validator({ | ||
name: { | ||
type: String, | ||
default: 'Нет имени' | ||
}, | ||
card: { | ||
type: Number, | ||
default: 12345, | ||
required: true | ||
}, | ||
date: { | ||
type: Date, | ||
default: Date | ||
} | ||
}, false, true) // Третий параметр, поставили validator.default в true | ||
const data = { | ||
card: 1 | ||
} | ||
validator.validateSync(data); | ||
/** | ||
* data.name —> "Нет имени" | ||
* data.number -> 1 | ||
* data.date —> Date() | ||
*/ | ||
``` | ||
Обратите внимание, что если в качестве значения по-умолчанию передать функцию, будет записан её результат | ||
## Лицензия | ||
MIT. Никакие гарантии на библиотеку не распространяются, она предоставляется как есть. |
@@ -59,1 +59,71 @@ /*global test expect */ | ||
}); | ||
const defaultSchema = { | ||
name: { | ||
type: String, | ||
default: 'Петя' | ||
}, | ||
card: { | ||
type: Number, | ||
default: 12345, | ||
required: true | ||
}, | ||
date: { | ||
type: Date, | ||
default: Date | ||
} | ||
} | ||
test('Валиданя с default', () => { | ||
const validator = new Validator(defaultSchema, false, true); | ||
const data = { | ||
card: 1 | ||
} | ||
validator.validateSync(data); | ||
expect(data).toEqual({ | ||
name: 'Петя', | ||
card: 1, | ||
date: Date() | ||
}); | ||
}); | ||
test('Валиданя с default', () => { | ||
const validator = new Validator(defaultSchema, false, true); | ||
const data = {} | ||
validator.validateSync(data); | ||
expect(data).toEqual({ | ||
name: 'Петя', | ||
card: 12345, | ||
date: Date() | ||
}); | ||
}); | ||
test('Валидная схема c типом-классом', () => { | ||
function A() { this.hello = 'world' } | ||
const validator = new Validator({ | ||
a: { | ||
required: true, | ||
type: A | ||
} | ||
}); | ||
expect(validator.validateSync({ a: new A() })).toBe(undefined); | ||
}); | ||
test('Валидная схема c типом-функцией', () => { | ||
function A(prop) { this.hello = prop || 'world' } | ||
function B(value) { | ||
return value && value.hello === 'world' | ||
} | ||
B.isValidator = true; | ||
const validator = new Validator({ | ||
a: { | ||
required: true, | ||
type: B | ||
}, | ||
b: { | ||
required: true, | ||
type: NaN | ||
} | ||
}); | ||
expect(validator.validateSync({ a: new A(), b: 'Привет' })).toBe(undefined); | ||
}); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
31357
491
272