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
undefined
if not found. - find(Object/Array types[, Object instance]) iterates through an object or array to find appropriate schema for given argument. Returns
undefined
if not found. - filter(Object/Array types[, Object instance]) iterates through an object or array to filter appropriate schemas for given argument. Returns
Array
with values. - env original environment