Skip to content

Commit

Permalink
Option for SHACL cloures in RDF validation
Browse files Browse the repository at this point in the history
  • Loading branch information
avillar committed Aug 14, 2023
1 parent 8904a26 commit 2dc6ca4
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 7 deletions.
6 changes: 6 additions & 0 deletions ogc/bblocks/examples-schema.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,12 @@ items:
type: string
enum: [jsonld, ttl]
- type: 'null'
shacl-closure:
description: |
List of Turtle documents (file names or URLs) that will be used as the SHACL closure graph.
type: array
items:
type: string
oneOf:
- required:
- code
Expand Down
27 changes: 20 additions & 7 deletions ogc/bblocks/validate.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import shutil
from json import JSONDecodeError
from pathlib import Path
from typing import Any, Sequence
from typing import Any, Sequence, Callable
from urllib.parse import urlsplit
from urllib.request import urlopen

Expand Down Expand Up @@ -71,7 +71,8 @@ def _validate_resource(filename: Path,
shacl_error: str | None = None,
base_uri: str | None = None,
shacl_files: list[Path | str] | None = None,
schema_ref: str | None = None) -> ValidationReport:
schema_ref: str | None = None,
shacl_closure: list[str] | None = None) -> ValidationReport:

require_fail = filename.stem.endswith('-fail')
report = ValidationReport(require_fail)
Expand Down Expand Up @@ -201,8 +202,14 @@ def validate_inner():
'Using SHACL files for validation:\n - ' + '\n - '.join(str(f) for f in shacl_files)
)
try:
shacl_conforms, shacl_result, shacl_report, focus_nodes = shacl_validate(graph, shacl_graph)
report_add = report.add_info if shacl_conforms else report.add_error
ont_graph = None
if shacl_closure:
ont_graph = Graph()
for c in shacl_closure:
ont_graph.parse(c)
shacl_conforms, shacl_result, shacl_report, focus_nodes = shacl_validate(
graph, shacl_graph, ont_graph=ont_graph)
report_add: Callable[[str, str], None] = report.add_info if shacl_conforms else report.add_error
report_add('SHACL', shacl_report)
if focus_nodes:
focus_nodes_report = ''
Expand Down Expand Up @@ -367,6 +374,10 @@ def validate_test_resources(bblock: BuildingBlock,
with open(output_fn, 'w') as f:
f.write(code)

shacl_closure = snippet.get('shacl-closure')
if shacl_closure:
shacl_closure = [bblock.files_path.joinpath(c) for c in shacl_closure]

example_result = _validate_resource(
fn, output_fn,
resource_contents=code,
Expand All @@ -379,7 +390,8 @@ def validate_test_resources(bblock: BuildingBlock,
shacl_error=shacl_error,
base_uri=snippet.get('base-uri', example_base_uri),
shacl_files=shacl_files,
schema_ref=snippet.get('schema-ref'))
schema_ref=snippet.get('schema-ref'),
shacl_closure=shacl_closure)
result = result and not example_result.has_errors
for file_format, file_contents in example_result.uplifted_files.items():
if file_format not in snippet_langs and file_format in add_snippets_formats:
Expand Down Expand Up @@ -436,8 +448,9 @@ def validate_json(instance: Any, validator: jsonschema.Validator):
raise error


def shacl_validate(g: Graph, s: Graph) -> tuple[bool, Graph, str, dict[pyshacl.Shape, Sequence[Node]]]:
validator = pyshacl.Validator(g, shacl_graph=s, options={
def shacl_validate(g: Graph, s: Graph, ont_graph: Graph | None = None) \
-> tuple[bool, Graph, str, dict[pyshacl.Shape, Sequence[Node]]]:
validator = pyshacl.Validator(g, shacl_graph=s, ont_graph=ont_graph, options={
'advanced': True
})
focus_nodes: dict[pyshacl.Shape, Sequence[Node]] = {shape: shape.focus_nodes(g)
Expand Down

0 comments on commit 2dc6ca4

Please sign in to comment.