Skip to content

Commit

Permalink
Merge pull request #273 from zazuko/shacl-report-success
Browse files Browse the repository at this point in the history
Shacl report success
  • Loading branch information
tpluscode authored Feb 9, 2024
2 parents d3f919f + 61728b4 commit 990b910
Show file tree
Hide file tree
Showing 7 changed files with 75 additions and 10 deletions.
5 changes: 5 additions & 0 deletions .changeset/lemon-icons-clean.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"barnard59-shacl": patch
---

Produce SHACL report on successful validation
4 changes: 2 additions & 2 deletions packages/cube/test/validation.pipeline.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ describe('cube validation pipeline', function () {
const result = shell.exec(command, { silent: true, cwd })

strictEqual(result.stderr, '')
strictEqual(result.stdout, '')
ok(result.stdout.includes('_:report <http://www.w3.org/ns/shacl#conforms> "true"^^<http://www.w3.org/2001/XMLSchema#boolean>'))
strictEqual(result.code, 0)
})

Expand Down Expand Up @@ -48,7 +48,7 @@ describe('cube validation pipeline', function () {
const result = shell.exec(command, { silent: true, cwd })

strictEqual(result.stderr, '')
strictEqual(result.stdout, '')
ok(result.stdout.includes('_:report <http://www.w3.org/ns/shacl#conforms> "true"^^<http://www.w3.org/2001/XMLSchema#boolean>'))
strictEqual(result.code, 0)
})

Expand Down
15 changes: 15 additions & 0 deletions packages/shacl/lib/TermCounter.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
export default class TermCounter {
/**
* @param {import('@rdfjs/term-map/Factory.js').TermMapFactory} env
*/
constructor(env) {
this.termMap = env.termMap()
}

/**
* @param {import('@rdfjs/types').Term} term
*/
add(term) {
this.termMap.set(term, (this.termMap.get(term) ?? 0) + 1)
}
}
16 changes: 14 additions & 2 deletions packages/shacl/lib/report.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,18 @@ function getMessages(report) {
.map(message => message + '\n')
}

export function getSummary(dataset) {
return getMessages(new ValidationReport(this.env.clownface({ dataset })))
export function getSummary() {
return async function * (stream) {
let total = 0
for await (const dataset of stream) {
const messages = getMessages(new ValidationReport(this.env.clownface({ dataset })))
total += messages.length
yield messages
}
if (total) {
this.error(new Error(`${total} violations found`))
} else {
yield 'successful\n'
}
}.bind(this)
}
8 changes: 4 additions & 4 deletions packages/shacl/pipeline/report-summary.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,10 @@
[ base:stdin () ]
[ n3:parse () ]
[ rdf:getDataset () ]
[ base:map ([
a code:EcmaScriptModule ;
code:link <file:../lib/report.js#getSummary>
])
[ a p:Step ;
code:implementedBy [ a code:EcmaScriptModule ;
code:link <file:../lib/report.js#getSummary>
] ;
]
[ base:flatten () ]
)
Expand Down
17 changes: 15 additions & 2 deletions packages/shacl/report.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Duplex } from 'node:stream'
import { isReadableStream, isStream } from 'is-stream'
import SHACLValidator from 'rdf-validate-shacl'
import TermCounter from './lib/TermCounter.js'

/**
* @this {import('barnard59-core').Context}
Expand All @@ -10,6 +11,7 @@ import SHACLValidator from 'rdf-validate-shacl'
*/
async function * validate(ds, maxViolations, iterable) {
let totalViolations = 0
const counter = new TermCounter(this.env)

for await (const chunk of iterable) {
if (maxViolations && totalViolations > maxViolations) {
Expand All @@ -21,14 +23,25 @@ async function * validate(ds, maxViolations, iterable) {
const validator = new SHACLValidator(ds, { maxErrors: 0, factory: this.env })
const report = validator.validate(chunk)
if (!report.conforms) {
const violations = report.results.filter(r => this.env.ns.sh.Violation.equals(r.severity)).length
totalViolations += violations
for (const result of report.results) {
if (result.severity) counter.add(result.severity)
}

totalViolations = counter.termMap.get(this.env.ns.sh.Violation) ?? 0
yield report.dataset
}
}

counter.termMap.forEach((count, term) => this.logger.warn(`${count} results with severity ${term.value}`))

if (totalViolations) {
this.error(new Error(`${totalViolations} violations found`))
} else {
const report = this.env.dataset()
const blankNode = this.env.blankNode('report')
report.add(this.env.quad(blankNode, this.env.ns.rdf.type, this.env.ns.sh.ValidationReport))
report.add(this.env.quad(blankNode, this.env.ns.sh.conforms, this.env.literal('true', this.env.ns.xsd.boolean)))
yield report
}
}

Expand Down
20 changes: 20 additions & 0 deletions packages/shacl/test/TermCounter.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { strictEqual } from 'assert'
import env from 'barnard59-env'
import TermCounter from '../lib/TermCounter.js'

describe('TermCounter', () => {
it('should count terms', async () => {
const counter = new TermCounter(env)
counter.add(env.ns.sh.Violation)
counter.add(env.ns.sh.Violation)
counter.add(env.ns.sh.Info)
counter.add(env.ns.sh.Warning)
counter.add(env.ns.sh.Violation)
counter.add(env.ns.sh.Warning)

strictEqual(counter.termMap.size, 3)
strictEqual(counter.termMap.get(env.ns.sh.Violation), 3)
strictEqual(counter.termMap.get(env.ns.sh.Warning), 2)
strictEqual(counter.termMap.get(env.ns.sh.Info), 1)
})
})

0 comments on commit 990b910

Please sign in to comment.