From 032c60f932b54fc9bdad81baa93214b0f6786d87 Mon Sep 17 00:00:00 2001 From: ajuvercr Date: Mon, 23 Dec 2024 07:51:02 +0100 Subject: [PATCH 1/3] 1.3.1 --- package-lock.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/package-lock.json b/package-lock.json index 7a6623c..12cb986 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "rdf-lens", - "version": "1.3.0", + "version": "1.3.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rdf-lens", - "version": "1.3.0", + "version": "1.3.1", "license": "MIT", "dependencies": { "@rdfjs/types": "^1.1.0", From 7932410d8b011c293602d0a78cc7fa5dc425a277 Mon Sep 17 00:00:00 2001 From: ajuvercr Date: Mon, 23 Dec 2024 08:53:16 +0100 Subject: [PATCH 2/3] add envReplace lens --- package.json | 2 +- src/lens.ts | 12 ++++++ src/ontology.ts | 1 + src/shacl.ts | 92 ++++++++++++++++++++++++++++++++++++++++----- test/shacl2.test.ts | 64 ++++++++++++++++++++++++++++++- 5 files changed, 160 insertions(+), 11 deletions(-) diff --git a/package.json b/package.json index 17a4752..12595f0 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rdf-lens", - "version": "1.3.1", + "version": "1.3.2", "main": "./dist/index.js", "type": "module", "exports": { diff --git a/src/lens.ts b/src/lens.ts index f5d0bb5..c0c632b 100644 --- a/src/lens.ts +++ b/src/lens.ts @@ -159,6 +159,18 @@ export class BasicLensM extends BasicLens { return this.execute(c, states).filter(fn); }); } + + reduce( + lens: BasicLens<[T, F], F>, + start: BasicLens, + ): BasicLens { + return new BasicLens((c, _, states) => { + const st = this.then(empty().and(start)).map(([ts, f]) => { + return ts.reduce((acc, v) => lens.execute([v, acc], states), f); + }); + return st.execute(c, states); + }); + } } export function pred(pred?: Term): BasicLensM { diff --git a/src/ontology.ts b/src/ontology.ts index d65827d..70331cd 100644 --- a/src/ontology.ts +++ b/src/ontology.ts @@ -42,4 +42,5 @@ export const RDFL = createUriAndTermNamespace( "EnvVariable", "envKey", "envDefault", + "datatype", ); diff --git a/src/shacl.ts b/src/shacl.ts index 47c90f3..19b8a66 100644 --- a/src/shacl.ts +++ b/src/shacl.ts @@ -1,12 +1,14 @@ -import { Quad, Term } from "@rdfjs/types"; -import { RDF, XSD } from "@treecg/types"; +import { Quad, Quad_Object, Term } from "@rdfjs/types"; +import { PROV, RDF, XSD } from "@treecg/types"; import { BasicLens, BasicLensM, Cont, empty, invPred, + match, pred, + subject, subjects, unique, } from "./lens"; @@ -14,7 +16,7 @@ import { import { DataFactory, NamedNode } from "rdf-data-factory"; import { RDFL, RDFS, SHACL } from "./ontology"; -const { literal } = new DataFactory(); +const { literal, quad } = new DataFactory(); export interface ShapeField { name: string; @@ -78,7 +80,6 @@ export function toLens( .map((x) => { const out = x.filter((x) => !!x); if (maxCount < 2) { - // console.log(out, x); return out[0]; } else { return out; @@ -288,8 +289,9 @@ function dataTypeToExtract(dataType: Term, t: Term): unknown { if (dataType.equals(XSD.terms.dateTime)) return new Date(t.value); if (dataType.equals(XSD.terms.custom("boolean"))) return t.value === "true"; if (dataType.equals(XSD.terms.custom("iri"))) return new NamedNode(t.value); - if (dataType.equals(XSD.terms.custom("anyURI"))) + if (dataType.equals(XSD.terms.custom("anyURI"))) { return new NamedNode(t.value); + } return t; } @@ -302,7 +304,7 @@ type SubClasses = { [clazz: string]: string; }; -function envLens(dataType: Term): BasicLens { +function envLens(dataType?: Term): BasicLens { const checkType = pred(RDF.terms.type) .thenSome( new BasicLens(({ id }) => { @@ -326,12 +328,18 @@ function envLens(dataType: Term): BasicLens { defaultValue: found?.id.value, })); + const envDatatype = pred(RDFL.terms.datatype) + .one(undefined) + .map((found) => ({ dt: found?.id })); + return checkType - .and(envName, defaultValue) - .map(([_thing, { key }, { defaultValue }]) => { + .and(envName, defaultValue, envDatatype) + .map(([_thing, { key }, { defaultValue }, { dt }]) => { const value = process.env[key] || defaultValue; + const thisDt = dataType || dt || XSD.terms.custom("literal"); + if (value) { - return dataTypeToExtract(dataType, literal(value)); + return dataTypeToExtract(thisDt, literal(value)); } else { throw ( "Nothing set for ENV " + @@ -342,6 +350,72 @@ function envLens(dataType: Term): BasicLens { }); } +export function sliced(): BasicLens { + return new BasicLens((x) => x.slice()); +} + +function remove_cbd(quads: Quad[], subject: Term) { + const toRemoves = [subject]; + while (toRemoves.length > 0) { + const toRemove = toRemoves.pop(); + + quads = quads.filter((q) => { + if (q.subject.equals(toRemove)) { + if (q.object.termType === "BlankNode") { + toRemoves.push(q.object); + } + return false; + } else { + return true; + } + }); + } + return quads; +} + +export function envReplace(): BasicLens { + const shouldReplace = empty<[Cont, Quad[]]>() + .map((x) => x[0]) + .then(envLens().and(empty().map((x) => x.id))) + .map(([value, id]) => ({ + value, + id, + })); + + const reduce: BasicLens<[Cont, Quad[]], Quad[]> = shouldReplace + .and(empty<[Cont, Quad[]]>().map((x) => x[1])) + .map(([{ value, id }, quads]) => { + return remove_cbd( + quads.map((q) => { + if (q.object.equals(id)) { + return quad( + q.subject, + q.predicate, + value, + q.graph, + ); + } else { + return q; + } + }), + id, + ); + }); + + const actualReplace = match( + undefined, + RDF.terms.type, + RDFL.terms.EnvVariable, + ) + .thenAll(subject) + .reduce( + reduce, + empty().map((x) => x[0].quads), + ); + + return sliced().then(actualReplace); +} + function extractLeaf(datatype: Term): BasicLens { return envLens(datatype).or( empty().map((item) => dataTypeToExtract(datatype, item.id)), diff --git a/test/shacl2.test.ts b/test/shacl2.test.ts index 9bac7c9..b02f87d 100644 --- a/test/shacl2.test.ts +++ b/test/shacl2.test.ts @@ -2,7 +2,7 @@ import { describe, expect, test } from "vitest"; import { Quad } from "@rdfjs/types"; import { RDF } from "@treecg/types"; import { Parser } from "n3"; -import { extractShapes } from "../src/shacl"; +import { envReplace, extractShapes } from "../src/shacl"; import { RDFL } from "../src/ontology"; import { BasicLensM, Cont } from "../src"; @@ -564,4 +564,66 @@ ${prefixes} expect(obj.str).toBe("true"); }); }); + + describe("Env replace works", () => { + const data = ` +${prefixes} +<1> js:test [ + a rdfl:EnvVariable; + rdfl:envDefault js:generatedAtTime; + rdfl:envKey "key1" + ]. + +<2> js:test [ + a rdfl:EnvVariable; + rdfl:envDefault js:generatedAtTime; + rdfl:envKey "key2"; + rdfl:datatype xsd:iri + ]. + +<3> js:test [ + a rdfl:EnvVariable; + rdfl:envDefault js:generatedAtTime; + rdfl:envKey "notset"; + rdfl:datatype xsd:iri + ]. +`; + + const quads = parseQuads(data); + const l1 = quads.length; + + process.env["key1"] = "true"; + process.env["key2"] = "42"; + + const lens = envReplace(); + const newQuads = lens.execute(quads); + + test("Didn't change original quads", () => { + expect(quads.length).toBe(l1); + }); + + test("Did change new quads", () => { + expect(newQuads.length).toBe(3); + }); + + test("Datatype defaults to literal", () => { + const q1 = newQuads.find((x) => x.subject.value == "1")!; + expect(q1.object.termType).toBe("Literal"); + expect(q1.object.value).toBe("true"); + }); + + test("Datatype iri happens", () => { + const q1 = newQuads.find((x) => x.subject.value == "2")!; + expect(q1.object.termType).toBe("NamedNode"); + expect(q1.object.value).toBe("42"); + }); + + test("Default value works", () => { + const q1 = newQuads.find((x) => x.subject.value == "3")!; + expect(q1.object.termType).toBe("NamedNode"); + expect(q1.object.value).toBe( + "https://w3id.org/conn/js#generatedAtTime", + ); + }); + }); }); From ba3bf091949e17cdd91a649ddb3016eb8d40827a Mon Sep 17 00:00:00 2001 From: ajuvercr Date: Tue, 24 Dec 2024 12:23:13 +0100 Subject: [PATCH 3/3] start from quads, not undefined --- package.json | 2 +- src/lens.ts | 5 +++-- src/shacl.ts | 5 +---- test/shacl2.test.ts | 10 ++++++++-- 4 files changed, 13 insertions(+), 9 deletions(-) diff --git a/package.json b/package.json index 12595f0..9ed9dc2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rdf-lens", - "version": "1.3.2", + "version": "1.3.3", "main": "./dist/index.js", "type": "module", "exports": { diff --git a/src/lens.ts b/src/lens.ts index c0c632b..ead8882 100644 --- a/src/lens.ts +++ b/src/lens.ts @@ -162,12 +162,13 @@ export class BasicLensM extends BasicLens { reduce( lens: BasicLens<[T, F], F>, - start: BasicLens, + start: BasicLens, ): BasicLens { return new BasicLens((c, _, states) => { - const st = this.then(empty().and(start)).map(([ts, f]) => { + const st = this.and(start).map(([ts, f]) => { return ts.reduce((acc, v) => lens.execute([v, acc], states), f); }); + return st.execute(c, states); }); } diff --git a/src/shacl.ts b/src/shacl.ts index 19b8a66..02490c8 100644 --- a/src/shacl.ts +++ b/src/shacl.ts @@ -408,10 +408,7 @@ export function envReplace(): BasicLens { RDFL.terms.EnvVariable, ) .thenAll(subject) - .reduce( - reduce, - empty().map((x) => x[0].quads), - ); + .reduce(reduce, empty()); return sliced().then(actualReplace); } diff --git a/test/shacl2.test.ts b/test/shacl2.test.ts index b02f87d..10ce7f4 100644 --- a/test/shacl2.test.ts +++ b/test/shacl2.test.ts @@ -568,11 +568,12 @@ ${prefixes} describe("Env replace works", () => { const data = ` ${prefixes} + . <1> js:test [ a rdfl:EnvVariable; rdfl:envDefault js:generatedAtTime; rdfl:envKey "key1" - ]. + ], . <2> js:test [ a rdfl:EnvVariable; @@ -603,7 +604,7 @@ ${prefixes} }); test("Did change new quads", () => { - expect(newQuads.length).toBe(3); + expect(newQuads.length).toBe(5); }); test("Datatype defaults to literal", () => { @@ -625,5 +626,10 @@ ${prefixes} "https://w3id.org/conn/js#generatedAtTime", ); }); + + test("Doesnt fail when no env to replace", () => { + const newNewQuads = envReplace().execute(newQuads); + expect(newNewQuads.length).toBe(newQuads.length); + }); }); });