From 8cd66cd5ba73a3e6c9aa27d25f529d85fed7d0fb Mon Sep 17 00:00:00 2001 From: Giacomo Citi Date: Tue, 30 Jan 2024 10:48:44 +0100 Subject: [PATCH 1/7] report shacl success --- packages/cube/test/validation.pipeline.test.js | 4 ++-- packages/shacl/report.js | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/cube/test/validation.pipeline.test.js b/packages/cube/test/validation.pipeline.test.js index 213c9806..1494dfbe 100644 --- a/packages/cube/test/validation.pipeline.test.js +++ b/packages/cube/test/validation.pipeline.test.js @@ -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 "true"^^')) strictEqual(result.code, 0) }) @@ -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 "true"^^')) strictEqual(result.code, 0) }) }) diff --git a/packages/shacl/report.js b/packages/shacl/report.js index 123062b2..33335e70 100644 --- a/packages/shacl/report.js +++ b/packages/shacl/report.js @@ -29,6 +29,12 @@ async function * validate(ds, maxViolations, iterable) { 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 } } From 052b1a511ce5663acd1c8dae4843b8d0a05ce1da Mon Sep 17 00:00:00 2001 From: Giacomo Citi Date: Tue, 30 Jan 2024 11:31:41 +0100 Subject: [PATCH 2/7] true literal --- .changeset/lemon-icons-clean.md | 5 +++++ packages/shacl/report.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 .changeset/lemon-icons-clean.md diff --git a/.changeset/lemon-icons-clean.md b/.changeset/lemon-icons-clean.md new file mode 100644 index 00000000..ee9d8656 --- /dev/null +++ b/.changeset/lemon-icons-clean.md @@ -0,0 +1,5 @@ +--- +"barnard59-shacl": patch +--- + +Produce SHACL report on successful validation diff --git a/packages/shacl/report.js b/packages/shacl/report.js index 33335e70..df059f77 100644 --- a/packages/shacl/report.js +++ b/packages/shacl/report.js @@ -33,7 +33,7 @@ async function * validate(ds, maxViolations, iterable) { 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))) + report.add(this.env.quad(blankNode, this.env.ns.sh.conforms, this.env.literal('true', this.env.ns.xsd.boolean))) yield report } } From d592b22b350f4c7a900e8f6fe0d83e57cea60789 Mon Sep 17 00:00:00 2001 From: Giacomo Citi Date: Mon, 5 Feb 2024 12:31:47 +0100 Subject: [PATCH 3/7] error on violations --- packages/shacl/lib/report.js | 14 ++++++++++++-- packages/shacl/pipeline/report-summary.ttl | 8 ++++---- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/packages/shacl/lib/report.js b/packages/shacl/lib/report.js index 95c1bf0e..c79bd9e1 100644 --- a/packages/shacl/lib/report.js +++ b/packages/shacl/lib/report.js @@ -23,6 +23,16 @@ 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`)) + } + }.bind(this) } diff --git a/packages/shacl/pipeline/report-summary.ttl b/packages/shacl/pipeline/report-summary.ttl index 3989babe..8694f723 100644 --- a/packages/shacl/pipeline/report-summary.ttl +++ b/packages/shacl/pipeline/report-summary.ttl @@ -13,10 +13,10 @@ [ base:stdin () ] [ n3:parse () ] [ rdf:getDataset () ] - [ base:map ([ - a code:EcmaScriptModule ; - code:link - ]) + [ a p:Step ; + code:implementedBy [ a code:EcmaScriptModule ; + code:link + ] ; ] [ base:flatten () ] ) From 50de22e3c4886cb68cdb54e4a551a463d7366b8a Mon Sep 17 00:00:00 2001 From: Giacomo Citi Date: Wed, 7 Feb 2024 14:30:13 +0100 Subject: [PATCH 4/7] return a successful message --- packages/shacl/lib/report.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/packages/shacl/lib/report.js b/packages/shacl/lib/report.js index c79bd9e1..f2e05ced 100644 --- a/packages/shacl/lib/report.js +++ b/packages/shacl/lib/report.js @@ -33,6 +33,8 @@ export function getSummary() { } if (total) { this.error(new Error(`${total} violations found`)) + } else { + yield 'successful\n' } }.bind(this) } From 12c6ca1e68c094dd6a053a80916e7dd6765c835b Mon Sep 17 00:00:00 2001 From: Giacomo Citi Date: Thu, 8 Feb 2024 10:36:50 +0100 Subject: [PATCH 5/7] count results by severity --- packages/shacl/lib/TermCounter.js | 9 +++++++++ packages/shacl/report.js | 8 ++++++-- packages/shacl/test/TermCounter.test.js | 20 ++++++++++++++++++++ 3 files changed, 35 insertions(+), 2 deletions(-) create mode 100644 packages/shacl/lib/TermCounter.js create mode 100644 packages/shacl/test/TermCounter.test.js diff --git a/packages/shacl/lib/TermCounter.js b/packages/shacl/lib/TermCounter.js new file mode 100644 index 00000000..244e6265 --- /dev/null +++ b/packages/shacl/lib/TermCounter.js @@ -0,0 +1,9 @@ +export default class TermCounter { + constructor(env) { + this.termMap = env.termMap() + } + + add(term) { + this.termMap.set(term, (this.termMap.get(term) ?? 0) + 1) + } +} diff --git a/packages/shacl/report.js b/packages/shacl/report.js index df059f77..ea61d51f 100644 --- a/packages/shacl/report.js +++ b/packages/shacl/report.js @@ -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} @@ -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) { @@ -21,12 +23,14 @@ 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 + report.results.forEach(r => counter.add(r.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 { diff --git a/packages/shacl/test/TermCounter.test.js b/packages/shacl/test/TermCounter.test.js new file mode 100644 index 00000000..bc5540b8 --- /dev/null +++ b/packages/shacl/test/TermCounter.test.js @@ -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) + }) +}) From 3972ec180d5625f05134cadabfb046e14ff48604 Mon Sep 17 00:00:00 2001 From: Giacomo Citi Date: Thu, 8 Feb 2024 13:31:16 +0100 Subject: [PATCH 6/7] fix: type annotations --- packages/shacl/lib/TermCounter.js | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/packages/shacl/lib/TermCounter.js b/packages/shacl/lib/TermCounter.js index 244e6265..e1cf2c63 100644 --- a/packages/shacl/lib/TermCounter.js +++ b/packages/shacl/lib/TermCounter.js @@ -1,8 +1,14 @@ 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) } From 61728b40807c56d36b4a6e7a68631966e2bb651e Mon Sep 17 00:00:00 2001 From: Giacomo Citi Date: Thu, 8 Feb 2024 14:24:28 +0100 Subject: [PATCH 7/7] check type precondition --- packages/shacl/report.js | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/shacl/report.js b/packages/shacl/report.js index ea61d51f..bd34419a 100644 --- a/packages/shacl/report.js +++ b/packages/shacl/report.js @@ -23,7 +23,10 @@ async function * validate(ds, maxViolations, iterable) { const validator = new SHACLValidator(ds, { maxErrors: 0, factory: this.env }) const report = validator.validate(chunk) if (!report.conforms) { - report.results.forEach(r => counter.add(r.severity)) + 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 }