apxtrib/nationchains/socialworld/contracts/checkdata.js

302 lines
10 KiB
JavaScript
Raw Normal View History

2023-01-22 09:53:09 +00:00
/*
This module have to be independant of any external package
it is shared between back and front and is usefull
to apply common check in front before sending it in back
can be include in project with
2023-03-27 05:52:21 +00:00
- into a browser : <script src="https://townName.nationName.dns/socialworld/contracts/check.js"></script>
- into a node.js : const check = require( `../nationchains/socialworld/contracts/check.js`);
2023-01-22 09:53:09 +00:00
*/
// --##
2023-03-27 05:52:21 +00:00
const check = {};
check.schema = {};
check.schema.properties = {};
check.schema.properties.type = {};
check.schema.properties.type.string = (str) => typeof str === "string";
check.schema.properties.type.number = (n) => typeof n === "number";
check.schema.properties.type.integer = (n) =>
n != "" && !isNaN(n) && Math.round(n) == n;
check.schema.properties.type.float = (n) =>
n != "" && !isNaN(n) && Math.round(n) != n; //not yet in json schema
check.schema.properties.minLength = (str, min) =>
typeof str === "string" && str.length > parseInt(min);
check.schema.properties.maxLength = (str, max) =>
typeof str === "string" && str.length < parseInt(max);
check.schema.properties.multipleOf = (n, val) =>
typeof n === "number" &&
typeof val === "number" &&
parseFloat(n) / parseFloat(val) -
Math.round(parseFloat(n) / parseFloat(val)) <
0.0000001;
check.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;
2023-01-22 09:53:09 +00:00
};
2023-03-27 05:52:21 +00:00
check.schema.properties.pattern = (str, pattern) => {
try {
new RegExp(pattern);
} catch (e) {
return false;
}
return pattern.test(str);
2023-01-22 09:53:09 +00:00
};
2023-03-27 05:52:21 +00:00
check.schema.properties.enum = (str, enumvalues) =>
typeof str === "string" && enumvalues.includes(str);
// see format https://json-schema.org/understanding-json-schema/reference/string.html#format
check.schema.properties.format = {
"date-time": / /,
time: / /,
date: / /,
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}$/,
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}:))$/,
telephonefr: /^0[1-9][0-9]{9}$/,
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}$)/,
2023-01-22 09:53:09 +00:00
};
2023-03-27 05:52:21 +00:00
check.schema.validation = (schema) => {
/*validate a schema structure*/
const res = { status: 200, err: [] };
if (schema.properties) {
Object.keys(schema.properties).forEach((p) => {
const properties = schema.properties;
if (
properties[p].type &&
typeof properties[p].type === "string" &&
!check.schema.properties.type[properties[p].type]
) {
res.err.push({
info: "|checkdata|typedoesnotexistinschema",
moreinfo: ` ${properties[p].type}`,
});
}
if (
properties[p].type &&
typeof properties[p].type === "object" &&
Array.isArray(properties[p].type)
) {
properties[p].type.forEach((tp) => {
if (!check.schema.properties.type[tp])
res.err.push({
info: "|checkdata|typedoesnotexistinschema",
moreinfo: `${tp} of ${properties[p].type}`,
});
});
}
if (
properties[p].format &&
!check.schema.properties.format[properties[p].format]
) {
res.err.push({
info: "|checkdata|formatdoesnotexistinschema",
moreinfo: ` ${properties[p].format}`,
});
}
if (properties[p].enum && !Array.isArray(properties[p].enum)) {
res.err.push({
info: "|checkdata|enumisnotarrayinschema",
moreinfo: ` ${properties[p].enum}`,
});
}
});
}
// 406 means not acceptable
if (res.err.length > 0) res.status = 406;
return res;
2023-01-22 09:53:09 +00:00
};
2023-03-27 05:52:21 +00:00
check.schema.data = (schema, ctx, data) => {
/* validate a data set with a schema in a context ctx */
/*
console.log('#################')
console.log(schema);
console.log('---------')
console.log(data)
*/
const validschema = check.schema.validation(schema);
if (validschema.status != 200) return validschema;
const res = { status: 200, err: [] };
if (schema.properties) {
const properties = schema.properties;
Object.keys(properties).forEach((p) => {
//type is mandatory in a propertie
if (data[p]) {
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 ok
if (check.schema.properties.type[typ](data[p])) valid = true;
});
if (!valid)
res.err.push({
info: "|checkdata|dataerrpropertie",
moreinfo: `${p} : ${data[p]}`,
});
if (
properties[p].minLength &&
!check.schema.properties.minLength(data[p], properties[p].minLength)
) {
res.err.push({
info: "|checkdata|dataerrpropertie",
moreinfo: `${p} : ${data[p]} minLength:${properties[p].minLength}`,
});
}
if (
properties[p].maxLength &&
!check.schema.properties.maxLength(data[p], properties[p].maxLength)
) {
res.err.push({
info: "|checkdata|dataerrpropertie",
moreinfo: `${p} : ${data[p]} maxLength:${properties[p].maxLength}`,
});
}
if (
properties[p].multipleOf &&
!check.schema.properties.multipleOf(data[p], properties[p].multipleOf)
) {
res.err.push({
info: "|checkdata|dataerrpropertie",
moreinfo: `${p} : ${data[p]} not a multipleOf:${properties[p].multipleOf}`,
});
}
if (
properties[p].minimum ||
properties[p].maximum ||
properties[p].exclusiveMinimum ||
properties[p].exclusiveMaximum
) {
// test range
if (
!check.schema.properties.range(
data[p],
properties[p].minimum,
properties[p].exclusiveMinimum,
properties[p].maximum,
properties[p].exclusiveMaximum
)
) {
res.err.push({
info: "|checkdata|dataerrpropertie",
moreinfo: `${p} : ${data[p]} not in range ${properties[p].minimum} exclu: ${properties[p].exclusiveMinimum} and ${properties[p].maximum} exclu: ${properties[p].exclusiveMaximum}`,
});
}
}
if (
properties[p].enum &&
!check.schema.properties.enum(data[p], properties[p].enum)
) {
res.err.push({
info: "|checkdata|dataerrpropertie",
moreinfo: `${p} : ${data[p]} not in enum list :${properties[p].enum}`,
});
}
if (properties[p].format) {
properties[p].pattern =
check.schema.properties.format[properties[p].format];
}
if (
properties[p].pattern &&
!check.schema.properties.pattern(data[p], properties[p].pattern)
) {
res.err.push({
info: "|checkdata|dataerrpropertie",
moreinfo: `${p} : ${data[p]} problem pattern or format ${properties[p].pattern}`,
});
}
} else if (schema.required.includes(p)) {
res.err.push({
info: "|checkdata|dataerrpropertiesrequired",
moreinfo: `${p}`,
});
}
});
}
if (res.err.length > 0) res.status = 417;
return res;
2023-01-22 09:53:09 +00:00
};
2023-03-27 05:52:21 +00:00
2023-01-22 09:53:09 +00:00
/*
2023-03-27 05:52:21 +00:00
Normalize data link to check.schema.properties.format
or any normalization to get consistent data
*/
const normalize={};
normalize.telephonefr =(phone)=>{
phone = phone.trim().replace(/[- .]/g, "");
if (
check.schema.properties.format.telephoenfr(phone) &&
phone.length == 10 &&
phone[0] == "0"
) {
phone = "+33 " + phone.substring(1);
}
return phone;
2023-01-22 09:53:09 +00:00
};
2023-03-27 05:52:21 +00:00
normalize.zfill10 = (num) => {
let s = num + "";
while (s.length < 10) s = "0" + s;
return s;
2023-01-22 09:53:09 +00:00
};
2023-03-27 05:52:21 +00:00
check.test.unique = (ctx, val) => {
if (ctx.list[ctx.currentfield]) {
return !ctx.list[ctx.currentfield].includes(val);
} else {
console.log("ERR no list for field:" + ctx.currentfield);
return false;
}
};
// check.normalize take a correct data then reformat it to harmonise it
check.normalize = {};
check.normalize.phoneNumber = (ctx, phone) => {
phone = phone.trim().replace(/[- .]/g, "");
if (
check.test.phoneNumber("", phone) &&
phone.length == 10 &&
phone[0] == "0"
) {
phone = "+33 " + phone.substring(1);
}
return phone;
};
check.normalize.upperCase = (ctx, txt) => txt.toUpperCase();
check.normalize.lowerCase = (ctx, txt) => txt.toLowerCase();
// fixe 10 position et complete par des 0 devant
check.normalize.zfill10 = (ctx, num) => {
let s = num + "";
while (s.length < 10) s = "0" + s;
return s;
2023-01-22 09:53:09 +00:00
};
2023-03-27 05:52:21 +00:00
if (typeof module !== "undefined") module.exports = check;