This was call jsonql-param-validator
now move into the @jsonql
scope with many additional features
We are now making it a general purpose validation library to use with Javascript / Typescript project
// breaking change 0.7.0 change to Validator
import { Validator } from '@jsonql/validator'
// @TODO how to get the ast
const validator = new Validator(ast: Array<JsonqlPropertyParamMap>)
validator.validate(values)
.then(result => {
// The same that comes in - if there is default value then nit will be apply
})
.catch(error => {
console.log(error.details)
// contains position where your validation failed
})
This is what the ast
looks like, we use our other plugin @jsonql/ast
to generate this automatically, but you can write your own
// @TODO should we have two different types map 1 for external use?
declare type JsonqlPropertyParamMap = {
name: string // the argument name
required: boolean
type: string | Array<string> // Array<string> is union type
tstype?: string // generated by swc
types?: any // this is internal generated
validate?: (value: unknown) => boolean
validateAsync?: (value: unknown) => Promise<boolean>
// rules get create the moment we init the object
rules?: Array<JsonqlValidateCbFn>
}
Here is an example of a normal function using our customized AST extractor
export default function baselineFn(value1: string, value2: number, value3 = false): string {
return `${value1} with number ${value2} ${value3 ? ' fine' : ' or not'}`
}
please note our extractor only support default export - not export
this is to do with how we organize code within a jsonql
project
but fear not if this doesn't work for you. The way we build our @jsonql/ast
is
like many small parts, and you can easily mix and match to build one for your need
Then it will transform into this for the validator
:
{
"baselineFn": [
{
"name": "value1",
"required": true,
"type": "string"
},
{
"name": "value2",
"required": true,
"type": "number"
},
{
"name": "value3",
"required": true,
"type": "boolean",
"defaultvalue": false
}
]
}
To use with the @jsonql/validator
const validator = new Validator(ast.baselineFn)
Note that we ONLY use the ast.baselineFn
part which is an array,
because the validator
only cares about the argument parameter
and nothing else
When use with Javascript, you can create your own AST map like the above example, and it will able to understand your parameter types.
import { Validator } from '@jsonql/validator'
// See above example how to get the ast
const validator = new Validator(ast)
validator.registerPlugin('myStandardPlugin', {
main: (value: any): boolean => {
// must return boolean
}
})
validator.registerPlugin('myPluginRequireArg', {
main: (arg1: number, arg2: number, value: any): boolean => {
// do things with it
},
params: ['arg1', 'arg2']
})
There are required fields when you build a plugin with extra argument:
name: string
- the name of the plugin, and we will check if its collide with one of our own (see the complete list below)main: Function: boolean
- the actual function to perform the validation. It expect a boolean return for validator to know if the validation is success or not.params: Array<string>
- the parameters name used in your main
function, see the value
which is the value that gets validate MUST be the last of the argument. The reason is during run time, we curry your function first and pass into a queue system to run the value. See example belowCurry main function example:
// your function
function main(arg1: number, arg2: number, value: any) {
return value >= arg2 && value < arg2
}
const queueFn = curry(main)(arg1, arg2) // see how we get that value from example below
queueFn(value): boolean
Then when you need to execute your validator
validator.addValidationRules([
{plugin: 'myPlugin', arg1: 100, arg2: 200}
])
validator.validate([101])
.then((result: any) => {
// result will be an object
// {arg: 101}
})
.catch((err: ValidationError) => {
// look for err.detail
// if this fail you will get an array contain two number
// [1,0]
// what that means is it failed the second rule (zero based index) on the first position
// the built in type checking rule always comes first
})
At first it might sound weird why the Error return [argNameIndex, validationRuleIndex]
.
The reason behind this is because we need to have a super tiny format that travel between server side / client side. Instead of adding loads of code just to deal with an Error message. We just tell you the position which rule failed. And you can map it out yourself. And the up side is - you can map it in multiple languages, in different code, your imagination is the limitation :)
The build in rules only support primitive types: string
, number
, boolean
, array
checking (without value check, just Array.isArray
) and object
(typeof
) check. If you need complex array or object key/value check, please DIY that's what the plugin is for!
All the built-in plugins provide by another package, please see @jsonql/validator-core for more info.
Apart from creating a plugin, you could just pass a function or Async function directly.
// continue from previous example
// using name parameter
validator.addValidationRules({
value2: {
name: 'myExtraRule1',
validate: function(value: number) {
return value > 1000
}
}
})
The above method will automatically insert into the validation sequence right after the built-in rule (in our example, which is checking if your input value is a number)
If this fail then you will get [1,1]
as an error result.
You could also add async
method (all rules convert to async
internally):
// let say we want to validate an email address against the database
validator.addValidatorRules({
email: {
name: 'MyServerSideCheck',
server: true, // for our contract system this will only get call on the server side @TODO
validateAsync: async function(value: string) {
return db.checkIfExists(value)
.then(result => {
return true
})
.catch(error => {
return false
})
}
}
})
All the function(s) expect a true / false boolean
return result and nothing else.
Important unlike using registerPlugin
, you can not pass extra parameters
(of course you can just put the required value inside the function itself). Also
this validate method will always be one side only (server:true
if you use the jsonql / velocejs) system
because we can not pass your inline function over the wire.
If you need share this rule between client / server. You MUST create it in an external file,
and use the loadPlugin
method.
@TODO will be available in next release
By default, all your inline plugin, validate
, validateAsync
function will treat as server: true
what that means is, when using our jsonql / velocejs system, the contract will not contain (@TODO in the next release) those validation info.
The reason is, very often on a SPA system, you validate user input on your UI once then the same rule will run again on the server side. Which is a lot of duplication; and in our example about validate an email address, you might want to do some server side only check (i.e. check against the database). We provide this mapping to separate rules for front end / back end.
More to come.
ValidatorBase
You can use a standalone (without plugin system) Jsonql Validator by import it like this:
import { ValidatorBase } from '@jsonql/validator/base'
const vb = new ValidatorBase(astMap)
vb.validate(values)
.then(result=> {
// the result object is different
})
This module will only validate against the type you define plus the validate
or validateAsync
method you add via
the addValidationRules
method. If you try to use the plugin then it will throw in that particular rule you add with
ValidationError
object, and the ValidationError.message
will be NO_PLUGIN_DUMMY_FUNCTION
.
We add this mainly for us to create a more modular system, in our @jsonql/validators
(@NOTE with an s
)
it will create one plugin system and share among all the API interfaces.
NEWBRAN LTD / TO1SOURCE CN (c) 2022
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。