diff --git a/.changeset/lucky-doors-brush.md b/.changeset/lucky-doors-brush.md new file mode 100644 index 0000000..65b6db4 --- /dev/null +++ b/.changeset/lucky-doors-brush.md @@ -0,0 +1,8 @@ +--- +"cube-link": patch +--- + +Improve validation of `meta:dimensionRelation`: + +1. Check that upper/lower bound has at most one `dcterms:type` +2. Check that `meta:relatesTo` is actually a dimension diff --git a/test/standalone-constraint-constraint/invalid.dimensionRelation-multipleDcType.ttl b/test/standalone-constraint-constraint/invalid.dimensionRelation-multipleDcType.ttl new file mode 100644 index 0000000..d6ea241 --- /dev/null +++ b/test/standalone-constraint-constraint/invalid.dimensionRelation-multipleDcType.ttl @@ -0,0 +1,63 @@ +PREFIX dcterms: +@prefix relation: . +@prefix meta: . +@prefix rdf: . +@prefix cube: . +@prefix observation: . +@prefix sh: . +@prefix xsd: . +@prefix schema: . +@base . + + a cube:Cube ; + cube:observationConstraint ; + cube:observationSet . + + cube:observation , , . + + a cube:Observation ; + cube:observedBy ; + 4.9 ; + 0.1 ; +. + + a cube:Constraint ; + sh:targetClass cube:Observation ; + sh:closed true ; + sh:property + [ + sh:path rdf:type ; + sh:nodeKind sh:IRI ; + sh:minCount 1 ; + sh:maxCount 1 + ] ; + sh:property + [ + sh:path cube:observedBy ; ; + sh:nodeKind sh:IRI ; + sh:minCount 1 ; + sh:maxCount 1 + ] ; + sh:property + [ + a cube:MeasureDimension ; + sh:datatype xsd:decimal ; + sh:path ; + schema:name "dimension" ; + sh:minCount 1 ; + sh:maxCount 1 ; + ], + [ + sh:datatype xsd:decimal ; + sh:path ; + schema:name "upper confidence" ; + sh:minCount 1 ; + sh:maxCount 1 ; + meta:dimensionRelation + [ + a relation:ConfidenceUpperBound ; + dcterms:type "Confidence interval", "bogus" ; + meta:relatesTo ; + ] ; + ]; +. diff --git a/test/standalone-constraint-constraint/invalid.dimensionRelation-multipleDcType.ttl.approved.txt b/test/standalone-constraint-constraint/invalid.dimensionRelation-multipleDcType.ttl.approved.txt new file mode 100644 index 0000000..2ce2feb --- /dev/null +++ b/test/standalone-constraint-constraint/invalid.dimensionRelation-multipleDcType.ttl.approved.txt @@ -0,0 +1,21 @@ +@prefix sh: . +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix schema: . +@prefix cube: . + +_:report a sh:ValidationReport ; + sh:result [ + rdf:type sh:ValidationResult ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MaxCountConstraintComponent ; + sh:sourceShape [ + sh:path ; + sh:maxCount 1 ; + ] ; + sh:focusNode _:b5 ; + sh:resultPath ; + sh:resultMessage "More than 1 values" ; + ] ; + sh:conforms false . diff --git a/test/standalone-constraint-constraint/invalid.dimensionRelation-relatesToNotMatching.ttl b/test/standalone-constraint-constraint/invalid.dimensionRelation-relatesToNotMatching.ttl new file mode 100644 index 0000000..7c27982 --- /dev/null +++ b/test/standalone-constraint-constraint/invalid.dimensionRelation-relatesToNotMatching.ttl @@ -0,0 +1,62 @@ +@prefix dcterms: . +@prefix relation: . +@prefix meta: . +@prefix rdf: . +@prefix cube: . +@prefix observation: . +@prefix sh: . +@prefix xsd: . +@prefix schema: . +@base . + + a cube:Cube ; + cube:observationConstraint ; + cube:observationSet . + + cube:observation , , . + + a cube:Observation ; + cube:observedBy ; + 4.9 ; + 0.1 ; +. + + a cube:Constraint ; + sh:targetClass cube:Observation ; + sh:closed true ; + sh:property + [ + sh:path rdf:type ; + sh:nodeKind sh:IRI ; + sh:minCount 1 ; + sh:maxCount 1 + ] ; + sh:property + [ + sh:path cube:observedBy ; ; + sh:nodeKind sh:IRI ; + sh:minCount 1 ; + sh:maxCount 1 + ] ; + sh:property + [ + sh:datatype xsd:decimal ; + sh:path ; + schema:name "dimension" ; + sh:minCount 1 ; + sh:maxCount 1 ; + ], + [ + sh:datatype xsd:decimal ; + sh:path ; + schema:name "upper confidence" ; + sh:minCount 1 ; + sh:maxCount 1 ; + meta:dimensionRelation + [ + a relation:ConfidenceUpperBound ; + dcterms:type "Confidence interval" ; + meta:relatesTo ; + ] ; + ]; +. diff --git a/test/standalone-constraint-constraint/invalid.dimensionRelation-relatesToNotMatching.ttl.approved.txt b/test/standalone-constraint-constraint/invalid.dimensionRelation-relatesToNotMatching.ttl.approved.txt new file mode 100644 index 0000000..4bac4dc --- /dev/null +++ b/test/standalone-constraint-constraint/invalid.dimensionRelation-relatesToNotMatching.ttl.approved.txt @@ -0,0 +1,46 @@ +@prefix sh: . +@prefix rdf: . +@prefix rdfs: . +@prefix xsd: . +@prefix schema: . +@prefix cube: . + +_:report a sh:ValidationReport ; + sh:result [ + rdf:type sh:ValidationResult ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:NodeConstraintComponent ; + sh:sourceShape [ + sh:path sh:property ; + sh:node ; + ] ; + sh:focusNode ; + sh:resultPath sh:property ; + sh:resultMessage "Value does not have shape " ; + sh:detail [ + rdf:type sh:ValidationResult ; + sh:resultSeverity sh:Violation ; + sh:sourceConstraintComponent sh:MinCountConstraintComponent ; + sh:sourceShape [ + sh:path _:b675 ; + sh:class cube:Constraint ; + sh:minCount 1 ; + sh:message "value of meta:relatesTo must be a cube dimension" ; + ] ; + sh:focusNode _:b5 ; + sh:resultPath _:b675 ; + sh:resultMessage "value of meta:relatesTo must be a cube dimension" ; + ] ; + sh:value _:b4 ; + ] ; + sh:conforms false . + +_:b675 rdf:first ; + rdf:rest ( + [ + sh:inversePath sh:path ; + ] + [ + sh:inversePath sh:property ; + ] + ) . diff --git a/test/standalone-constraint-constraint/invalid.malformedList.ttl.approved.txt b/test/standalone-constraint-constraint/invalid.malformedList.ttl.approved.txt index 97be214..e9aa921 100644 --- a/test/standalone-constraint-constraint/invalid.malformedList.ttl.approved.txt +++ b/test/standalone-constraint-constraint/invalid.malformedList.ttl.approved.txt @@ -10,7 +10,7 @@ _:report a sh:ValidationReport ; rdf:type sh:ValidationResult ; sh:resultSeverity sh:Violation ; sh:sourceConstraintComponent sh:MinCountConstraintComponent ; - sh:sourceShape _:b679 ; + sh:sourceShape _:b690 ; sh:focusNode _:b6 ; sh:resultPath rdf:rest ; sh:resultMessage "list node needs exactly one rdf:rest" ; @@ -41,7 +41,7 @@ _:report a sh:ValidationReport ; rdf:type sh:ValidationResult ; sh:resultSeverity sh:Violation ; sh:sourceConstraintComponent sh:MinCountConstraintComponent ; - sh:sourceShape _:b679 ; + sh:sourceShape _:b690 ; sh:focusNode _:b6 ; sh:resultPath rdf:rest ; sh:resultMessage "list node needs exactly one rdf:rest" ; @@ -60,7 +60,7 @@ _:report a sh:ValidationReport ; ] ; sh:conforms false . -_:b679 sh:path rdf:rest ; +_:b690 sh:path rdf:rest ; sh:maxCount 1 ; sh:minCount 1 ; sh:message "list node needs exactly one rdf:rest" . diff --git a/test/standalone-constraint-constraint/valid.dimensionRelation.ttl b/test/standalone-constraint-constraint/valid.dimensionRelation.ttl new file mode 100644 index 0000000..ef32bcd --- /dev/null +++ b/test/standalone-constraint-constraint/valid.dimensionRelation.ttl @@ -0,0 +1,77 @@ +PREFIX dcterms: +@prefix relation: . +@prefix meta: . +@prefix rdf: . +@prefix cube: . +@prefix observation: . +@prefix sh: . +@prefix xsd: . +@prefix schema: . +@base . + + a cube:Cube ; + cube:observationConstraint ; + cube:observationSet . + + cube:observation , , . + + a cube:Observation ; + cube:observedBy ; + 4.9 ; + 0.1 ; + 0.15 ; +. + + a cube:Constraint ; + sh:targetClass cube:Observation ; + sh:closed true ; + sh:property + [ + sh:path rdf:type ; + sh:nodeKind sh:IRI ; + sh:minCount 1 ; + sh:maxCount 1 + ] ; + sh:property + [ + sh:path cube:observedBy ; ; + sh:nodeKind sh:IRI ; + sh:minCount 1 ; + sh:maxCount 1 + ] ; + sh:property + [ + a cube:MeasureDimension ; + sh:datatype xsd:decimal ; + sh:path ; + schema:name "dimension" ; + sh:minCount 1 ; + sh:maxCount 1 ; + ], + [ + sh:datatype xsd:decimal ; + sh:path ; + schema:name "upper confidence" ; + sh:minCount 1 ; + sh:maxCount 1 ; + meta:dimensionRelation + [ + a relation:ConfidenceUpperBound ; + dcterms:type "Confidence interval" ; + meta:relatesTo ; + ] ; + ], + [ + sh:datatype xsd:decimal ; + sh:path ; + schema:name "lower confidence" ; + sh:minCount 1 ; + sh:maxCount 1 ; + meta:dimensionRelation + [ + a relation:ConfidenceLowerBound ; + dcterms:type "Confidence interval" ; + meta:relatesTo ; + ] ; + ] ; +. diff --git a/validation/standalone-constraint-constraint.ttl b/validation/standalone-constraint-constraint.ttl index bc3c56b..ebd84ae 100644 --- a/validation/standalone-constraint-constraint.ttl +++ b/validation/standalone-constraint-constraint.ttl @@ -1,3 +1,4 @@ +@prefix dcterms: . @prefix : . @prefix dash: . @prefix rdf: . @@ -8,6 +9,7 @@ @prefix cube: . @prefix meta: . @prefix qudt: . +@prefix relation: . # # This is the bare minimal SHACL shape for validating a Cube Constraint. @@ -179,17 +181,47 @@ . :DimensionRelation a sh:NodeShape ; - sh:property [ + sh:property + [ sh:path meta:dimensionRelation; - sh:property [ - sh:path meta:relatesTo; - sh:nodeKind sh:IRI ; - sh:minCount 1; - sh:message "meta:dimensionRelation requires at least one meta:relatesTo"; - ] ; + sh:property + [ + sh:path meta:relatesTo ; + sh:nodeKind sh:IRI ; + sh:minCount 1 ; + sh:message "meta:dimensionRelation requires at least one meta:relatesTo" ; + ], + [ + sh:path + ( + meta:relatesTo + [ sh:inversePath sh:path ] + [ sh:inversePath sh:property ] + ) ; + sh:minCount 1 ; + sh:class cube:Constraint ; + sh:message "value of meta:relatesTo must be a cube dimension" ; + ], + [ + sh:path + ( + meta:relatesTo + [ sh:inversePath sh:path ] + ) ; + sh:class cube:MeasureDimension ; + sh:message "value of meta:relatesTo must point to measure dimension " ; + ] ; ] ; . +:Confidence a sh:NodeShape ; + sh:targetClass relation:ConfidenceLowerBound, relation:ConfidenceUpperBound ; + sh:property + [ + sh:path dcterms:type ; + sh:maxCount 1 ; + ] ; +. # Testing proper rdf:list construction