diff --git a/src/index.ts b/src/index.ts index c5c0953..22fbfac 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,12 +4,14 @@ import { Readable } from "stream"; import formatsPretty from "@rdfjs/formats/pretty.js"; import Serializer from "@rdfjs/serializer-turtle"; import { Validator } from "shacl-engine"; +import { ShaclError } from "./error"; type ValidateArguments = { path: string; incoming: Stream; outgoing: Writer; report?: Writer; + verbose?: boolean; }; export async function validate( @@ -29,7 +31,13 @@ export async function validate( // Create shape stream. const res = await rdf.fetch(path); - const shapes = await res.dataset(); + if (!res.ok) { + throw ShaclError.fileSystemError(); + } + + const shapes = await res.dataset().catch(() => { + throw ShaclError.invalidRdfFormat(); + }); // Parse input stream using shape stream. // @ts-expect-error Factory is valid. @@ -42,16 +50,21 @@ export async function validate( // Parse data into a dataset. const rawStream = Readable.from(data); const quadStream = parser.import(rawStream); - const dataset = await rdf.dataset().import(quadStream); + const dataset = await rdf + .dataset() + .import(quadStream) + .catch(() => { + throw ShaclError.invalidRdfFormat(); + }); // Run through validator. const result = await validator.validate({ dataset }); - const resultRaw = serializer.transform(result.dataset); // Pass through data if valid. if (result.conforms) { await outgoing.push(data); } else if (report) { + const resultRaw = serializer.transform(result.dataset); await report.push(resultRaw); } }); diff --git a/tests/data/invalid.report.ttl b/tests/data/invalid.report.ttl index 482d109..4c15d08 100644 --- a/tests/data/invalid.report.ttl +++ b/tests/data/invalid.report.ttl @@ -4,14 +4,6 @@ [ a sh:ValidationReport; sh:conforms false; sh:result [ a sh:ValidationResult; - sh:focusNode ex:ValidPoint; - sh:resultMessage "Requires an integer X coordinate"; - sh:resultPath ex:x; - sh:resultSeverity sh:Violation; - sh:sourceConstraintComponent sh:DatatypeConstraintComponent; - sh:sourceShape []; - sh:value "1" - ], [ a sh:ValidationResult; sh:focusNode ex:ValidPoint; sh:resultMessage "Requires an integer Y coordinate"; sh:resultPath ex:y; @@ -27,5 +19,13 @@ sh:sourceConstraintComponent sh:ClosedConstraintComponent; sh:sourceShape ex:PointShape; sh:value "3" + ], [ a sh:ValidationResult; + sh:focusNode ex:ValidPoint; + sh:resultMessage "Requires an integer X coordinate"; + sh:resultPath ex:x; + sh:resultSeverity sh:Violation; + sh:sourceConstraintComponent sh:DatatypeConstraintComponent; + sh:sourceShape []; + sh:value "1" ] ]. diff --git a/tests/error.test.ts b/tests/error.test.ts new file mode 100644 index 0000000..56ad0fc --- /dev/null +++ b/tests/error.test.ts @@ -0,0 +1,30 @@ +import { describe, test, expect } from "vitest"; +import { validate } from "../src"; +import { SimpleStream } from "@ajuvercr/js-runner"; +import { ShaclError } from "../src/error"; + +describe("errors", () => { + test("invalid shacl file path", async () => { + expect.assertions(1); + + const func = validate({ + path: "/tmp/shacl-doesnt-exist.ttl", + incoming: new SimpleStream(), + outgoing: new SimpleStream(), + }); + + expect(func).rejects.toThrow(ShaclError.fileSystemError()); + }); + + test("invalid shacl rdf format", async () => { + expect.assertions(1); + + const func = validate({ + path: "./tests/shacl/invalid.ttl", + incoming: new SimpleStream(), + outgoing: new SimpleStream(), + }); + + expect(func).rejects.toThrow(ShaclError.invalidRdfFormat()); + }); +}); diff --git a/tests/shacl/invalid.ttl b/tests/shacl/invalid.ttl new file mode 100644 index 0000000..f472376 --- /dev/null +++ b/tests/shacl/invalid.ttl @@ -0,0 +1,25 @@ +@prefix ex: . +@prefix sh: . +@prefix rdf: . +@prefix xsd: . + +ex:PointShape + a sh:NodeShape; + sh:targetClass ex:Point; + sh:closed true; + sh:ignoredProperties (rdf:type); + sh:property [ + sh:path ex:x; + sh:message "Requires an integer X coordinate"; + sh:name "X-coordinate"; + sh:datatype xsd:integer; + sh:minCount 1; + sh:maxCount 1; + ], [ + sh:path ex:y; + sh:message "Requires an integer Y coordinate"; + sh:name "Y-coordinate"; + sh:datatype xsd:integer; + sh:minCount 1; + sh:maxCount 1; + ] # NOTE: Intentionally misses a period here!