Json-schema Validator Utilities
Json-schema is a declaration of document type in a json format. Basic usage of a json schema is validation, but also it might bring a clear picture of protocols used in an application.
What are reasons to use json-schema?
- Validation (type checking)
- Protocols (database & API description)
Anything else? Lots of things, but this package is about functional programming and awesome techniques, that can be achieved with json-schema usage:
- Use any json-schema validator
- Expose native environment API
// shorthands for native API
- jvu.add('', schema) or env.addSchema
- jvu('', object) or
validate/is
// new API
- jvu.not('', object)
- jvu.match({ '': value })
- jvu.find([''])
- jvu.filter([''])
- validator - a json-schema validator used for internal tasks
- validation function - a generated by path or schema function which checks any given argument among with a schema and returns isValid value
List of supported validators
Adapter required
The main protocol required by this package is to have addSchema and validate methods inside a created environment. For special cases adapter functions can be defined.
Examples of adapters can be found in a support.spec.js.
Lets have an example schema
jsonSchema = {
"common": {
"properties": {
"type": {
"enum": ["common"]
}
},
"required": [
"type"
]
}
};
It defines a common object type, which has a type property with a single value common.
{ type : 'common' } // => valid by '#/common' path
Utils will create an empty environment with a given validator.
jvu = require('jvu')(validator);
In order to support different json-schema validators api the second argument in jvu constructor provides a wrapper function, which is used as an adapter for environment.
jvu = require('jvu')(validator, validator => ({
validate: nil =>
nil ? validator.customValidate('any', nil) : false
}))
Validator environment is available with a jvu.env link. However, created jvu environment is extended with validator environment, so you can use it if it does not intercept. This is done for the reason to decrease API changes needed to integrate jvu to existing code.
Use add to add json schema after initialization
jvu.add('', jsonSchema);
Use validate/is to check an object by schema reference.
jvu('', ?) is a short notation for validate/is.
jvu.validate('#/common', { type: 'common' }) // => true
jvu.is('#/other', { type: 'common' }) // => false
jvu('#/other', { type: 'common' }) // => false
When it's called without a second argument - it returns a validation function, which can be used in further calculations.
const validate = jvu('#/common');
validate({ type: 'common' }) // => true
The generated validation function accepts object as a param and returns the isValid flag.
- one argument - partial execution,
- two arguments - returns a value.
var testCommon = jvu('#/common');
[commonObj].map(testCommon) // => [true]
var testNotCommon = jvu('#/common', true);
[commonObj].map(testNotCommon) // => [false]
Partial execution is very helpful in each, find and other iterable operations.
Same functionality as jvu.is but with an opposite meaning.
Validation function returns the isNotValid flag.
jvu.not('#/common', { type: 'common' }) // => false
jvu.not('#/other', { type: 'common' }) // => true
Use match as a Matching pattern.
jvu.match({ '#/common': () => 1 }, commonObj) // => 1
jvu.match({ '#/common': () => 1 }, unknownObj) // => undefined
// ...
jvu.match({
'#/0': () => 0,
'#/other': () => 1
}, 0); // => 1
This comes from funcy package
const fact = jvu.match({
'#/0': () => 1,
'#/other': n => n * fact(n - 1)
});
fact(5) // => 120
/**
if(some === null) { throw new Error('#/null') }
else { process() }
*/// =>
jvu.match({
'#/null': () => throw new Error('#/null'),
'#/other': process,
}, some)
A better example of if-less or Matching Pattern would be a stream (in Reactive Programming) or Promise chain
new Promise((resolve, reject) => resolve({ type: 'common' }))
.then(
jvu.match({
'#/null': () => 'so far so good',
'#/common': () => 'common',
'#/other': () => 'enough',
}),
jvu.match({
'#/error/system': () => 'this is bad',
'#/error/common': () => '\_(ツ)_/¯',
})
)
.then(result => console.log(result)); // => 'common'
Use find to ease a switch condition. A difference between match is that find only returns a given result, when match is also executing found expression. Both find and match method can use either object or array as a param.
jvu.find(['#/common'], commonObj) // => 0
jvu.find({ '#/common': 1 }, unknownObj) // => undefined
When executed with an object it returns a value by found key, but when executed with an array returns an index of founded item. It looks a bit inconsistent, nevertheless it has a reason to output an opposite information comparing to input.
Filters all appropriate pathes. This pattern does not exist with a function call (like match is a find with call).
jvu.filter({ '#/common': 1, '#/all': 2 }, unknownObj) // => [1, 2]
- add(String namespace, Object jsonSchema) add schema to existing environment
- jvu(String/Object reference[, Object instance]) validate object by schema reference. Shorthands - validate, is
- not(String/Object reference[, Object instance]) validate object by schema reference with negative case.
- match(Object/Array types[, Object instance]) iterates through an object or array to match appropriate schema for given argument. Executes found function. Returns
undefinedif not found. - find(Object/Array types[, Object instance]) iterates through an object or array to find appropriate schema for given argument. Returns
undefinedif not found. - filter(Object/Array types[, Object instance]) iterates through an object or array to filter appropriate schemas for given argument. Returns
Arraywith values. - env original environment