Skip to content

Commit 83d142f

Browse files
authored
Merge pull request #15 from eccenca/feature/allowBlacklistTypeIRIs-CMEM-6667
Blacklist type IRIs + extend documentation
2 parents 07cba6c + 76ed016 commit 83d142f

File tree

13 files changed

+937
-684
lines changed

13 files changed

+937
-684
lines changed

.copier-answers.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
# Changes here will be overwritten by Copier
2-
_commit: v7.3.0-22-g87d8a9b
2+
_commit: v8.2.0
33
_src_path: gh:eccenca/cmem-plugin-template
44
author_mail: cmempy-developer@eccenca.com
55
author_name: eccenca GmbH

.github/workflows/check.yml

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,13 @@ jobs:
2222
- name: Check out repository
2323
uses: actions/checkout@v5
2424

25+
- name: Cache Trivy DB
26+
id: cache-trivydb
27+
uses: actions/cache@v4
28+
with:
29+
path: .trivycache
30+
key: ${{ runner.os }}-trivydb
31+
2532
- name: Install Task
2633
uses: arduino/setup-task@v2
2734

@@ -61,9 +68,13 @@ jobs:
6168
run: |
6269
task check:deptry
6370
64-
- name: safety
71+
- name: trivy
72+
env:
73+
TRIVY_NO_PROGRESS: "true"
74+
TRIVY_CACHE_DIR: ".trivycache/"
75+
TRIVY_DISABLE_VEX_NOTICE: "true"
6576
run: |
66-
task check:safety
77+
task check:trivy
6778
6879
- name: Publish Test Report in Action
6980
uses: mikepenz/action-junit-report@v4

.gitlab-ci.yml

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,17 +60,24 @@ deptry:
6060
script:
6161
- task check:deptry
6262

63-
safety:
63+
trivy:
6464
stage: test
65+
variables:
66+
TRIVY_NO_PROGRESS: "true"
67+
TRIVY_CACHE_DIR: ".trivycache/"
68+
TRIVY_DISABLE_VEX_NOTICE: "true"
6569
script:
66-
- task check:safety
70+
- task check:trivy
71+
cache:
72+
paths:
73+
- .trivycache/
6774

6875
build:
6976
stage: build
7077
needs:
7178
- mypy
7279
- pytest
73-
- safety
80+
- trivy
7481
- deptry
7582
script:
7683
- task build

.pre-commit-config.yaml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
11
---
2+
default_language_version:
3+
python: python3.13
4+
25
repos:
36
- repo: local
47
hooks:
@@ -36,3 +39,9 @@ repos:
3639
stages: [post-checkout, post-merge]
3740
always_run: true
3841

42+
- id: trivy
43+
name: check:trivy
44+
description: run trivy to scan for vulnerabilities
45+
entry: task check:trivy
46+
language: python
47+
pass_filenames: false

.trivyignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# .trivyignore
2+
3+
# ignore 51358 safety - dev dependency only
4+
CVE-2022-39280

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning](https://semver.org/)
66

7+
## [Unreleased]
8+
9+
### Added
10+
11+
- Parameter to ignore specific type IRIs (blacklisting)
12+
713
## [4.2.0] 2025-10-20
814

915
### Changed

Taskfile.yaml

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ tasks:
112112
- task: check:ruff
113113
- task: check:mypy
114114
- task: check:deptry
115-
- task: check:safety
115+
- task: check:trivy
116116

117117
check:pytest:
118118
desc: Run unit and integration tests
@@ -154,12 +154,16 @@ tasks:
154154
vars:
155155
JUNIT_FILE: ./{{.DIST_DIR}}/junit-mypy.xml
156156

157-
check:safety:
158-
desc: Complain about vulnerabilities in dependencies
157+
check:trivy:
158+
desc: Scan for vulnerabilities using Trivy
159159
<<: *preparation
160160
cmds:
161-
# ignore 51358 safety - dev dependency only
162-
- poetry run safety check -i 51358
161+
- >
162+
poetry run trivy fs
163+
--include-dev-deps
164+
--scanners vuln
165+
--exit-code 1
166+
.
163167
164168
check:deptry:
165169
desc: Complain about unused or missing dependencies

cmem_plugin_shapes/doc/__init__.py

Lines changed: 0 additions & 6 deletions
This file was deleted.

cmem_plugin_shapes/doc/shapes_doc.md

Lines changed: 0 additions & 47 deletions
This file was deleted.

cmem_plugin_shapes/plugin_shapes.py

Lines changed: 75 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,6 @@
3232
from rdflib import DCTERMS, RDF, RDFS, SH, XSD, Graph, Literal, Namespace, URIRef
3333
from rdflib.namespace import split_uri
3434

35-
from cmem_plugin_shapes.doc import SHAPES_DOC
36-
3735
from . import __path__
3836

3937
SHUI = Namespace("https://vocab.eccenca.com/shui/")
@@ -73,7 +71,65 @@ def str2bool(value: str) -> bool:
7371
label=PLUGIN_LABEL,
7472
icon=Icon(file_name="shapes.svg", package=__package__),
7573
description="Generate SHACL node and property shapes from a data graph",
76-
documentation=SHAPES_DOC,
74+
documentation="""This workflow task generates SHACL (Shapes Constraint Language)
75+
node and property shapes by analyzing instance data from a knowledge graph. The generated
76+
shapes describe the structure and properties of the classes used in the data graph.
77+
78+
## Usage
79+
80+
The plugin analyzes an input data graph and creates:
81+
82+
- **Node shapes**: One for each class (`rdf:type`) used in the data graph
83+
- **Property shapes**: For all properties associated with each class, including:
84+
- Regular object properties (subject → object relationships)
85+
- Inverse object properties (object ← subject relationships, marked with ← prefix)
86+
- Datatype properties (literal values)
87+
88+
## Output
89+
90+
The generated shapes are written to a shape catalog graph with:
91+
92+
- Unique URIs based on UUIDs (UUID5 derived from class/property IRIs)
93+
- Human-readable labels and names (using namespace prefixes when available)
94+
- Metadata including source data graph reference and timestamps
95+
- Optional plugin provenance information (see advanced options)
96+
97+
## Example
98+
99+
Given a data graph with:
100+
101+
``` turtle
102+
ex:Person123 a ex:Person ;
103+
ex:name "John" ;
104+
ex:knows ex:Person456 .
105+
```
106+
107+
The plugin generates:
108+
109+
- A node shape for `ex:Person` with `sh:targetClass ex:Person`
110+
111+
``` turtle
112+
graph:90ee6e27-59b1-5ac8-9d7a-116c60c6791a a sh:NodeShape ;
113+
rdfs:label "Person (ex:)"@en ;
114+
sh:name "Person (ex:)"@en ;
115+
sh:property
116+
graph:0fcf371d-f99a-5eeb-ab50-6e6b5fbb0e06 ,
117+
graph:dd5c6728-75a2-5215-8a5d-f9cd4077aaea ;
118+
sh:targetClass ex:Person .
119+
```
120+
121+
- Property shapes for `ex:name` (datatype property) and `ex:knows` (object property)
122+
123+
``` turtle
124+
graph:0fcf371d-f99a-5eeb-ab50-6e6b5fbb0e06 a sh:PropertyShape ;
125+
rdfs:label "knows (ex:)"@en ;
126+
sh:name "knows (ex:)"@en ;
127+
sh:nodeKind sh:IRI ;
128+
sh:path ex:knows ;
129+
shui:showAlways true .
130+
```
131+
132+
""",
77133
parameters=[
78134
PluginParameter(
79135
param_type=GraphParameterType(allow_only_autocompleted_values=False),
@@ -135,6 +191,13 @@ def str2bool(value: str) -> bool:
135191
description="Provide the list of properties (as IRIs) to ignore.",
136192
advanced=True,
137193
),
194+
PluginParameter(
195+
param_type=MultilineStringParameterType(),
196+
name="ignore_types",
197+
label="Types to ignore",
198+
description="Provide the list of types (as IRIs) to ignore.",
199+
advanced=True,
200+
),
138201
PluginParameter(
139202
param_type=BoolParameterType(),
140203
name="plugin_provenance",
@@ -156,6 +219,7 @@ def __init__( # noqa: PLR0913
156219
import_shapes: bool = False,
157220
prefix_cc: bool = False,
158221
ignore_properties: str = "http://www.w3.org/1999/02/22-rdf-syntax-ns#type",
222+
ignore_types: str = "",
159223
plugin_provenance: bool = False,
160224
) -> None:
161225
if not validators.url(data_graph_iri):
@@ -191,6 +255,12 @@ def __init__( # noqa: PLR0913
191255
raise ValueError(f"Invalid property IRI ({_}) in parameter 'Properties to ignore'")
192256
self.ignore_properties.append(_)
193257

258+
self.ignore_types = []
259+
for _ in filter(None, ignore_types.split("\n")):
260+
if not validators.url(_):
261+
raise ValueError(f"Invalid type IRI ({_}) in parameter 'Types to ignore'")
262+
self.ignore_types.append(_)
263+
194264
self.plugin_provenance = plugin_provenance
195265

196266
self.shapes_count = 0
@@ -296,6 +366,7 @@ def get_class_dict(self) -> dict:
296366
?subject a ?class .
297367
?subject ?property ?object .
298368
{self.iri_list_to_filter(self.ignore_properties)}
369+
{self.iri_list_to_filter(self.ignore_types, name="class")}
299370
BIND(isLiteral(?object) AS ?data)
300371
BIND("false" AS ?inverse)
301372
}}
@@ -304,6 +375,7 @@ def get_class_dict(self) -> dict:
304375
?object a ?class .
305376
?subject ?property ?object .
306377
{self.iri_list_to_filter(self.ignore_properties)}
378+
{self.iri_list_to_filter(self.ignore_types, name="class")}
307379
BIND("false" AS ?data)
308380
BIND("true" AS ?inverse)
309381
}}

0 commit comments

Comments
 (0)