1
0
forked from apxtri/apxtri
apxtri/models/Checkjson.js

366 lines
13 KiB
JavaScript
Raw Normal View History

2023-12-07 12:04:19 +01:00
/*
This module have to be use in back as well front
can be include in project with
- >into a browser : <script src="https://townName.nationName.dns/nationchains/contracts/Checkjson.js"></script>
- into a node.js : const Checkjson = require( `../nationchains/socialworld/contracts/Checkjson.js`);
*/
// --##
const Checkjson = {};
Checkjson.schema = {};
Checkjson.schema.properties = {};
Checkjson.schema.properties.type = {};
Checkjson.schema.properties.type.string = (str) => typeof str === "string";
Checkjson.schema.properties.type.array = (val) => Array.isArray(val);
Checkjson.schema.properties.type.object = (val) => typeof val === 'object' && val !== null && !Array.isArray(val);
Checkjson.schema.properties.type.number = (n) => typeof n === "number";
Checkjson.schema.properties.type.boolean = (n) => typeof n === "boolean";
Checkjson.schema.properties.type.integer = (n) =>
n != "" && !isNaN(n) && Math.round(n) == n;
Checkjson.schema.properties.type.float = (n) =>
n != "" && !isNaN(n) && Math.round(n) != n; //not yet in json schema
Checkjson.schema.properties.minLength = (str, min) =>
2024-04-05 11:58:31 +02:00
typeof str === "string" && str.length >= parseInt(min);
2023-12-07 12:04:19 +01:00
Checkjson.schema.properties.maxLength = (str, max) =>
2024-04-05 11:58:31 +02:00
typeof str === "string" && str.length <= parseInt(max);
2023-12-07 12:04:19 +01:00
Checkjson.schema.properties.multipleOf = (n, val) =>
typeof n === "number" &&
typeof val === "number" &&
parseFloat(n) / parseFloat(val) -
Math.round(parseFloat(n) / parseFloat(val)) <
0.0000001;
Checkjson.schema.properties.range = (
n,
minimum,
exclusiveMinimum,
maximum,
exclusiveMaximum
) => {
//console.log(minimum,exclusiveMinimum,maximum, exclusiveMaximum,n)
if (typeof n !== "number") return false;
if (minimum && parseFloat(n) < parseFloat(minimum)) return false;
if (exclusiveMinimum && parseFloat(n) <= parseFloat(exclusiveMinimum))
return false;
if (maximum && parseFloat(n) > parseFloat(maximum)) return false;
if (exclusiveMaximum && parseFloat(n) >= parseFloat(exclusiveMaximum))
return false;
return true;
};
Checkjson.schema.properties.pattern = (str, pattern) => {
try {
pattern = new RegExp(pattern);
} catch (e) {
console.log("err pattern in checkjon", pattern);
return false;
}
return pattern.test(str);
};
Checkjson.schema.properties.enum = (str, enumvalues) => {
if (Array.isArray(enumvalues)) {
return typeof str === "string" && enumvalues.includes(str);
} else if (tribeId) {
//enumvalues is a reference of objectname.key
const { tribeId, obj, keyid } = enumvalues.split(".");
return fs.existsSync(
2024-03-15 08:49:23 +01:00
`../../../${tribeId}/schema/${obj}/itm/${keyid}.json`
2023-12-07 12:04:19 +01:00
);
} else {
return true;
}
};
// to check a value for a pattern
// Checkjson.schema.properties.pattern(value, properties[p].pattern)
/**
*
* @param {string} str to test
* @param {string} format keyworkd existing in Checkjson.schema.properties.format
* @return null if format does not exist, true or false
*/
Checkjson.testformat=(str, format)=>{
if (!Checkjson.schema.properties.format[format]) { return null}
return Checkjson.schema.properties.pattern(str, Checkjson.schema.properties.format[format])
}
// see format https://json-schema.org/understanding-json-schema/reference/string.html#format
// to check a just value with a format use Checkjson.testformat=(value, format)
Checkjson.schema.properties.format = {
"date-time": /\d{4}-[01]\d-[0-3]\dT[0-2]\d:[0-5]\d:[0-5]\d\.\d{1,3}/,
stringalphaonly: /^[A-Za-z0-9]{3,}$/,
time: /[0-2]\d:[0-5]\d:[0-5]\d\.\d{1,3}/,
date: /\d{4}-[01]\d-[0-3]\d/,
duration: / /,
email:
/^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/,
"idn-email": / /,
uuid: /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/,
2024-04-05 11:58:31 +02:00
url:/^(?:(?:https?|ftp):\/\/)(?:\w+(?::\w+)?@)?(?:(?:[a-z0-9-\.]+\.[a-z]{2,})(?:[-a-z0-9+\._\%\!\\[\]\(\)\,\*\?\&\=\:]*){1,})|(?:(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?)\.(?:25[0-5]|2[0-4][0-9]|[0-1]?[0-9][0-9]?))(?:[:\/#][^#]*)?)$/,
2023-12-07 12:04:19 +01:00
uri: / /,
"uri-reference": / /,
iri: / /,
hostname: / /,
"idn-hostname": / /,
ipv4: /^([09]{1,3}.){3}.([09]{1,3})$/,
ipv6: /^((([09A-Fa-f]{1,4}:){7}[09A-Fa-f]{1,4})|(([09A-Fa-f]{1,4}:){6}:[09A-Fa-f]{1,4})|(([09A-Fa-f]{1,4}:){5}:([09A-Fa-f]{1,4}:)?[09A-Fa-f]{1,4})|(([09A-Fa-f]{1,4}:){4}:([09A-Fa-f]{1,4}:){0,2}[09A-Fa-f]{1,4})|(([09A-Fa-f]{1,4}:){3}:([09A-Fa-f]{1,4}:){0,3}[09A-Fa-f]{1,4})|(([09A-Fa-f]{1,4}:){2}:([09A-Fa-f]{1,4}:){0,4}[09A-Fa-f]{1,4})|(([09A-Fa-f]{1,4}:){6}((b((25[05])|(1d{2})|(2[04]d)|(d{1,2}))b).){3}(b((25[05])|(1d{2})|(2[04]d)|(d{1,2}))b))|(([09A-Fa-f]{1,4}:){0,5}:((b((25[05])|(1d{2})|(2[04]d)|(d{1,2}))b).){3}(b((25[05])|(1d{2})|(2[04]d)|(d{1,2}))b))|(::([09A-Fa-f]{1,4}:){0,5}((b((25[05])|(1d{2})|(2[04]d)|(d{1,2}))b).){3}(b((25[05])|(1d{2})|(2[04]d)|(d{1,2}))b))|([09A-Fa-f]{1,4}::([09A-Fa-f]{1,4}:){0,5}[09A-Fa-f]{1,4})|(::([09A-Fa-f]{1,4}:){0,6}[09A-Fa-f]{1,4})|(([09A-Fa-f]{1,4}:){1,7}:))$/,
2024-03-27 23:52:41 +01:00
telephonefr: /^0[1-9][0-9]{8}$/,
2023-12-07 12:04:19 +01:00
telephoneinter: /^\+*(\d{3})*[0-9,\-]{8,}/,
password:
/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[$@$!%*?&.])[A-Za-z\d$@$!%*?&.{}:|\s]{8,}/,
postalcodefr: /(^\d{5}$)|(^\d{5}-\d{4}$)/,
pgppublickey:
/^-----BEGIN PGP PUBLIC KEY BLOCK-----(\n|\r|\r\n)(\n|\r|\r\n)([0-9a-zA-Z\+\/=]*(\n|\r|\r\n))*-----END PGP PUBLIC KEY BLOCK-----(\n|\r|\r\n)?$/gm,
pgpprivatekey:
/^-----BEGIN PGP PRIVATE KEY BLOCK-----(\n|\r|\r\n)(\n|\r|\r\n)([0-9a-zA-Z\+\/=]*(\n|\r|\r\n))*-----END PGP PRIVATE KEY BLOCK-----(\n|\r|\r\n)?$/gm,
};
Checkjson.schema.properties.default;
Checkjson.schema.validation = (schema) => {
/*validate a schema structure*/
const multimsg = [];
const res = {};
if (schema.properties) {
Object.keys(schema.properties).forEach((p) => {
const properties = schema.properties;
if (
properties[p].type &&
typeof properties[p].type === "string" &&
!Checkjson.schema.properties.type[properties[p].type]
) {
multimsg.push({
ref: "Checkjson",
msg: "schemaerrtypedoesnotexist",
data: { propertie: p, type: properties[p].type },
});
}
if (
properties[p].type &&
typeof properties[p].type === "object"){
if (properties[p]['$ref']){
//This is manage by Odmdb.schema to load recursively complex schema
multimsg.push({
ref: "Checkjson",
msg: "externalrefnotload",
data: { propertie: p, ref: properties[p]["$ref"]},
});
}
//case type=="object" with properties
if (properties[p].properties){
const checksub = Checkjson.schema.validation(properties[p])
if (checksub.status!=200){
multimsg = multimsg.concat(checksub.multimsg)
}
}
// if not $ref or no properties then any object is accepted
}
if (
properties[p].format &&
!Checkjson.schema.properties.format[properties[p].format]
) {
multimsg.push({
ref: "Checkjson",
msg: "schemaerrformatdoesnotexist",
data: { propertie: p, format: properties[p].format },
});
}
if (properties[p].enum && !Array.isArray(properties[p].enum)) {
multimsg.push({
ref: "Checkjson",
msg: "schemaerrenumnotarray",
data: { propertie: p, enum: properties[p].enum },
});
}
});
}
// 406 means not acceptable
if (multimsg.length > 0) {
res.status = 406;
res.multimsg = multimsg;
} else {
res.status = 200;
res.ref = "Checkjson";
res.msg = "validcheck";
}
return res;
};
/**
* Check data with a schema
*
* @param {object} schema a json schema
* @param {*} data some data to check using schema
* @param {*} withschemacheck boolean that force a schema check (usefull on modification schema)
* @returns {status: 200, ref:"Checkjson", msg:"validcheck", data:{itm:object}}
* {status:417, multimsg:[{re,msg,data}],data:{itm:object}}
*/
Checkjson.schema.data = (schema, data, withschemacheck) => {
/* validate a data set with a schema in a context ctx */
/*
console.log('#################')
console.log(schema);
console.log('---------')
console.log(data)
*/
const propertiescheck=(properties,subdata)=>{
// properties ={prop1:{type,format},prop2:{type:object,...}}
// subdata={prop1,prop2}
// Return [] => no error, else 1 item per error {msg,ref:checkjson,data}
let multimsg=[]
Object.keys(properties).forEach((p) => {
//type is mandatory in a propertie
if (subdata[p]) {
if (properties[p].properties){
//means it is a subobject
multimsg=multimsg.concat(propertiescheck(properties[p].properties,subdata[p]))
}
//type can be a list of string; number, array, boolean, object, null
const typlist =
properties[p].type && typeof properties[p].type === "string"
? [properties[p].type]
: properties[p].type;
let valid = false;
typlist.forEach((typ) => {
// at least one test have to be valid
if (Checkjson.schema.properties.type[typ](subdata[p])) valid = true;
});
if (!valid)
multimsg.push({
ref: "Checkjson",
msg: "dataerrpropertie",
data: { key: p, value: subdata[p] },
});
if (
properties[p].minLength &&
!Checkjson.schema.properties.minLength(
subdata[p],
properties[p].minLength
)
) {
multimsg.push({
ref: "Checkjson",
msg: "dataerrpropertie",
data: {
key: p,
value: subdata[p],
minLength: properties[p].minLength,
},
});
}
if (
properties[p].maxLength &&
!Checkjson.schema.properties.maxLength(
subdata[p],
properties[p].maxLength
)
) {
multimsg.push({
ref: "Checkjson",
msg: "dataerrpropertie",
data: {
key: p,
value: subdata[p],
maxLength: properties[p].maxLength,
},
});
}
if (
properties[p].multipleOf &&
!Checkjson.schema.properties.multipleOf(
subdata[p],
properties[p].multipleOf
)
) {
multimsg.push({
ref: "Checkjson",
msg: "dataerrpropertie",
data: {
key: p,
value: subdata[p],
multipleOf: properties[p].multipleOf,
},
});
}
if (
properties[p].minimum ||
properties[p].maximum ||
properties[p].exclusiveMinimum ||
properties[p].exclusiveMaximum
) {
// test range
if (
!Checkjson.schema.properties.range(
subdata[p],
properties[p].minimum,
properties[p].exclusiveMinimum,
properties[p].maximum,
properties[p].exclusiveMaximum
)
) {
multimsg.push({
ref: "Checkjson",
msg: "dataerrpropertie",
data: {
key: p,
value: subdata[p],
minimum: properties[p].minimum,
maximum: properties[p].maximum,
exclusiveMinimum: properties[p].exclusiveMinimum,
exclusiveMaximum: properties[p].exclusiveMaximum,
},
});
}
}
if (
properties[p].enum &&
!Checkjson.schema.properties.enum(subdata[p], properties[p].enum)
) {
multimsg.push({
ref: "Checkjson",
msg: "dataerrpropertie",
data: { key: p, value: subdata[p], enumlst: properties[p].enum },
});
}
if (properties[p].format) {
properties[p].pattern =
Checkjson.schema.properties.format[properties[p].format];
}
if (
properties[p].pattern &&
!Checkjson.schema.properties.pattern(subdata[p], properties[p].pattern)
) {
multimsg.push({
ref: "Checkjson",
msg: "dataerrpropertie",
data: { key: p, value: subdata[p], pattern: properties[p].pattern },
});
}
} else if (schema.required && schema.required.includes(p)) {
multimsg.push({
ref: "Checkjson",
msg: "dataerrpropertierequired",
data: { key: p, required: true },
});
}
});
return multimsg
};//end propertiescheck()
if (withschemacheck) {
const validschema = Checkjson.schema.validation(schema);
if (validschema.status != 200) return validschema;
}
let multi=propertiescheck(schema.properties,data)
const res = {};
if (multi.length > 0) {
res.status = 417;
res.multimsg = multi;
} else {
res.status = 200;
res.ref = "Checkjson";
res.msg = "validcheck";
}
if (schema.apxid) {
res.data={apxid : data[schema.apxid],itm:data};
}
return res;
};
if (typeof module !== "undefined") module.exports = Checkjson;