shacl-engine
Version:
A fast RDF/JS SHACL engine
97 lines (80 loc) • 2.86 kB
JavaScript
import TermSet from '@rdfjs/term-set'
import { fromRdf } from 'rdf-literal'
import * as ns from '../namespaces.js'
function compileClosedNode (shape) {
const closed = fromRdf(shape.ptr.out([ns.sh.closed]).term)
if (!closed) {
return null
}
const propertyShapes = shape.ptr.out([ns.sh.property]).map(ptr => shape.validator.shape(ptr))
const properties = new TermSet(propertyShapes.filter(shape => !shape.deactivated).map(shape => shape.path[0].predicates[0]))
const ignoredProperties = new TermSet([...(shape.ptr.out([ns.sh.ignoredProperties]).list() || [])].map(item => item.term))
return {
node: validateClosedNode(properties, ignoredProperties)
}
}
function validateClosedNode (properties, ignoredProperties) {
return context => {
const notAllowed = context.focusNode.execute({ start: 'subject', end: 'predicate' }).filter(property => {
if (ignoredProperties.has(property.term)) {
return false
}
return !properties.has(property.term)
})
if (notAllowed.ptrs.length > 0) {
for (const value of notAllowed) {
context.violation(ns.sh.ClosedConstraintComponent, {
message: [context.factory.literal('Predicate is not allowed (closed shape)')],
path: [{ quantifier: 'one', start: 'subject', end: 'object', predicates: [value.term] }],
value: context.focusNode.node([[...value.quads()][0].object])
})
}
} else {
context.debug(ns.sh.ClosedConstraintComponent)
}
}
}
function compileHasValue (shape) {
const hasValue = shape.ptr.out([ns.sh.hasValue]).term
return {
node: validateHasValueNode(hasValue),
property: validateHasValueProperty(hasValue)
}
}
function validateHasValueNode (hasValue) {
return context => {
context.test(hasValue.equals(context.valueOrNode.term), ns.sh.HasValueConstraintComponent, {
args: { hasValue },
message: [context.factory.literal('Value must be {$hasValue}')]
})
}
}
function validateHasValueProperty (hasValue) {
return context => {
const result = [...context.values].some(value => hasValue.equals(value.term))
context.test(result, ns.sh.HasValueConstraintComponent, {
args: { hasValue },
message: [context.factory.literal('Missing expected value {$hasValue}')]
})
}
}
function compileIn (shape) {
const values = new TermSet([...shape.ptr.out([ns.sh.in]).list()].map(item => item.term))
return {
generic: validateIn(values)
}
}
function validateIn (values) {
return context => {
context.test(values.has(context.valueOrNode.term), ns.sh.InConstraintComponent, {
args: { in: [...values].map(v => v.value).join(', ') },
message: [context.factory.literal('Value is not in {$in}')],
value: context.valueOrNode
})
}
}
export {
compileClosedNode,
compileHasValue,
compileIn
}