Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

289 move validation after enrichment #290

Merged
merged 2 commits into from
Feb 7, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 5 additions & 4 deletions gatsby-node.js
Original file line number Diff line number Diff line change
Expand Up @@ -106,18 +106,19 @@ exports.onPreBootstrap = async ({ createContentDigest, actions, getNode }) => {
console.info(`Found these turtle files:`)
ttlFiles.forEach((e) => console.info(e))
for (const f of ttlFiles) {
const ttlString = fs.readFileSync(f).toString()
const doc = await jsonld.fromRDF(ttlString, { format: "text/turtle" })
const compacted = await jsonld.compact(doc, context.jsonld)

if (config.failOnValidation) {
try {
console.info("Validating: ", f)
await validate("shapes/skohub.shacl.ttl", f)
await validate("shapes/skohub.shacl.ttl", f, doc)
} catch (e) {
console.error(e)
throw e
}
}
const ttlString = fs.readFileSync(f).toString()
const doc = await jsonld.fromRDF(ttlString, { format: "text/turtle" })
const compacted = await jsonld.compact(doc, context.jsonld)

const conceptSchemeIds = compacted["@graph"]
.filter((node) => node.type === "ConceptScheme")
Expand Down
27 changes: 15 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
"dependencies": {
"@emotion/react": "^11.10.5",
"@gatsbyjs/reach-router": "^2.0.0",
"@rdfjs/dataset": "^2.0.1",
"@rdfjs/parser-n3": "^2.0.1",
"crypto-browserify": "^3.12.0",
"dotenv": "^16.0.3",
Expand All @@ -21,7 +22,7 @@
"gatsby-transformer-sharp": "^5.0.0",
"graceful-fs": "^4.2.8",
"js-yaml": "^4.1.0",
"jsonld": "^8.2.0",
"jsonld": "^8.3.2",
"lodash.escaperegexp": "^4.1.2",
"markdown-to-jsx": "^7.1.8",
"n3": "^1.16.3",
Expand Down
17 changes: 15 additions & 2 deletions src/validate.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@ const fs = require("fs")
const f = import("rdf-ext")
const PN3 = import("@rdfjs/parser-n3")
const shacl = import("rdf-validate-shacl")
const jsonld = require("jsonld")
const datasetFactory = import("@rdfjs/dataset")
const N3 = require("n3")

async function loadDataset(filePath) {
const ParserN3 = await PN3
Expand All @@ -11,19 +14,29 @@ async function loadDataset(filePath) {
return factory.default.dataset().import(parser.import(stream))
}

async function loadDataFromJsonld(doc) {
const nquads = await jsonld.toRDF(doc, { format: "application/n-quads" })
const parser = new N3.Parser({ format: "N-Triples" })
const parsed = parser.parse(nquads)
const dFactory = await datasetFactory
const dataset = dFactory.default.dataset(parsed)
return dataset
}

/**
* Validates a file against a given shape.
* @param {string} shapePath - Path to shape file
* @param {string} filePath - Path to file to validate
* @param {Object} doc - JSON-LD document
* @returns {boolean} Validation result
* @throws {Error} Throws an error if the validation fails
*/
async function validate(shapePath, filePath) {
async function validate(shapePath, filePath, doc) {
const factory = await f
const SHACLValidator = await shacl

const shapes = await loadDataset(shapePath)
const data = await loadDataset(filePath)
const data = await loadDataFromJsonld(doc)
const validator = new SHACLValidator.default(shapes, {
factory: factory.default,
})
Expand Down
42 changes: 41 additions & 1 deletion test/validate.test.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
import { describe, expect, it } from "vitest"
import { validate } from "../src/validate"
import fs from "fs"
import jsonld from "jsonld"
import n3 from "n3"
const { DataFactory } = n3
const { namedNode } = DataFactory

describe("validate", () => {
it("Returns true for a valid SkoHub Turtle File", async () => {
Expand All @@ -10,10 +15,45 @@ describe("validate", () => {
expect(result).toBeTruthy()
})
it("Throws error for an invalid SkoHub Turtle File", async () => {
const inverses = {
"http://www.w3.org/2004/02/skos/core#narrower":
"http://www.w3.org/2004/02/skos/core#broader",
"http://www.w3.org/2004/02/skos/core#broader":
"http://www.w3.org/2004/02/skos/core#narrower",
"http://www.w3.org/2004/02/skos/core#related":
"http://www.w3.org/2004/02/skos/core#related",
"http://www.w3.org/2004/02/skos/core#hasTopConcept":
"http://www.w3.org/2004/02/skos/core#topConceptOf",
"http://www.w3.org/2004/02/skos/core#topConceptOf":
"http://www.w3.org/2004/02/skos/core#hasTopConcept",
}

jsonld.registerRDFParser("text/turtle", (ttlString) => {
const quads = new n3.Parser().parse(ttlString)
const store = new n3.Store()
store.addQuads(quads)
quads.forEach((quad) => {
quad.object.language &&
inverses[quad.predicate.id] &&
store.addQuad(
quad.object,
namedNode(inverses[quad.predicate.id]),
quad.subject,
quad.graph
)
})
return store.getQuads()
})
const f = "./test/data/ttl/invalid_hashURIConceptScheme.ttl"

const ttlString = fs.readFileSync(f).toString()
const doc = await jsonld.fromRDF(ttlString, { format: "text/turtle" })

await expect(() =>
validate(
"./shapes/skohub.shacl.ttl",
"./test/data/ttl/invalid_hashURIConceptScheme.ttl"
"./test/data/ttl/invalid_hashURIConceptScheme.ttl",
doc
)
).rejects.toThrowError()
})
Expand Down
Loading