From cda8bcc0023ec6094bf55a95a51ab3b6f2f71870 Mon Sep 17 00:00:00 2001 From: Giacomo Citi Date: Mon, 16 Oct 2023 16:09:13 +0200 Subject: [PATCH] use a copy of cube.link shapes --- cli/lib/commands/publish.ts | 1 + cli/pipelines/publish.ttl | 51 +++++- cli/shapes.ttl | 80 +-------- cli/standalone-constraint-constraint.ttl | 217 +++++++++++++++++++++++ 4 files changed, 269 insertions(+), 80 deletions(-) create mode 100644 cli/standalone-constraint-constraint.ttl diff --git a/cli/lib/commands/publish.ts b/cli/lib/commands/publish.ts index e8d4a8292..e99b6a9ca 100644 --- a/cli/lib/commands/publish.ts +++ b/cli/lib/commands/publish.ts @@ -43,6 +43,7 @@ export default runner.create({ variable.set('metadata', $rdf.dataset()) // this should be possible as relative path in pipeline ttl but does not work variable.set('shapesPath', path.resolve(__dirname, '../../shapes.ttl')) + variable.set('cubeShapesPath', path.resolve(__dirname, '../../standalone-constraint-constraint.ttl')) if (cubeCreatorVersion) { variable.set('cubeCreatorVersion', cubeCreatorVersion) diff --git a/cli/pipelines/publish.ttl b/cli/pipelines/publish.ttl index 4f91e5070..43546694a 100644 --- a/cli/pipelines/publish.ttl +++ b/cli/pipelines/publish.ttl @@ -59,7 +59,7 @@ code:link ] ; code:arguments - [ code:name "shape" ; code:value <#loadShapes> ] , + [ code:name "shape" ; code:value <#concatShapes> ] , [ code:name "maxErrors" ; code:value 100 ] , [ code:name "onViolation" ; @@ -67,6 +67,29 @@ ] ; . +<#concatShapes> a :Pipeline, :ReadableObjectMode ; + :steps [ + :stepList + ( + <#loadAllShapes> + ) + ]. + + +<#loadAllShapes> + a :Step ; + code:implementedBy + [ + a code:EcmaScriptModule ; + code:link + ] ; + code:arguments + ( + <#loadShapes> + <#loadCubeShapes> + ) +. + <#loadShapes> a :Pipeline, :ReadableObjectMode ; :steps @@ -93,6 +116,32 @@ ) ] . +<#loadCubeShapes> + a :Pipeline, :ReadableObjectMode ; + :steps + [ + :stepList + ( + [ + a :Step ; + code:implementedBy + [ + code:link ; + a code:EcmaScriptModule ; + ] ; + code:arguments ( "cubeShapesPath"^^:VariableName ) + ] + [ + a :Step ; + code:implementedBy + [ + code:link ; + a code:EcmaScriptModule ; + ] ; + ] + ) + ] . + <#flatten> a :Step ; code:implementedBy [ diff --git a/cli/shapes.ttl b/cli/shapes.ttl index 952a250e1..2b5f2aaaa 100644 --- a/cli/shapes.ttl +++ b/cli/shapes.ttl @@ -25,7 +25,7 @@ base sh:message "cube:Cube needs at least one cube:ObservationSet" ; ] , [ sh:path cube:observationConstraint ; - sh:node ; + sh:node ; # from cube.link sh:message "cube:Cube must point to a valid cube:Constraint" ; ] ; . @@ -60,84 +60,6 @@ base ] ; . - - a sh:NodeShape ; - sh:property [ - # we assume at least 3 dimensions, otherwise we would have an empty list of dimensions - # one for cube:observedBy, one for rdf:type and at least one cube dimension - sh:path sh:property ; - sh:minCount 3 ; - sh:message "cube:Constraint needs at least {$minCount} sh:properties" ; - ] , [ - sh:path sh:property ; - sh:node ; - ] , [ - sh:path sh:closed ; - sh:hasValue true ; - ] ; -. - - a sh:NodeShape; - sh:property [ - sh:path qudt:scaleType ; - sh:in ( qudt:IntervalScale qudt:NominalScale qudt:EnumerationScale qudt:RatioScale qudt:OrdinalScale) ; - sh:message "If qudt:scale is used it needs to be within" ; - ] , [ - sh:path sh:path ; - sh:minCount 1 ; - sh:maxCount 1 ; - sh:message "a sh:path is needed on a property" ; - ] , [ - sh:path sh:in ; - sh:node ; - sh:message "sh:in needs to be a list" ; - ] , [ - sh:message "needs a schema:name" ; - sh:or ( - [ - sh:path schema:name ; - sh:minCount 1 ; - sh:datatype xsd:string ; - ] - [ - sh:path schema:name ; - sh:minCount 1 ; - sh:datatype rdf:langString ; - ] - [ - sh:path sh:path ; - sh:in (rdf:type cube:observedBy) ; - ] - ) ; - ] , [ - sh:message "needs a sh:datatype or sh:nodeKind" ; - sh:or ( - [ - sh:path sh:datatype ; - sh:minCount 1 ; - ] - [ - sh:path sh:nodeKind ; - sh:minCount 1 ; - ] - ) ; - ] ; -. - -# Testing proper rdf:list construction - a sh:NodeShape ; - sh:property [ - sh:path rdf:first ; - sh:minCount 1 ; - sh:maxCount 1 ; - ] , [ - sh:path rdf:rest ; - sh:minCount 1 ; - sh:maxCount 1 ; - sh:message "a rdf:List can only have one rdf:rest node, multiples nodes need to be linked" ; - #sh:node ; - ] ; -. a sh:NodeShape ; diff --git a/cli/standalone-constraint-constraint.ttl b/cli/standalone-constraint-constraint.ttl new file mode 100644 index 000000000..0766ddeed --- /dev/null +++ b/cli/standalone-constraint-constraint.ttl @@ -0,0 +1,217 @@ +@base . +@prefix dash: . +@prefix rdf: . +@prefix rdfs: . +@prefix schema: . +@prefix sh: . +@prefix xsd: . +@prefix cube: . +@prefix meta: . +@prefix qudt: . + +# +# This is the bare minimal SHACL shape for validating a Cube Constraint. +# All Cube Constraints should pass this validation. +# + + + a sh:NodeShape ; + sh:targetClass cube:Constraint ; + sh:property [ + # we assume at least 3 dimensions, otherwise we would have an empty list of dimensions + # one for cube:observedBy, one for rdf:type and at least one cube dimension + sh:path sh:property ; + sh:minCount 3 ; + sh:message "cube:Constraint needs at least a certain amount of sh:properties" + ] ; + sh:property [ + sh:path sh:property ; + sh:node ; + sh:message "The constraints do not validate" + ] ; + sh:property [ + sh:path sh:closed; + sh:hasValue true; + ] ; + sh:property [ + sh:path meta:inHierarchy; + sh:node ; + sh:message "meta:inHierarchy does not validate" + ] ; + . + + a sh:NodeShape; + sh:property [ + sh:path qudt:scaleType; + sh:in ( qudt:IntervalScale qudt:NominalScale qudt:EnumerationScale qudt:RatioScale qudt:OrdinalScale) ; + sh:maxCount 1; + sh:message "If qudt:scaleType is used it needs to be within ( qudt:IntervalScale qudt:NominalScale qudt:EnumerationScale qudt:RatioScale qudt:OrdinalScale )" + ]; + sh:property [ + sh:path sh:path; + sh:minCount 1; + sh:maxCount 1; + sh:message "a sh:path is needed on a property" + ]; + sh:property [ + sh:path sh:minInclusive; + sh:nodeType sh:Literal; + sh:message "sh:minInclusive needs to be a literal" + ]; + sh:property [ + sh:path sh:in; + sh:node ; + sh:message "sh:in needs to be a list" + ]; + sh:property [ + sh:path sh:maxInclusive; + sh:nodeType sh:Literal; + sh:message "sh:maxInclusive needs to be a literal" + ]; + sh:property [ + sh:path meta:dimensionRelation; + sh:node ; + sh:message "meta:dimensionRelation does not validate" + ]; + + sh:property [ + sh:message "needs a schema:name" ; + sh:or( + [ + sh:path schema:name; + sh:minCount 1; + sh:or ( [ sh:datatype xsd:string ] [ sh:datatype rdf:langString ] ) ; + ] + [ + sh:path sh:path; + sh:in (rdf:type cube:observedBy); + ] + ); + ]; + + sh:property [ + sh:message "needs a sh:datatype, sh:nodeKind or sh:datatype within sh:or (...)" ; + sh:or( + [ + sh:path sh:datatype; + sh:minCount 1; + ] + [ + sh:path sh:nodeKind; + sh:minCount 1; + ] + [ + sh:path sh:or; + sh:minCount 1; + sh:node ; + sh:node [ + sh:path ( [ sh:zeroOrMorePath rdf:rest ] rdf:first ) ; # all list elements + sh:property [ sh:path sh:datatype ; sh:minCount 1 ] ; # have at least one datatype + ] + ] + ); + ] ; + +. + + + a sh:NodeShape ; + sh:property [ + sh:path meta:relatesTo; + sh:nodeKind sh:IRI ; + sh:minCount 1; + sh:message "meta:dimensionRelation requires at least one meta:relatesTo"; + ] . + + +# Testing proper rdf:list construction + a sh:NodeShape ; + sh:targetClass rdf:List ; + sh:property [ + sh:path rdf:first; + sh:minCount 1; + sh:maxCount 1; + ]; + sh:property [ + sh:path rdf:rest; + sh:minCount 1; + sh:maxCount 1; + sh:or ( + [ + sh:node ; + ] + [ + sh:hasValue rdf:nil; + ] + ) ; + sh:message "a rdf:List can only have one rdf:rest node, multiples nodes need to be linked" + #sh:node ; + ] + . + + a sh:NodeShape ; + sh:property [ + sh:path sh:inversePath; + sh:nodeKind sh:IRI; + sh:minCount 1; + sh:maxCount 1; + sh:message "nextInHierarchy requires sh:path to be IRI or [ sh:inversePath ... ]" + ] . + + a sh:NodeShape ; + sh:property [ + sh:path sh:path; + sh:minCount 1; + sh:maxCount 1; + sh:message "nextInHierarchy requires exactly one sh:path" + ], + [ + sh:path schema:name; + sh:minCount 1; + sh:message "nextInHierarchy requires schema:name" + ], + [ + sh:path sh:targetClass; + sh:nodeKind sh:IRI; + sh:message "meta:nextInHierarchy/sh:targetClass must be an IRI" + ], + [ + sh:path meta:nextInHierarchy; + sh:node ; + sh:severity sh:Info; + sh:message "nextInHierarchy can have nested nodes" + ] ; + sh:or ( + [ + sh:path sh:path; + sh:nodeKind sh:IRI; + sh:message "nextInHierarchy requires sh:path to be IRI or [ sh:inversePath ... ]" + ] + [ + sh:path sh:path; + sh:nodeKind sh:BlankNode; + sh:node ; + sh:message "nextInHierarchy requires sh:path to be IRI or [ sh:inversePath ... ]" + ] + ) + . + + a sh:NodeShape ; + sh:targetClass meta:Hierarchy ; + sh:property [ + sh:path meta:hierarchyRoot; + sh:minCount 1; + sh:nodeKind sh:IRI; + sh:message "inHierarchy requires hierarchyRoot"; + ], + [ + sh:path schema:name; + sh:minCount 1; + sh:message "inHierarchy requires schema:name" + ], + [ + sh:path meta:nextInHierarchy; + sh:node ; + sh:minCount 1; + sh:message "inHierarchy requires a conform nextInHierarchy" + ] . \ No newline at end of file