LookAtMySuitBot/js/node_modules/protodef-validator/index.js

138 lines
4.1 KiB
JavaScript

const Ajv = require('ajv');
const assert=require("assert");
class Validator {
constructor(typesSchemas) {
this.createAjvInstance(typesSchemas);
this.addDefaultTypes();
}
createAjvInstance(typesSchemas) {
this.typesSchemas = {};
this.compiled=false;
this.ajv = new Ajv({verbose:true});
this.ajv.addSchema(require("./ProtoDef/schemas/definitions.json"),"definitions");
this.ajv.addSchema(require("./ProtoDef/schemas/protocol_schema.json"),"protocol");
if(typesSchemas) {
Object.keys(typesSchemas).forEach(s => this.addType(s, typesSchemas[s]));
}
}
addDefaultTypes() {
this.addTypes(require("./ProtoDef/schemas/numeric.json"));
this.addTypes(require("./ProtoDef/schemas/utils.json"));
this.addTypes(require("./ProtoDef/schemas/structures.json"));
this.addTypes(require("./ProtoDef/schemas/conditional.json"));
this.addTypes(require("./ProtoDef/schemas/primitives.json"));
}
addTypes(schemas) {
Object.keys(schemas).forEach((name) => this.addType(name, schemas[name]));
}
typeToSchemaName(name) {
return name.replace('|','_');
}
addType(name,schema) {
const schemaName=this.typeToSchemaName(name);
if(this.typesSchemas[schemaName] != undefined)
return;
if(!schema) { // default schema
schema={
"oneOf":[
{"enum":[name]},
{
"type": "array",
"items": [
{"enum":[name]},
{"oneOf":[{"type": "object"},{"type": "array"}]}
]
}
]};
}
this.typesSchemas[schemaName]=schema;
// recreate ajv instance to recompile dataType (and all depending types) when adding a type
if(this.compiled)
this.createAjvInstance(this.typesSchemas);
else {
this.ajv.addSchema(schema, schemaName);
}
this.ajv.removeSchema("dataType");
this.ajv.addSchema({
"title": "dataType",
"oneOf": [{"enum":["native"]}].concat(Object.keys(this.typesSchemas).map(name => ({"$ref": this.typeToSchemaName(name)})))
},"dataType");
}
validateType(type) {
let valid = this.ajv.validate("dataType",type);
this.compiled=true;
if(!valid) {
console.log(JSON.stringify(this.ajv.errors[0],null,2));
if(this.ajv.errors[0]['parentSchema']['title']=="dataType") {
this.validateTypeGoingInside(this.ajv.errors[0]['data']);
}
throw new Error("validation error");
}
}
validateTypeGoingInside(type) {
if(Array.isArray(type)) {
assert.ok(this.typesSchemas[this.typeToSchemaName(type[0])]!=undefined,type+" is an undefined type");
let valid = this.ajv.validate(type[0],type);
this.compiled=true;
if(!valid) {
console.log(JSON.stringify(this.ajv.errors[0],null,2));
if(this.ajv.errors[0]['parentSchema']['title']=="dataType") {
this.validateTypeGoingInside(this.ajv.errors[0]['data']);
}
throw new Error("validation error");
}
}
else {
if(type=="native")
return;
assert.ok(this.typesSchemas[this.typeToSchemaName(type)]!=undefined,type+" is an undefined type");
}
}
validateProtocol(protocol) {
// 1. validate with protocol schema with basic datatype def
let valid = this.ajv.validate("protocol",protocol);
assert.ok(valid, JSON.stringify(this.ajv.errors,null,2));
// 2. recursively create several validator from current one and validate that
function validateTypes(p,originalValidator,path) {
const v=new Validator(originalValidator.typesSchemas);
Object.keys(p).forEach(k => {
if(k=="types") {
// 2 steps for recursive types
Object.keys(p[k]).forEach(typeName => v.addType(typeName));
Object.keys(p[k]).forEach(typeName => {
try {
v.validateType(p[k][typeName], path + "." + k + "." + typeName);
}
catch(e) {
throw new Error("Error at "+path + "." + k + "." + typeName);
}
});
}
else {
validateTypes(p[k],v,path+"."+k);
}
})
}
validateTypes(protocol,this,"root");
}
}
module.exports=Validator;