@wmfs/j2119
Version:
A general-purpose validator generator that uses RFC2119-style assertions as input.
83 lines (67 loc) • 2.58 kB
JavaScript
class NodeValidator {
constructor (parser) {
this.parser = parser
} // constructor
validateDocument (doc) {
const problems = []
this.validateNode(doc, this.parser.root, [this.parser.root], problems)
return problems
} // validateDocument
validateNode (node, path, roles, problems) {
if ((node === null) || (typeof node !== 'object') || Array.isArray(node)) return
// may have more roles based on field presence/value etc
this.parser.findMoreRoles(node, roles)
this.checkConstraints(node, path, roles, problems)
// for each field
for (const [name, value] of Object.entries(node)) {
this.validateField(name, path, roles, problems)
this.validateChild(value, name, path, roles, problems)
this.validateGrandchildren(value, name, path, roles, problems)
} // for ...
} // validateNode
checkConstraints (node, path, roles, problems) {
// constraints are attached per-role
for (const role of roles) {
const allConstraints = this.parser.getConstraints(role)
for (const constraint of allConstraints) {
if (constraint.applies(node, roles)) {
constraint.check(node, path, problems)
}
}
}
} // checkConstraints
validateField (name, path, roles, problems) {
if (!this.parser.isFieldAllowed(roles, name)) {
problems.push(`Field "${name}" not allowed in ${path}`)
}
} // validateField
validateChild (childNode, name, path, roles, problems) {
const childRoles = this.parser.findChildRoles(roles, name)
if (!childRoles.length) return
// recurse into children if they have roles
this.validateNode(
childNode,
`${path}.${name}`,
childRoles,
problems
)
} // validateChild
validateGrandchildren (childNode, name, path, roles, problems) {
if ((childNode === null) || (typeof childNode !== 'object')) return
// only objects & arrays have grand children
// find inheritance-based roles for that field
const grandchildRoles = this.parser.findGrandchildRoles(roles, name)
if (!grandchildRoles.length) return
if (this.parser.allowsAnyField(grandchildRoles)) return
const gcIndex = Array.isArray(childNode) ? i => `[${i}]` : i => `.${i}`
for (const [index, gcValue] of Object.entries(childNode)) {
this.validateNode(
gcValue,
`${path}.${name}${gcIndex(index)}`,
[...grandchildRoles],
problems
)
} // for ...
} // validateGrandchildren
} // class NodeValidator
module.exports = parser => new NodeValidator(parser)