Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Issue 602: Add review mechanisms for key-uniqueness in types:Dictionary #607

Merged
merged 9 commits into from
Jul 26, 2024
102 changes: 100 additions & 2 deletions ontology/uco/types/types.ttl
Original file line number Diff line number Diff line change
Expand Up @@ -59,16 +59,47 @@ types:Dictionary
;
rdfs:subClassOf core:UcoInherentCharacterizationThing ;
rdfs:label "Dictionary"@en ;
rdfs:comment "A dictionary is list of (term/key, value) pairs with each term/key existing no more than once."@en ;
rdfs:comment "A dictionary is list of (term/key, value) pairs with each term/key having an expectation to exist no more than once. types:Dictionary alone does not validate this expectation, but validation is available. For use cases where this expectation must be validated, the subclass types:ProperDictionary should be used instead of types:Dictionary. For instances where this expectation has been found to be violated, the subclass types:ImproperDictionary should be used instead of types:Dictionary."@en ;
sh:property [
sh:class types:DictionaryEntry ;
sh:minCount "1"^^xsd:integer ;
sh:nodeKind sh:IRI ;
sh:path types:entry ;
] ;
sh:targetClass types:Dictionary ;
.

types:Dictionary-keyUniqueness-shape
a sh:NodeShape ;
sh:description "This shape is separated from the types:Dictionary class-shape in order to associate a warning-severity SPARQL-based shape."@en ;
sh:severity sh:Warning ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A key in a dictionary should appear no more than once. The value literal does. Please consider using the types:ImproperDictionary class and types:repeatsKey property."@en ;
sh:select """
PREFIX types: <https://ontology.unifiedcyberontology.org/uco/types/>
SELECT $this ?value
WHERE {
$this
types:entry/types:key ?value ;
.
FILTER NOT EXISTS {
$this
a types:ImproperDictionary ;
.
}
FILTER NOT EXISTS {
$this
a types:ProperDictionary ;
.
}
}
GROUP BY ?value
HAVING (COUNT(?value) > 1)
""" ;
] ;
sh:targetClass types:Dictionary ;
.

types:DictionaryEntry
a
owl:Class ,
Expand Down Expand Up @@ -167,11 +198,64 @@ types:Identifier
rdfs:comment "An identifier is a string conformant to the specified UUID-based format for UCO object identifiers."@en ;
.

types:ImproperDictionary
a
owl:Class ,
sh:NodeShape
;
rdfs:subClassOf types:Dictionary ;
rdfs:label "ImproperDictionary"@en ;
owl:disjointWith types:ProperDictionary ;
sh:property [
sh:datatype xsd:string ;
sh:nodeKind sh:Literal ;
sh:path types:repeatsKey ;
] ;
sh:targetClass types:ImproperDictionary ;
.

types:ImproperDictionary-disjointWith-ProperDictionary-shape
a sh:NodeShape ;
sh:message "types:ImproperDictionary and types:ProperDictionary are disjoint classes."@en ;
sh:not [
a sh:NodeShape ;
sh:class types:ProperDictionary ;
] ;
sh:targetClass types:ImproperDictionary ;
.

types:NativeFormatString
a rdfs:Datatype ;
rdfs:comment "Specifies data in its native format of some external language. The data may be encoded in Base64 per [RFC4648]. Data encoded in Base64 must be denoted as such using the encoded property."@en ;
.

types:ProperDictionary
a
owl:Class ,
sh:NodeShape
;
rdfs:subClassOf types:Dictionary ;
rdfs:label "ProperDictionary"@en ;
rdfs:comment "A proper dictionary is list of (term/key, value) pairs with each term/key existing no more than once."@en ;
owl:disjointWith types:ImproperDictionary ;
sh:sparql [
a sh:SPARQLConstraint ;
sh:message "A key in a proper dictionary can appear no more than once."@en ;
sh:select """
PREFIX types: <https://ontology.unifiedcyberontology.org/uco/types/>
SELECT $this ?value
WHERE {
$this
types:entry/types:key ?value ;
.
}
GROUP BY ?value
HAVING (COUNT(?value) > 1)
""" ;
] ;
sh:targetClass types:ProperDictionary ;
.

types:StructuredText
a rdfs:Datatype ;
rdfs:comment "Expresses string-based data in some information structuring format (e.g., HTML5)."@en ;
Expand Down Expand Up @@ -269,6 +353,20 @@ types:key
rdfs:range xsd:string ;
.

types:repeatsKey
a owl:DatatypeProperty ;
rdfs:label "repeatsKey"@en ;
rdfs:comment "A key found to be repeated in multiple dictionary entries within one dictionary."@en ;
rdfs:domain types:ImproperDictionary ;
rdfs:range xsd:string ;
.

types:repeatsKey-subjects-shape
a sh:NodeShape ;
sh:class types:ImproperDictionary ;
sh:targetSubjectsOf types:repeatsKey ;
.

types:threadNextItem
a owl:ObjectProperty ;
rdfs:subPropertyOf types:threadSuccessor ;
Expand Down
4 changes: 4 additions & 0 deletions tests/examples/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ all: \
configuration_setting_XFAIL_validation.ttl \
database_records_PASS_validation.ttl \
database_records_XFAIL_validation.ttl \
dictionary_PASS_validation.ttl \
dictionary_XFAIL_validation.ttl \
disjointedness_PASS_validation.ttl \
event_XFAIL_validation.ttl \
file_url_PASS_validation.ttl \
Expand Down Expand Up @@ -101,6 +103,8 @@ check: \
configuration_setting_XFAIL_validation.ttl \
database_records_PASS_validation.ttl \
database_records_XFAIL_validation.ttl \
dictionary_PASS_validation.ttl \
dictionary_XFAIL_validation.ttl \
disjointedness_PASS_validation.ttl \
event_XFAIL_validation.ttl \
file_url_PASS_validation.ttl \
Expand Down
26 changes: 26 additions & 0 deletions tests/examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,32 @@ Two instance data files are currently in the directory:
SHACL validation results are stored in corresponding files named `..._validation.ttl`, to present the current state of validation conditions.


## Design of the Dictionary tests

The `Dictionary` objects in the `dictionary_*.json` files cover these combinations of asserted type (proper dictionary, improper dictionary, or the generic parent class), whether a dictionary entry key is repeated in the data, and whether the `repeatsKey` property is asserted. (P/X denotes whether the instance is a PASS or XFAIL test case.)

| uuid | P/X | Dictionary type | Key repeats | repeatsKey asserted |
| --- | --- | --- | --- | --- |
| `3bb38b3e` | P | `Dictionary` | no | no |
| `e6dc9c2e` | X | `Dictionary` | no | yes |
| `e9adf6c1` | P | `Dictionary` | yes | no |
| `34ac0c49` | X | `Dictionary` | yes | yes |
| `cbc1c80d` | P | `ImproperDictionary` | no | no |
| `7fa3ea45` | P | `ImproperDictionary` | no | yes |
| `14e28425` | P | `ImproperDictionary` | yes | no |
| `a8e5e8e1` | P | `ImproperDictionary` | yes | yes |
| `eaded28e` | P | `ProperDictionary` | no | no |
| `8114819f` | X | `ProperDictionary` | no | yes |
| `b2baf8af` | X | `ProperDictionary` | yes | no |
| `f5ae2e6a` | X | `ProperDictionary` | yes | yes |

Other miscellaneous tests are added without full combinatoric review:

* `kb:ProperDictionary-f5ae2e6a-9b10-46f3-8441-30aada36aa1b` also demonstrates an XFAIL case where a key-value *pair* is repeated.
* `kb:ImproperDictionary-7fa3ea45-6426-4ad3-bb5f-7559e07adeb4` also demonstrates a PASS case where `repeatsKey`'s value is not in the supplied dictionary.
* `kb:Dictionary-5bc55661-4808-48e6-9e02-80a153eee5d3` demonstrates an XFAIL case where the disjoint `Dictionary` subtypes are both asserted.


## Design of the Relationship tests

The `Relationship` objects in the `relationship_*.json` files include a numbering scheme in their identifiers, (object class)-(lexical value)-(datatype). These track the following matrix of test cases:
Expand Down
125 changes: 125 additions & 0 deletions tests/examples/dictionary_PASS.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
{
"@context": {
"kb": "http://example.org/kb/",
"rdfs": "http://www.w3.org/2000/01/rdf-schema#",
"types": "https://ontology.unifiedcyberontology.org/uco/types/"
},
"@graph": [
{
"@id": "kb:Dictionary-3bb38b3e-d47a-43c8-8a77-afc0e6655ce1",
"@type": "types:Dictionary",
"types:entry": [
{
"@id": "kb:DictionaryEntry-b8a01d49-53c1-440f-a2d5-618b58801d37",
"@type": "types:DictionaryEntry",
"types:key": "x",
"types:value": "1"
},
{
"@id": "kb:DictionaryEntry-6cac6c2c-5d4e-45f5-b784-c029c9f9fb6d",
"@type": "types:DictionaryEntry",
"types:key": "y",
"types:value": "2"
}
]
},
{
"@id": "kb:ProperDictionary-eaded28e-0bf8-4df1-aee8-84d22c09702c",
"@type": "types:ProperDictionary",
"types:entry": [
{
"@id": "kb:DictionaryEntry-314212eb-39c4-4bf3-be3a-f07c38f0eae8",
"@type": "types:DictionaryEntry",
"types:key": "x",
"types:value": "1"
},
{
"@id": "kb:DictionaryEntry-9ec24a1a-7e99-41c9-ba7d-9d23f11babb4",
"@type": "types:DictionaryEntry",
"types:key": "y",
"types:value": "2"
}
]
},
{
"@id": "kb:ImproperDictionary-a8e5e8e1-b3de-4ac4-99dd-e36f96beea4d",
"@type": "types:ImproperDictionary",
"types:repeatsKey": "x",
"types:entry": [
{
"@id": "kb:DictionaryEntry-55786f64-534d-4e8c-8a64-616f708ea4d3",
"@type": "types:DictionaryEntry",
"types:key": "x",
"types:value": "1"
},
{
"@id": "kb:DictionaryEntry-d1a83c3d-cbe6-40b0-bb26-3527c47a01d8",
"@type": "types:DictionaryEntry",
"types:key": "x",
"types:value": "2"
}
]
},
{
"@id": "kb:Dictionary-e9adf6c1-0287-4290-95a9-c94a128d7ff6",
"@type": "types:Dictionary",
"rdfs:comment": "This dictionary, not being typed as a ProperDictionary, will not trigger a warning from having two entries keyed with value 'x'.",
"types:entry": [
{
"@id": "kb:DictionaryEntry-20431f00-64a3-4c0f-94a4-1eb09f8a6b6a",
"@type": "types:DictionaryEntry",
"types:key": "x",
"types:value": "1"
},
{
"@id": "kb:DictionaryEntry-f187ee7f-12fb-4580-966d-47bf1afd4975",
"@type": "types:DictionaryEntry",
"types:key": "x",
"types:value": "1"
}
]
},
{
"@id": "kb:ImproperDictionary-7fa3ea45-6426-4ad3-bb5f-7559e07adeb4",
"@type": "types:ImproperDictionary",
"repeatsKey": "z"
},
{
"@id": "kb:ImproperDictionary-14e28425-00c1-4f11-b2ed-21390fc0749a",
"@type": "types:ImproperDictionary",
"types:entry": [
{
"@id": "kb:DictionaryEntry-09f23642-389b-4553-b5be-283a6160f534",
"@type": "types:DictionaryEntry",
"types:key": "x",
"types:value": "1"
},
{
"@id": "kb:DictionaryEntry-7a84a0d6-d1cd-4291-afb4-c834d611898d",
"@type": "types:DictionaryEntry",
"types:key": "x",
"types:value": "2"
}
]
},
{
"@id": "kb:ImproperDictionary-cbc1c80d-1bad-4947-8459-c53ff61e8bfa",
"@type": "types:ImproperDictionary",
"rdfs:comment": "This improper dictionary has no repeated key or assertion of a repeated key. This should not trigger a data error, because the information in the graph could merely be incomplete.",
"types:entry": [
{
"@id": "kb:DictionaryEntry-ca1910ab-fa26-402a-86bb-229f490dd89a",
"@type": "types:DictionaryEntry",
"types:key": "x",
"types:value": "1"
},
{
"@id": "kb:DictionaryEntry-2a13e674-5e95-4a7a-9fac-c90417dcd97c",
"@type": "types:DictionaryEntry",
"types:key": "y",
"types:value": "2"
}
]
}
]
}
46 changes: 46 additions & 0 deletions tests/examples/dictionary_PASS_validation.ttl
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
@prefix owl: <http://www.w3.org/2002/07/owl#> .
@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> .
@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> .
@prefix sh: <http://www.w3.org/ns/shacl#> .
@prefix types: <https://ontology.unifiedcyberontology.org/uco/types/> .
@prefix xsd: <http://www.w3.org/2001/XMLSchema#> .

[]
a sh:ValidationReport ;
sh:conforms "true"^^xsd:boolean ;
sh:result [
a sh:ValidationResult ;
sh:focusNode <http://example.org/kb/Dictionary-e9adf6c1-0287-4290-95a9-c94a128d7ff6> ;
sh:resultMessage "A key in a dictionary should appear no more than once. The value literal does. Please consider using the types:ImproperDictionary class and types:repeatsKey property." ;
sh:resultSeverity sh:Warning ;
sh:sourceConstraint [
a sh:SPARQLConstraint ;
sh:message "A key in a dictionary should appear no more than once. The value literal does. Please consider using the types:ImproperDictionary class and types:repeatsKey property."@en ;
sh:select """
PREFIX types: <https://ontology.unifiedcyberontology.org/uco/types/>
SELECT $this ?value
WHERE {
$this
types:entry/types:key ?value ;
.
FILTER NOT EXISTS {
$this
a types:ImproperDictionary ;
.
}
FILTER NOT EXISTS {
$this
a types:ProperDictionary ;
.
}
}
GROUP BY ?value
HAVING (COUNT(?value) > 1)
""" ;
] ;
sh:sourceConstraintComponent sh:SPARQLConstraintComponent ;
sh:sourceShape types:Dictionary-keyUniqueness-shape ;
sh:value "x" ;
] ;
.

Loading
Loading