Support the correct nodeKind determination for a limited sh:or scenario #13

Merged
jsheunis merged 3 commits from shor into main 2025-07-24 20:18:45 +00:00
4 changed files with 45 additions and 8 deletions

View file

@ -1,6 +1,6 @@
{
"name": "shacl-tulip",
"version": "0.0.3",
"version": "0.0.4",
"type": "module",
"main": "./src/index.js",
"module": "./src/index.js",

View file

@ -166,7 +166,19 @@ dlsocial:Person a sh:NodeShape ;
sh:maxCount 1 ;
sh:nodeKind sh:IRI ;
sh:order 2 ;
sh:path dlspatial:at_location ] ;
sh:path dlspatial:at_location ],
[ sh:description "The type of person, which is one of two classes: a 'Parent' or a 'Child'" ;
sh:maxCount 1 ;
sh:minCount 1 ;
sh:or ( [ sh:class ex:Parent ] [ sh:class ex:Child ] ) ;
sh:order 1 ;
sh:path dlthings:person_type ],
[ sh:description "The organization that the Person belongs to" ;
sh:maxCount 1 ;
sh:minCount 1 ;
sh:or ( [ sh:class ex:Organization ] [ sh:nodeKind sh:Literal ; sh:datatype xsd:string ] ) ;
sh:order 1 ;
sh:path dlthings:belongs_to ] ;
sh:targetClass dlsocial:Person .

View file

@ -145,6 +145,15 @@ export class ShapesDataset extends RdfDataset {
// Assume Literal nodekind for any arrays
console.log(`\t- NodeKind not found for property shape: ${property_uri}; found 'sh:in'. Setting to default literal`)
nodeFunc = literal
} else if (
propertyShape.hasOwnProperty(SHACL.or.value) &&
Array.isArray(propertyShape[SHACL.or.value]) &&
propertyShape[SHACL.or.value].every(obj => obj.hasOwnProperty(SHACL.class.value))
) {
// This is a temporary solution to exactly match the property values entered using `shacl-vue`'s `ShaclORClassEditor`
// A future replacement should account for a generic `sh:or`
console.log(`\t- NodeKind not found for property shape: ${property_uri}; found 'sh:or' with every element containing 'sh:class'. Setting to namedNode`)
nodeFunc = namedNode
}
else {
console.log(`\t- NodeKind not found for property shape: ${property_uri}. Setting to default literal`)

View file

@ -1,7 +1,7 @@
import { describe, it, expect, beforeEach} from 'vitest';
import { ShapesDataset } from '@/classes/ShapesDataset';
import { DataFactory } from 'n3';
const { literal, blankNode } = DataFactory;
import { DataFactory, NamedNode } from 'n3';
const { literal, blankNode, namedNode } = DataFactory;
import httpServer from 'http-server';
let server;
const PORT = 8082;
@ -28,7 +28,7 @@ describe('ShapesDataset', () => {
await new Promise(resolve => dataset.addEventListener('graphLoaded', resolve));
expect(dataset.data.graphLoaded).toBe(true);
expect(dataset.data.prefixesLoaded).toBe(true);
expect(dataset.data.graph.size).toBe(318); // number of quads in the mockShapes.ttl file
expect(dataset.data.graph.size).toBe(345); // number of quads in the mockShapes.ttl file
// Test content of all loaded variables
expect(dataset.data.nodeShapeNames).toEqual(
{
@ -76,16 +76,15 @@ describe('ShapesDataset', () => {
}
)
// Test getPropertyNodeKind
// expect(getPropertyNodeKind(class_uri, property_uri, id_uri))
// Test literal nodekind
var nk1 = dataset.getPropertyNodeKind(
'https://concepts.datalad.org/s/social/unreleased/Person',
'https://concepts.datalad.org/s/social/unreleased/honorific_name_prefix',
'https://concepts.datalad.org/s/things/v1/id'
)
expect(nk1[0]).toBeTypeOf('function')
expect(nk1[0]).toEqual(literal)
// Test blankNode
var nk2 = dataset.getPropertyNodeKind(
'https://concepts.datalad.org/s/social/unreleased/Person',
'https://concepts.datalad.org/s/things/v1/attributes',
@ -93,6 +92,23 @@ describe('ShapesDataset', () => {
)
expect(nk2[0]).toBeTypeOf('function')
expect(nk2[0]).toEqual(blankNode)
// Test sh:or with ALL elements in array containing sh:class
var nk3 = dataset.getPropertyNodeKind(
'https://concepts.datalad.org/s/social/unreleased/Person',
'https://concepts.datalad.org/s/things/v1/person_type',
'https://concepts.datalad.org/s/things/v1/id'
)
expect(nk3[0]).toBeTypeOf('function')
expect(nk3[0]).toEqual(namedNode)
// Test sh:or with NOT all elements in array containing sh:class
var nk4 = dataset.getPropertyNodeKind(
'https://concepts.datalad.org/s/social/unreleased/Person',
'https://concepts.datalad.org/s/things/v1/belongs_to',
'https://concepts.datalad.org/s/things/v1/id'
)
expect(nk4[0]).toBeTypeOf('function')
expect(nk4[0]).toEqual(literal)
server.close();
});