From 350fa0a962ba49b0db9cb6186ab575a1bb49c8c9 Mon Sep 17 00:00:00 2001
From: Douglas Lowe <10961945+douglowe@users.noreply.github.com>
Date: Thu, 5 Nov 2026 16:39:25 +0000
Subject: [PATCH 1/5] roc validator version 0.13.1, plus other package updates
---
pyproject.toml | 12 ++++++------
requirements-dev.txt | 27 +++++++++++++--------------
requirements.txt | 27 +++++++++++++--------------
3 files changed, 32 insertions(+), 34 deletions(-)
diff --git a/pyproject.toml b/pyproject.toml
index fed3923..feb5224 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -13,15 +13,15 @@ authors = [{ name = "eScience Lab, The University of Manchester" }]
# Direct runtime dependencies:
dependencies = [
- "celery==5.6.3",
- "boto3==1.43.97",
+ "celery==5.7.0",
+ "boto3==1.43.124",
"requests==2.34.2",
"Flask==3.1.3",
- "Werkzeug==3.1.8",
+ "Werkzeug==3.1.9",
"redis==8.1.0",
- "python-dotenv==1.2.3",
- "apiflask==3.1.2",
- "roc-validator==0.11.4",
+ "python-dotenv==1.2.4",
+ "apiflask==3.1.3",
+ "roc-validator==0.13.1",
]
[project.optional-dependencies]
diff --git a/requirements-dev.txt b/requirements-dev.txt
index d984ee4..615d7d7 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -4,27 +4,29 @@
#
# pip-compile --extra=dev --no-index --output-file=requirements-dev.txt
#
-amqp==5.3.1
+amqp==5.4.2
# via kombu
annotated-types==0.8.0
# via pydantic
-apiflask==3.1.2
+apiflask==3.1.3
# via ro-crate-validation-service (pyproject.toml)
apispec==6.10.0
# via apiflask
+async-timeout==5.0.1
+ # via redis
attrs==26.1.0
# via
# cattrs
# requests-cache
-billiard==4.3.0
+billiard==4.3.2
# via celery
blinker==1.9.0
# via flask
-boto3==1.43.97
+boto3==1.43.124
# via
# moto
# ro-crate-validation-service (pyproject.toml)
-botocore==1.43.97
+botocore==1.43.124
# via
# boto3
# moto
@@ -33,7 +35,7 @@ build==1.6.1
# via pip-tools
cattrs==26.2.0
# via requests-cache
-celery==5.6.3
+celery==5.7.0
# via ro-crate-validation-service (pyproject.toml)
certifi==2026.7.22
# via requests
@@ -55,7 +57,7 @@ click-didyoumean==0.3.1
# via celery
click-plugins==1.1.1.2
# via celery
-click-repl==0.4.0
+click-repl==0.4.1
# via celery
colorlog==6.12.0
# via roc-validator
@@ -98,7 +100,7 @@ jmespath==1.1.0
# via
# boto3
# botocore
-kombu==5.6.2
+kombu==5.7.0
# via celery
markdown-it-py==4.2.0
# via rich
@@ -122,7 +124,6 @@ packaging==26.3
# via
# apispec
# build
- # kombu
# pyshacl
# pytest
# webargs
@@ -172,7 +173,7 @@ python-dateutil==2.9.0.post0
# via
# botocore
# celery
-python-dotenv==1.2.3
+python-dotenv==1.2.4
# via ro-crate-validation-service (pyproject.toml)
pyyaml==6.0.3
# via
@@ -202,7 +203,7 @@ rich==13.9.4
# roc-validator
rich-click==1.9.9
# via roc-validator
-roc-validator==0.11.4
+roc-validator==0.13.1
# via ro-crate-validation-service (pyproject.toml)
ruff==0.16.8
# via ro-crate-validation-service (pyproject.toml)
@@ -222,8 +223,6 @@ typing-extensions==4.16.0
# typing-inspection
typing-inspection==0.4.4
# via pydantic
-tzdata==2026.4
- # via kombu
tzlocal==5.4.4
# via celery
url-normalize==3.0.0
@@ -245,7 +244,7 @@ wcwidth==0.8.4
# prompt-toolkit
webargs==8.7.1
# via apiflask
-werkzeug==3.1.8
+werkzeug==3.1.9
# via
# flask
# moto
diff --git a/requirements.txt b/requirements.txt
index 397703e..224388d 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -4,31 +4,33 @@
#
# pip-compile --no-index --output-file=requirements.txt
#
-amqp==5.3.1
+amqp==5.4.2
# via kombu
annotated-types==0.8.0
# via pydantic
-apiflask==3.1.2
+apiflask==3.1.3
# via ro-crate-validation-service (pyproject.toml)
apispec==6.10.0
# via apiflask
+async-timeout==5.0.1
+ # via redis
attrs==26.1.0
# via
# cattrs
# requests-cache
-billiard==4.3.0
+billiard==4.3.2
# via celery
blinker==1.9.0
# via flask
-boto3==1.43.97
+boto3==1.43.124
# via ro-crate-validation-service (pyproject.toml)
-botocore==1.43.97
+botocore==1.43.124
# via
# boto3
# s3transfer
cattrs==26.2.0
# via requests-cache
-celery==5.6.3
+celery==5.7.0
# via ro-crate-validation-service (pyproject.toml)
certifi==2026.7.22
# via requests
@@ -47,7 +49,7 @@ click-didyoumean==0.3.1
# via celery
click-plugins==1.1.1.2
# via celery
-click-repl==0.4.0
+click-repl==0.4.1
# via celery
colorlog==6.12.0
# via roc-validator
@@ -86,7 +88,7 @@ jmespath==1.1.0
# via
# boto3
# botocore
-kombu==5.6.2
+kombu==5.7.0
# via celery
markdown-it-py==4.2.0
# via rich
@@ -107,7 +109,6 @@ owlrl==7.6.2
packaging==26.3
# via
# apispec
- # kombu
# pyshacl
# webargs
pfzy==0.3.4
@@ -136,7 +137,7 @@ python-dateutil==2.9.0.post0
# via
# botocore
# celery
-python-dotenv==1.2.3
+python-dotenv==1.2.4
# via ro-crate-validation-service (pyproject.toml)
rdflib[html]==7.6.0
# via
@@ -158,7 +159,7 @@ rich==13.9.4
# roc-validator
rich-click==1.9.9
# via roc-validator
-roc-validator==0.11.4
+roc-validator==0.13.1
# via ro-crate-validation-service (pyproject.toml)
s3transfer==0.19.2
# via boto3
@@ -176,8 +177,6 @@ typing-extensions==4.16.0
# typing-inspection
typing-inspection==0.4.4
# via pydantic
-tzdata==2026.4
- # via kombu
tzlocal==5.4.4
# via celery
url-normalize==3.0.0
@@ -198,7 +197,7 @@ wcwidth==0.8.4
# prompt-toolkit
webargs==8.7.1
# via apiflask
-werkzeug==3.1.8
+werkzeug==3.1.9
# via
# flask
# ro-crate-validation-service (pyproject.toml)
From 0c7a37f77f0e03aaa1299662a7a49c31a4325977 Mon Sep 17 00:00:00 2001
From: Douglas Lowe <10961945+douglowe@users.noreply.github.com>
Date: Fri, 6 Nov 2026 10:02:03 +0000
Subject: [PATCH 2/5] test crate update profile to 1.3
---
tests/data/ro-crate-metadata.json | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/tests/data/ro-crate-metadata.json b/tests/data/ro-crate-metadata.json
index 957ddea..cef48fb 100644
--- a/tests/data/ro-crate-metadata.json
+++ b/tests/data/ro-crate-metadata.json
@@ -1,5 +1,5 @@
{
- "@context": "https://w3id.org/ro/crate/1.1/context",
+ "@context": "https://w3id.org/ro/crate/1.3/context",
"@graph": [
{
"@id": "./",
@@ -32,7 +32,7 @@
"@id": "./"
},
"conformsTo": {
- "@id": "https://w3id.org/ro/crate/1.1"
+ "@id": "https://w3id.org/ro/crate/1.3"
}
},
{
From 8918a731bf62d7a2ba1d897d6c02b59e23acf3e6 Mon Sep 17 00:00:00 2001
From: Douglas Lowe <10961945+douglowe@users.noreply.github.com>
Date: Fri, 6 Nov 2026 10:06:21 +0000
Subject: [PATCH 3/5] clean test ro-crate profiles to avoid duplicate
requirement checks
---
.../alpha-crate/1_root_data_entity.ttl | 15 +-
.../ro-crate/may/4_data_entity_metadata.ttl | 89 ----
.../ro-crate/may/61_license_entity.ttl | 66 ---
.../ro-crate/must/0_file_descriptor_format.py | 401 ------------------
.../must/1_file-descriptor_metadata.ttl | 100 -----
.../must/2_root_data_entity_metadata.ttl | 175 --------
.../ro-crate/must/4_data_entity_metadata.py | 75 ----
.../ro-crate/must/4_data_entity_metadata.ttl | 216 ----------
.../must/5_web_data_entity_metadata.ttl | 50 ---
.../ro-crate/must/6_contextual_entity.ttl | 81 ----
.../ro-crate/ontology.ttl | 67 ---
.../ro-crate/prefixes.ttl | 49 ---
.../ro-crate/profile.ttl | 74 ----
.../should/2_root_data_entity_metadata.ttl | 74 ----
.../should/2_root_data_entity_relative_uri.py | 42 --
.../should/4_data_entity_existence.py | 58 ---
.../should/4_data_entity_metadata.ttl | 69 ---
.../should/5_web_data_entity_metadata.py | 73 ----
.../should/5_web_data_entity_metadata.ttl | 63 ---
.../should/6_contextual_entity_metadata.ttl | 75 ----
20 files changed, 3 insertions(+), 1909 deletions(-)
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/may/4_data_entity_metadata.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/may/61_license_entity.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/must/0_file_descriptor_format.py
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/must/1_file-descriptor_metadata.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/must/2_root_data_entity_metadata.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/must/4_data_entity_metadata.py
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/must/4_data_entity_metadata.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/must/5_web_data_entity_metadata.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/must/6_contextual_entity.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/ontology.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/prefixes.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/profile.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/should/2_root_data_entity_metadata.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/should/2_root_data_entity_relative_uri.py
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/should/4_data_entity_existence.py
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/should/4_data_entity_metadata.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/should/5_web_data_entity_metadata.py
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/should/5_web_data_entity_metadata.ttl
delete mode 100644 tests/data/rocrate_validator_profiles/ro-crate/should/6_contextual_entity_metadata.ttl
diff --git a/tests/data/rocrate_validator_profiles/alpha-crate/1_root_data_entity.ttl b/tests/data/rocrate_validator_profiles/alpha-crate/1_root_data_entity.ttl
index af82b5d..6923d34 100644
--- a/tests/data/rocrate_validator_profiles/alpha-crate/1_root_data_entity.ttl
+++ b/tests/data/rocrate_validator_profiles/alpha-crate/1_root_data_entity.ttl
@@ -29,18 +29,9 @@ alpha-crate:RootDataEntityRequiredProperties
sh:property [
a sh:PropertyShape ;
- sh:name "sourceOrganization" ;
- sh:path schema:sourceOrganization;
+ sh:name "datePublished" ;
+ sh:path schema:datePublished;
sh:minCount 1 ;
sh:severity sh:Violation ;
- sh:message """The Root Data Entity MUST have a `sourceOrganization` property.""" ;
- ] ;
-
- sh:property [
- a sh:PropertyShape ;
- sh:name "sourceOrganization" ;
- sh:path schema:sourceOrganization ;
- sh:class schema:Project ;
- sh:severity sh:Violation ;
- sh:message """The `sourceOrganization` property of the RootDataEntity MUST point to a Project entity.""" ;
+ sh:message """The Root Data Entity MUST have a `datePublished` property.""" ;
] .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/may/4_data_entity_metadata.ttl b/tests/data/rocrate_validator_profiles/ro-crate/may/4_data_entity_metadata.ttl
deleted file mode 100644
index 4dfd242..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/may/4_data_entity_metadata.ttl
+++ /dev/null
@@ -1,89 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix rdf: .
-@prefix sh: .
-@prefix xsd: .
-@prefix owl: .
-@prefix schema: .
-@prefix validator: .
-
-ro-crate:FileDataEntityWebOptionalProperties a sh:NodeShape ;
- sh:name "File Data Entity with web presence: OPTIONAL properties" ;
- sh:description """A File Data Entity which have a corresponding web presence,
- for instance a landing page that describes the file, including persistence identifiers (e.g. DOI),
- resolving to an intermediate HTML page instead of the downloadable file directly.
- These can included for File Data Entities as additional metadata by using the properties:
- `identifier`, `url`, `subjectOf`and `mainEntityOfPage`""" ;
- sh:targetClass ro-crate:File ;
- # Check if the Web-based Data Entity has a contentSize property
- sh:property [
- a sh:PropertyShape ;
- sh:minCount 1 ;
- sh:name "File Data Entity: optional formal `identifier` (e.g. DOI)" ;
- sh:description """Check if the File Data Entity has a formal identifier string such as a DOI""" ;
- sh:path schema:identifier ;
- sh:datatype xsd:anyURI ;
- sh:severity sh:Info ;
- sh:message """The File Data Entity MAY have a formal identifier specified through an `identifier` property""" ;
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:minCount 1 ;
- sh:name "File Data Entity: optional `url` property" ;
- sh:description """Check if the File Data Entity has an optional `download` link""" ;
- sh:path schema:url ;
- sh:datatype xsd:anyURI ;
- sh:severity sh:Info ;
- sh:message """The File Data Entity MAY use a `url` property to denote a `download` link""" ;
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:minCount 1 ;
- sh:name "File Data Entity: optional `subjectOf` property" ;
- sh:description """Check if the File Data Entity includes a `subjectOf` property to link `CreativeWork` instances that mention it.""" ;
- sh:path schema:subjectOf ;
- sh:class schema:WebPage, schema:CreativeWork ;
- sh:severity sh:Info ;
- sh:message """The File Data Entity MAY include a `subjectOf` property to link `CreativeWork` instances that mention it.""" ;
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:minCount 1 ;
- sh:name "File Data Entity: optional `mainEntityOfPage` property" ;
- sh:description """Check if the File Data Entity has a `mainEntityOfPage` property""" ;
- sh:path schema:mainEntityOfPage ;
- sh:class schema:WebPage, schema:CreativeWork ;
- sh:severity sh:Info ;
- sh:message """The File Data Entity MAY have a `mainEntityOfPage` property""" ;
- ] .
-
-
-ro-crate:DirectoryDataEntityWebOptionalDistribution a sh:NodeShape ;
- sh:name "Directory Data Entity: OPTIONAL `distribution` property" ;
- sh:description """A Directory Data Entity MAY have a `distribution` property to denote the distribution of the files within the directory""" ;
- sh:targetClass ro-crate:File ;
- # Check if the Web-based Data Entity has a contentSize property
- sh:property [
- a sh:PropertyShape ;
- sh:minCount 1 ;
- sh:name "Directory Data Entity: optional `distribution` property" ;
- sh:description """Check if the Directory Data Entity has a `distribution` property""" ;
- sh:path schema:distribution ;
- sh:datatype xsd:anyURI ;
- sh:severity sh:Info ;
- sh:message """The Directory Data Entity MAY have a `distribution` property to denote the distribution of the files within the directory""" ;
- ] .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/may/61_license_entity.ttl b/tests/data/rocrate_validator_profiles/ro-crate/may/61_license_entity.ttl
deleted file mode 100644
index 202af89..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/may/61_license_entity.ttl
+++ /dev/null
@@ -1,66 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix rdf: .
-@prefix schema_org: .
-@prefix sh: .
-@prefix xsd: .
-
-
-ro-crate:LicenseOptionalAllowedValues a sh:NodeShape ;
- sh:name "Root Data Entity: optional properties" ;
- sh:description """Define the optional properties for the Root Data Entity (e.g., license)""" ;
- sh:targetClass ro-crate:RootDataEntity ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "License" ;
- sh:description """Check if the RO-Crate has a license property with a URI or a textual description""" ;
- sh:message """MAY have a URI (eg for Creative Commons or Open Source licenses).
- MAY, if necessary be a textual description of how the RO-Crate may be used.""" ;
- sh:minCount 1 ;
- sh:nodeKind sh:IRIOrLiteral ;
- sh:path schema_org:license ;
- sh:or (
- [ sh:dataType xsd:string ]
- [ sh:dataType xsd:anyURI ]
- ) ;
- ].
-
-ro-crate:LicenseDefinition a sh:NodeShape ;
- sh:name "License definition" ;
- sh:description """Contextual entity representing a license with a name and description.""";
- sh:targetClass schema_org:license ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "License name" ;
- sh:description "The license MAY have a name" ;
- sh:minCount 1 ;
- sh:maxCount 1 ;
- sh:nodeKind sh:Literal ;
- sh:path schema_org:name ;
- sh:message "Missing license name" ;
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "License description" ;
- sh:description """The license MAY have a description""" ;
- sh:maxCount 1;
- sh:minCount 1 ;
- sh:nodeKind sh:Literal ;
- sh:path schema_org:description ;
- sh:message "Missing license description" ;
- ] .
-
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/must/0_file_descriptor_format.py b/tests/data/rocrate_validator_profiles/ro-crate/must/0_file_descriptor_format.py
deleted file mode 100644
index fa02f64..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/must/0_file_descriptor_format.py
+++ /dev/null
@@ -1,401 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from typing import Any
-
-from rocrate_validator.utils import log as logging
-from rocrate_validator.models import ValidationContext
-from rocrate_validator.requirements.python import (PyFunctionCheck, check,
- requirement)
-from rocrate_validator.utils.http import HttpRequester
-
-# set up logging
-logger = logging.getLogger(__name__)
-
-
-@requirement(name="File Descriptor existence")
-class FileDescriptorExistence(PyFunctionCheck):
- """The file descriptor MUST be present in the RO-Crate and MUST not be empty."""
-
- @check(name="File Descriptor Existence")
- def test_existence(self, context: ValidationContext) -> bool:
- """
- Check if the file descriptor is present in the RO-Crate
- """
- if context.settings.metadata_only:
- logger.debug("Skipping file descriptor existence check in metadata-only mode")
- return True
- if not context.ro_crate.has_descriptor():
- message = f'file descriptor "{context.rel_fd_path}" is not present'
- context.result.add_issue(message, self)
- return False
- return True
-
- @check(name="File Descriptor size check")
- def test_size(self, context: ValidationContext) -> bool:
- """
- Check if the file descriptor is not empty
- """
- if context.settings.metadata_only:
- logger.debug("Skipping file descriptor existence check in metadata-only mode")
- return True
- if not context.ro_crate.has_descriptor():
- message = f'file descriptor {context.rel_fd_path} is empty'
- context.result.add_issue(message, self)
- return False
- if context.ro_crate.metadata.size == 0:
- context.result.add_issue(f'RO-Crate "{context.rel_fd_path}" file descriptor is empty', self)
- return False
- return True
-
-
-@requirement(name="File Descriptor JSON format")
-class FileDescriptorJsonFormat(PyFunctionCheck):
- """
- The file descriptor MUST be a valid JSON file
- """
- @check(name="File Descriptor JSON format")
- def check(self, context: ValidationContext) -> bool:
- """ Check if the file descriptor is in the correct format"""
- try:
- logger.debug("Checking validity of JSON file at %s", context.ro_crate.metadata)
- context.ro_crate.metadata.as_dict()
- return True
- except Exception as e:
- context.result.add_issue(
- f'RO-Crate file descriptor "{context.rel_fd_path}" is not in the correct format', self)
- if logger.isEnabledFor(logging.DEBUG):
- logger.exception(e)
- return False
-
-
-@requirement(name="File Descriptor JSON-LD format")
-class FileDescriptorJsonLdFormat(PyFunctionCheck):
- """
- The file descriptor MUST be a valid JSON-LD file
- """
-
- def __check_remote_context__(self, context_uri: str) -> bool:
- # Try to retrieve the context
- try:
- raw_data = HttpRequester().get(context_uri, headers={"Accept": "application/ld+json"})
- if raw_data.status_code != 200:
- raise RuntimeError(f"Unable to retrieve the JSON-LD context '{context_uri}'", self)
- logger.debug(f"Retrieved context from {context_uri}")
-
- # Try to parse the JSON-LD and access the context
- jsonLD = raw_data.json()["@context"]
- assert isinstance(jsonLD, dict)
- return True
- except Exception as e:
- if logger.isEnabledFor(logging.DEBUG):
- logger.exception(e)
- return False
-
- def __check_contexts__(self, context: ValidationContext, jsonld_context: object) -> bool:
- """ Get the keys of the context URI """
- is_valid = True
- # if the context is a string, check if it is a valid URI
- if isinstance(jsonld_context, str):
- if not self.__check_remote_context__(jsonld_context):
- context.result.add_issue(
- f'Unable to retrieve the JSON-LD context "{jsonld_context}"', self)
- is_valid = False
-
- # if the context is a dictionary, get the keys of the dictionary
- if isinstance(jsonld_context, dict):
- logger.debug(f"Detected dictionary context: {jsonld_context}")
-
- # if the context is a list of contexts, get the keys of each context
- if isinstance(jsonld_context, list):
- for ctx in jsonld_context:
- if not self.__check_contexts__(context, ctx):
- is_valid = False
- # return if the context is valid
- return is_valid
-
- @check(name="File Descriptor @context property validation")
- def check_context(self, context: ValidationContext) -> bool:
- """ Check if the file descriptor contains
- the @context property and it is a valid JSON-LD context
- """
- try:
- json_dict = context.ro_crate.metadata.as_dict()
- if "@context" not in json_dict:
- context.result.add_issue(
- f'RO-Crate file descriptor "{context.rel_fd_path}" '
- "does not contain a context", self)
- return False
-
- # Check if the context is valid
- return self.__check_contexts__(context, json_dict["@context"])
- except Exception as e:
- if logger.isEnabledFor(logging.DEBUG):
- logger.exception(e)
- return False
-
- @check(name="File Descriptor JSON-LD must be flattened")
- def check_flattened(self, context: ValidationContext) -> bool:
- """ Check if the file descriptor is flattened """
-
- def is_entity_flat_recursive(entity: Any, is_first: bool = True, fail_fast: bool = False) -> bool:
- """ Recursively check if the given data corresponds to a flattened JSON-LD object
- and returns False if it does not and is not a root element
- """
- result = True
- if isinstance(entity, dict):
- if is_first:
- for _, elem in entity.items():
- if not is_entity_flat_recursive(elem, is_first=False, fail_fast=fail_fast):
- result = False
- if fail_fast:
- return False
- # if this is not the root element, it must not contain more properties than @id
- else:
- if "@id" in entity and "@value" in entity:
- # add issue if both @id and @value are present
- context.result.add_issue(
- (
- f'entity "{entity.get("@id", entity)}" contains both @id and @value: '
- 'an object with an @value represents a value object, which is a literal value such as '
- 'a string, number, date, or language-tagged string. This object is not an identifiable '
- 'resource, but a simple literal value.'
- ),
- self
- )
- result = False
- if fail_fast:
- return False
-
- # Handle value objects
- if "@value" in entity:
- # Inline the checks from is_value_object and add issues for each violation
- if not isinstance(entity, dict):
- context.result.add_issue(
- f'entity "{entity.get("@id", entity)}" is not a valid value object: '
- 'it MUST be a dictionary.',
- self
- )
- result = False
- if fail_fast:
- return False
-
- has_language = "@language" in entity
- has_type = "@type" in entity
-
- if has_language and has_type:
- context.result.add_issue(
- f'entity "{entity.get("@id", entity)}" is not a valid value object: '
- '@language and @type cannot coexist.',
- self
- )
- result = False
- if fail_fast:
- return False
-
- if has_language and not isinstance(entity["@value"], str):
- context.result.add_issue(
- f'entity "{entity.get("@id", entity)}" is not a valid value object: '
- 'if @language is present, @value must be a string.',
- self
- )
- result = False
- if fail_fast:
- return False
- # Handle node objects:
- # every remaining entity with len(entity) > 1 must be a node object
- elif "@id" not in entity or len(entity) > 1:
- context.result.add_issue(
- f'entity "{entity.get("@id", entity)}" is not a valid node object reference: '
- 'it MUST have only @id, but no other properties.',
- self
- )
- result = False
- if fail_fast:
- return False
- if isinstance(entity, list):
- for element in entity:
- if not is_entity_flat_recursive(element, is_first=False, fail_fast=fail_fast):
- result = False
- if fail_fast:
- return False
- return result
-
- try:
- fail_fast = context.settings.abort_on_first
- json_dict = context.ro_crate.metadata.as_dict()
- result = True
- for entity in json_dict["@graph"]:
- if not is_entity_flat_recursive(entity, fail_fast=fail_fast):
- context.result.add_issue(
- f'RO-Crate file descriptor "{context.rel_fd_path}" '
- f'is not fully flattened at entity "{entity.get("@id", entity)}"', self)
- result = False
- if fail_fast:
- return False
- return result
- except Exception as e:
- if logger.isEnabledFor(logging.DEBUG):
- logger.exception(e)
- return False
-
- @check(name="Validation of the @id property of the file descriptor entities")
- def check_identifiers(self, context: ValidationContext) -> bool:
- """ Check if the file descriptor entities have the @id property """
- try:
- json_dict = context.ro_crate.metadata.as_dict()
- for entity in json_dict["@graph"]:
- if "@id" not in entity:
- context.result.add_issue(
- f"Entity \"{entity.get('name', None) or entity}\" "
- f"of RO-Crate \"{context.rel_fd_path}\" "
- "file descriptor does not contain the @id attribute", self)
- return False
- return True
- except Exception as e:
- if logger.isEnabledFor(logging.DEBUG):
- logger.exception(e)
- return False
-
- @check(name="Validation of the @type property of the file descriptor entities")
- def check_types(self, context: ValidationContext) -> bool:
- """ Check if the file descriptor entities have the @type property """
- try:
- json_dict = context.ro_crate.metadata.as_dict()
- for entity in json_dict["@graph"]:
- if "@type" not in entity:
- context.result.add_issue(
- f"Entity \"{entity.get('name', None) or entity}\" "
- f"of RO-Crate \"{context.rel_fd_path}\" "
- "file descriptor does not contain the @type attribute", self)
- return False
- return True
- except Exception as e:
- if logger.isEnabledFor(logging.DEBUG):
- logger.exception(e)
- return False
-
- def __get_context_keys__(self, context: object) -> set:
- """ Get the keys of the context URI """
- if isinstance(context, str):
- return self.__get_remote_context_keys__(context)
-
- # if the context is a dictionary, get the keys of the dictionary
- if isinstance(context, dict):
- return set(context.keys())
-
- # if the context is a list of contexts, get the keys of each context
- if isinstance(context, list):
- keys = set()
- for ctx in context:
- keys.update(self.__get_context_keys__(ctx))
- return keys
-
- def __get_remote_context_keys__(self, context_uri: str) -> set:
- """ Get the keys of the context URI """
-
- logger.debug(f"Retrieving context from {context_uri}...")
- # Try to retrieve the context
- raw_data = HttpRequester().get(context_uri, headers={"Accept": "application/ld+json"})
- if raw_data.status_code != 200:
- raise RuntimeError(f"Unable to retrieve the JSON-LD context '{context_uri}'")
-
- logger.debug(f"Retrieved context from {context_uri}")
-
- # Get the keys of the context
- jsonLD = raw_data.json()
- jsonLD_ctx = jsonLD["@context"]
- if not isinstance(jsonLD_ctx, dict):
- raise RuntimeError("The context is not a dictionary", self)
- return set(jsonLD_ctx.keys())
-
- def __check_entity_keys__(self, entity: dict,
- context_keys: set,
- unexpected_keys: dict[str, int] = None) -> dict[str, int]:
- """ Check if the entity is in the correct format """
-
- def add_unexpected_key(k: str, u_keys: dict) -> None:
- """ Add a key to the unexpected keys dictionary """
- u_keys[k] = u_keys.get(k, 0) + 1
-
- # Keys that should be skipped
- SKIP_KEYS = {"@id", "@type", "@context", "@value", "@language"}
-
- # Ensure unexpected_keys is initialized
- if unexpected_keys is None:
- unexpected_keys = {}
-
- # If the entity is a dictionary, check each key
- if isinstance(entity, dict):
- for k, v in entity.items():
- if k not in context_keys and k not in SKIP_KEYS:
- logger.debug(f"Key {k} not in context keys")
- add_unexpected_key(k, unexpected_keys)
- if isinstance(v, (dict, list)):
- self.__check_entity_keys__(v, context_keys, unexpected_keys)
-
- # If the entity is a list, check each element
- elif isinstance(entity, list):
- for elem in entity:
- self.__check_entity_keys__(elem, context_keys, unexpected_keys)
-
- return unexpected_keys
-
- @check(name="Validation of the compaction format of the file descriptor")
- def check_compaction(self, context: ValidationContext) -> bool:
- """ Check if the file descriptor is in the **compacted** JSON-LD format """
- try:
- logger.debug("Checking compaction format of JSON-LD file at %s", context.ro_crate.metadata)
- json_dict = context.ro_crate.metadata.as_dict()
- logger.debug(f"JSONLD keys:{json_dict.keys()}")
-
- jsonld_context = json_dict.get("@context", None)
- logger.debug(f"Context: {jsonld_context}")
-
- try:
- context_keys = self.__get_context_keys__(jsonld_context)
- logger.debug(f"{context_keys}")
- except Exception as e:
- if logger.isEnabledFor(logging.DEBUG):
- logger.exception(e)
- context.result.add_issue(str(e), self)
- return False
-
- unexpected_keys = self.__check_entity_keys__(json_dict.get("@graph"), context_keys)
- logger.debug(f"Unexpected keys: {unexpected_keys}")
- if len(unexpected_keys) > 0:
- for k, v in unexpected_keys.items():
- logger.debug(f"Key {k} appears {v} times")
- # Add the correct suffix to the message
- suffix = "s" if v > 1 else ""
- # Check if k is a term or a URI
- if k.startswith("http"):
- context.result.add_issue(
- f'The The {v} occurrence{suffix} of the "{k}" URI cannot be used as a key{suffix} "'
- 'because the compacted format requires simple terms as keys '
- '(see https://www.w3.org/TR/json-ld-api/#compaction for more details).', self)
- else:
- context.result.add_issue(
- f'The {v} occurrence{suffix} of the JSON-LD key "{k}" '
- f'{"is" if v == 1 else "are"} not allowed in the compacted format '
- 'because it is not present in the @context of the document', self)
- return False
-
- return True
- except Exception as e:
- if logger.isEnabledFor(logging.DEBUG):
- logger.exception(e)
- context.result.add_issue(
- f'Unexpected error: {e}', self)
- return False
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/must/1_file-descriptor_metadata.ttl b/tests/data/rocrate_validator_profiles/ro-crate/must/1_file-descriptor_metadata.ttl
deleted file mode 100644
index 32d2a63..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/must/1_file-descriptor_metadata.ttl
+++ /dev/null
@@ -1,100 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix dct: .
-@prefix rdf: .
-@prefix schema_org: .
-@prefix sh: .
-@prefix validator: .
-
-
-ro-crate:FindROCrateMetadataFileDescriptorEntity a sh:NodeShape, validator:HiddenShape;
- sh:name "Identify the RO-Crate Metadata File Descriptor" ;
- sh:description """The RO-Crate Metadata File Descriptor entity describes the RO-Crate itself, and it is named as `ro-crate-metadata.json`.
- It can be identified by name according to the RO-Crate specification
- available at [Finding RO-Crate Root in RDF triple stores](https://www.researchobject.org/ro-crate/1.1/appendix/relative-uris.html#finding-ro-crate-root-in-rdf-triple-stores).""" ;
- sh:target [
- a sh:SPARQLTarget ;
- sh:prefixes ro-crate:sparqlPrefixes ;
- sh:select """
- SELECT ?this
- WHERE {
- ?this a schema:CreativeWork ;
- FILTER(contains(str(?this), "ro-crate-metadata.json"))
- }
- """
- ] ;
-
- # Expand data graph with triples from the file data entity
- sh:rule [
- a sh:TripleRule ;
- sh:subject sh:this ;
- sh:predicate rdf:type ;
- sh:object ro-crate:ROCrateMetadataFileDescriptor ;
- ] .
-
-ro-crate:ROCrateMetadataFileDescriptorExistence
- a sh:NodeShape ;
- sh:name "RO-Crate Metadata File Descriptor entity existence" ;
- sh:description "The RO-Crate JSON-LD MUST contain a Metadata File Descriptor entity named `ro-crate-metadata.json` and typed as `schema:CreativeWork`" ;
- sh:targetNode ro:ro-crate-metadata.json ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "RO-Crate Metadata File Descriptor entity existence" ;
- sh:description """Check if the RO-Crate Metadata File Descriptor entity exists,
- i.e., if there exists an entity with @id `ro-crate-metadata.json` and type `schema:CreativeWork`""" ;
- sh:path rdf:type ;
- sh:hasValue ro-crate:ROCrateMetadataFileDescriptor ;
- sh:minCount 1 ;
- sh:message "The root of the document MUST have an entity with @id `ro-crate-metadata.json`" ;
- ] .
-
-ro-crate:ROCrateMetadataFileDescriptorRecommendedProperties a sh:NodeShape ;
- sh:name "RO-Crate Metadata File Descriptor REQUIRED properties" ;
- sh:description """RO-Crate Metadata Descriptor MUST be defined
- according with the requirements details defined in
- [RO-Crate Metadata File Descriptor](https://www.researchobject.org/ro-crate/1.1/root-data-entity.html#ro-crate-metadata-file-descriptor)""";
- sh:targetNode ro:ro-crate-metadata.json ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Metadata File Descriptor entity type" ;
- sh:description "Check if the RO-Crate Metadata File Descriptor has `@type` CreativeWork, as per schema.org" ;
- sh:minCount 1 ;
- sh:nodeKind sh:IRI ;
- sh:path rdf:type ;
- sh:hasValue schema_org:CreativeWork ;
- sh:message "The RO-Crate metadata file MUST be a CreativeWork, as per schema.org" ;
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Metadata File Descriptor entity: `about` property" ;
- sh:description """Check if the RO-Crate Metadata File Descriptor has an `about` property referencing the Root Data Entity""" ;
- sh:maxCount 1;
- sh:minCount 1 ;
- sh:nodeKind sh:IRI ;
- sh:path schema_org:about ;
- sh:class schema_org:Dataset ;
- sh:message "The RO-Crate metadata file descriptor MUST have an `about` property referencing the Root Data Entity" ;
- ] ;
- sh:property ro-crate:conformsToROCrateSpec .
-
-ro-crate:conformsToROCrateSpec sh:name "Metadata File Descriptor entity: `conformsTo` property" ;
- sh:description """Check if the RO-Crate Metadata File Descriptor has a `conformsTo` property which points to the RO-Crate specification version""" ;
- sh:minCount 1 ;
- sh:nodeKind sh:IRI ;
- sh:path dct:conformsTo ;
- sh:hasValue ;
- sh:message "The RO-Crate metadata file descriptor MUST have a `conformsTo` property with the RO-Crate specification version" .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/must/2_root_data_entity_metadata.ttl b/tests/data/rocrate_validator_profiles/ro-crate/must/2_root_data_entity_metadata.ttl
deleted file mode 100644
index 2c2efcf..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/must/2_root_data_entity_metadata.ttl
+++ /dev/null
@@ -1,175 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix rdf: .
-@prefix schema_org: .
-@prefix sh: .
-@prefix validator: .
-@prefix xsd: .
-
-
-ro-crate:RootDataEntityType
- a sh:NodeShape ;
- sh:name "RO-Crate Root Data Entity type" ;
- sh:description "The Root Data Entity MUST be a `Dataset` (as per `schema.org`)" ;
- sh:target [
- a sh:SPARQLTarget ;
- sh:prefixes ro-crate:sparqlPrefixes ;
- sh:select """
- SELECT ?this
- WHERE {
- ?metadatafile schema:about ?this .
- FILTER(contains(str(?metadatafile), "ro-crate-metadata.json"))
- }
- """
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Root Data Entity type" ;
- sh:description "Check if the Root Data Entity is a `Dataset` (as per `schema.org`)" ;
- sh:path rdf:type ;
- sh:hasValue schema_org:Dataset ;
- sh:minCount 1 ;
- sh:message """The Root Data Entity MUST be a `Dataset` (as per `schema.org`)""" ;
- ] ;
- # Validate that if the publisher is specified, it is an Organization or a Person
- sh:property [
- sh:path schema_org:publisher ;
- sh:severity sh:Violation ;
- sh:name "Root Data Entity: `publisher` property" ;
- sh:description """Check if the Root Data Entity has a `publisher` property of type `Organization` or `Person`.""" ;
- sh:or (
- [ sh:class schema_org:Organization ]
- [ sh:class schema_org:Person ]
- ) ;
- sh:message """The Root Data Entity MUST have a `publisher` property of type `Organization` or `Person`.""" ;
- ] .
-
-
-ro-crate:FindRootDataEntity a sh:NodeShape, validator:HiddenShape;
- sh:name "Identify the Root Data Entity of the RO-Crate" ;
- sh:description """The Root Data Entity is the top-level Data Entity in the RO-Crate and serves as the starting point for the description of the RO-Crate.
- It is a schema:Dataset and is indirectly identified by the about property of the resource ro-crate-metadata.json in the RO-Crate
- (see the definition at [Finding RO-Crate Root in RDF triple stores](https://www.researchobject.org/ro-crate/1.1/appendix/relative-uris.html#finding-ro-crate-root-in-rdf-triple-stores)).
- """ ;
- sh:target [
- a sh:SPARQLTarget ;
- sh:prefixes ro-crate:sparqlPrefixes ;
- sh:select """
- SELECT ?this
- WHERE {
- ?this a schema:Dataset .
- ?metadatafile schema:about ?this .
- FILTER(contains(str(?metadatafile), "ro-crate-metadata.json"))
- }
- """
- ] ;
-
- # Expand data graph with triples from the file data entity
- sh:rule [
- a sh:TripleRule ;
- sh:subject sh:this ;
- sh:predicate rdf:type ;
- sh:object ro-crate:RootDataEntity ;
- ] .
-
-
-ro-crate:RootDataEntityValueRestriction
- a sh:NodeShape ;
- sh:name "RO-Crate Root Data Entity value restriction" ;
- sh:description "The Root Data Entity MUST end with `/`" ;
- sh:targetNode ro-crate:RootDataEntity ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Root Data Entity URI value" ;
- sh:description "Check if the Root Data Entity URI ends with `/`" ;
- sh:path [ sh:inversePath rdf:type ] ;
- sh:minCount 1 ;
- sh:message """The Root Data Entity URI MUST end with `/`""" ;
- sh:pattern "/$" ;
- ] .
-
-ro-crate:RootDataEntityRequiredProperties
- a sh:NodeShape ;
- sh:name "RO-Crate Root Data Entity REQUIRED properties" ;
- sh:description "The Root Data Entity MUST have a `name`, `description`, `license` and `datePublished`" ;
- sh:targetClass ro-crate:RootDataEntity ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Root Data Entity: `name` property" ;
- sh:description """Check if the Root Data Entity includes a `name` (as specified by schema.org)
- to clearly identify the dataset and distinguish it from other datasets.""" ;
- sh:minCount 1 ;
- sh:nodeKind sh:Literal ;
- sh:path schema_org:name;
- sh:message "The Root Data Entity MUST have a `name` property (as specified by schema.org)" ;
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Root Data Entity: `description` property" ;
- sh:description """Check if the Root Data Entity includes a `description` (as specified by schema.org)
- to provide a human-readable description of the dataset.""" ;
- sh:minCount 1 ;
- sh:nodeKind sh:Literal ;
- sh:path schema_org:description;
- sh:message "The Root Data Entity MUST have a `description` property (as specified by schema.org)" ;
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Root Data Entity: `license` property" ;
- sh:description """Check if the Root Data Entity includes a `license` property (as specified by schema.org)
- to provide information about the license of the dataset.""" ;
- sh:path schema_org:license;
- sh:minCount 1 ;
- sh:or (
- [ sh:class ro-crate:ContextualEntity ]
- [ sh:dataType xsd:anyURI ]
- [ sh:datatype xsd:string ]
- ) ;
- sh:message """The Root Data Entity MUST have a `license` property (as specified by schema.org).
- SHOULD link to a Contextual Entity in the RO-Crate Metadata File with a name and description.
- MAY have a URI (eg for Creative Commons or Open Source licenses).
- MAY, if necessary be a textual description of how the RO-Crate may be used.""" ;
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Root Data Entity: `datePublished` property" ;
- sh:description """Check if the Root Data Entity includes a `datePublished` (as specified by schema.org)
- to provide the date when the dataset was published. The datePublished MUST be a valid ISO 8601 date.""" ;
- sh:minCount 1 ;
- sh:nodeKind sh:Literal ;
- sh:path schema_org:datePublished ;
- sh:pattern "^([\\+-]?\\d{4})((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))?|W([0-4]\\d|5[0-2])(-?[1-7])?|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)?[0-5]\\d)?|24:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)?$" ;
- sh:message "The Root Data Entity MUST have a `datePublished` property (as specified by schema.org) with a valid ISO 8601 date" ;
- ] .
-
-ro-crate:RootDataEntityHasPartValueRestriction
- a sh:NodeShape ;
- sh:name "RO-Crate Root Data Entity: `hasPart` value restriction" ;
- sh:description "The Root Data Entity MUST be linked to the declared `File`, `Directory` and other types of instances through the `hasPart` property" ;
- sh:targetClass ro-crate:RootDataEntity ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "RO-Crate Root Data Entity: `hasPart` value restriction" ;
- sh:description "Check if the Root Data Entity is linked to the declared `File`, `Directory` and other types of instances through the `hasPart` property" ;
- sh:path schema_org:hasPart ;
- sh:or (
- [ sh:class ro-crate:File ]
- [ sh:class ro-crate:Directory ]
- [ sh:class ro-crate:GenericDataEntity ]
- ) ;
- sh:message """The Root Data Entity MUST be linked to either File or Directory instances, nothing else""" ;
- ] .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/must/4_data_entity_metadata.py b/tests/data/rocrate_validator_profiles/ro-crate/must/4_data_entity_metadata.py
deleted file mode 100644
index 1abcbcf..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/must/4_data_entity_metadata.py
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from rocrate_validator.utils import log as logging
-from rocrate_validator.models import ValidationContext
-from rocrate_validator.requirements.python import (PyFunctionCheck, check,
- requirement)
-
-# set up logging
-logger = logging.getLogger(__name__)
-
-
-@requirement(name="Data Entity: REQUIRED resource availability")
-class DataEntityRequiredChecker(PyFunctionCheck):
- """
- Resources corresponding to local Data Entities MUST be present in the RO-Crate payload
- """
-
- @check(name="Data Entity: REQUIRED resource availability")
- def check_availability(self, context: ValidationContext) -> bool:
- """
- Check the presence of the Data Entity in the RO-Crate
- """
- # Skip the check in metadata-only mode
- if context.settings.metadata_only:
- logger.debug("Skipping file descriptor existence check in metadata-only mode")
- return True
- # Perform the check
- result = True
- for entity in context.ro_crate.metadata.get_data_entities(exclude_web_data_entities=True):
- assert entity.id is not None, "Entity has no @id"
- logger.debug("Ensure the presence of the Data Entity '%s' within the RO-Crate", entity.id)
- try:
- logger.debug("Ensure the presence of the Data Entity '%s' within the RO-Crate", entity.id)
- if entity.has_local_identifier():
- logger.debug(
- "Ignoring the Data Entity '%s' as it is a local entity with a local identifier. "
- "According to the RO-Crate specification, local entities with local identifiers "
- "are not required to be included in the RO-Crate payload"
- "(see https://github.com/ResearchObject/ro-crate/issues/400#issuecomment-2779152885 and "
- "https://github.com/ResearchObject/ro-crate/pull/426 for more details)",
- entity.id)
- continue
- if not entity.has_relative_path():
- logger.debug(
- "Ignoring the Data Entity '%s' as it is a local entity with an absolute path. "
- "According to the RO-Crate specification, local entities with absolute paths "
- "are not required to be included in the RO-Crate payload. "
- "It is only recommended that they exist at the time of RO-Crate creation.",
- entity.id)
- continue
- if not entity.is_available():
- context.result.add_issue(
- f"The RO-Crate does not include the Data Entity '{entity.id}' as part of its payload", self)
- result = False
- except Exception as e:
- context.result.add_issue(
- f"Unable to check the the presence of the Data Entity '{entity.id}' within the RO-Crate", self)
- if logger.isEnabledFor(logging.DEBUG):
- logger.debug(e, exc_info=True)
- result = False
- if not result and context.fail_fast:
- return result
- return result
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/must/4_data_entity_metadata.ttl b/tests/data/rocrate_validator_profiles/ro-crate/must/4_data_entity_metadata.ttl
deleted file mode 100644
index a172e1f..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/must/4_data_entity_metadata.ttl
+++ /dev/null
@@ -1,216 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix rdf: .
-@prefix schema_org: .
-@prefix sh: .
-@prefix owl: .
-@prefix validator: .
-
-ro-crate:DataEntityRequiredProperties a sh:NodeShape ;
- sh:name "Data Entity: REQUIRED properties" ;
- sh:description """A Data Entity MUST be a `URI Path` relative to the ROCrate root,
- or an absolute URI""" ;
- sh:targetClass ro-crate:DataEntity ;
-
- sh:property [
- sh:name "Data Entity: @id value restriction" ;
- sh:description """Check if the Data Entity has an absolute or relative URI as `@id`""" ;
- sh:path [sh:inversePath rdf:type ] ;
- sh:nodeKind sh:IRI ;
- sh:severity sh:Violation ;
- sh:message """Data Entities MUST have an absolute or relative URI as @id.""" ;
- ] .
-
-ro-crate:FileDataEntity a sh:NodeShape ;
- sh:name "File Data Entity: REQUIRED properties" ;
- sh:description """A File Data Entity MUST be a `File`.
- `File` is an RO-Crate alias for the schema.org `MediaObject`.
- The term `File` here is liberal, and includes "downloadable" resources where `@id` is an absolute URI.
- """ ;
- sh:target [
- a sh:SPARQLTarget ;
- sh:prefixes ro-crate:sparqlPrefixes ;
- sh:select """
- SELECT ?this
- WHERE {
- ?this a schema:MediaObject .
- ?metadatafile schema:about ?root .
- FILTER(contains(str(?metadatafile), "ro-crate-metadata.json"))
- FILTER(!STRSTARTS(STR(?this), CONCAT(STR(?root), "#")))
- }
- """
- ] ;
-
- sh:property [
- sh:name "File Data Entity: REQUIRED type" ;
- sh:description """Check if the File Data Entity has `File` as `@type`.
- `File` is an RO-Crate alias for the schema.org `MediaObject`.
- """ ;
- sh:path rdf:type ;
- sh:hasValue ro-crate:File ;
- sh:severity sh:Violation ;
- sh:message """File Data Entities MUST have "File" as a value for @type.""" ;
- ] ;
-
- # Expand data graph with triples from the file data entity
- sh:rule [
- a sh:TripleRule ;
- sh:subject sh:this ;
- sh:predicate rdf:type ;
- sh:object ro-crate:DataEntity ;
- ] .
-
-
-ro-crate:DirectoryDataEntity a sh:NodeShape ;
- sh:name "Directory Data Entity: REQUIRED properties" ;
- sh:description """A Directory Data Entity MUST be of @type `Dataset`.
- The term `directory` here includes HTTP file listings where `@id` is an absolute URI.
- """ ;
- sh:target [
- a sh:SPARQLTarget ;
- sh:prefixes ro-crate:sparqlPrefixes ;
- sh:select """
- SELECT ?this
- WHERE {
- ?this a schema:Dataset .
- ?metadatafile schema:about ?root .
- # Exclude all dataset entities that ends with `./#`
- FILTER(contains(str(?metadatafile), "ro-crate-metadata.json"))
- FILTER(?this != ?root)
- FILTER(!STRSTARTS(STR(?this), CONCAT(STR(?root), "#")))
- }
- """
- ] ;
-
- # Decomment for debugging
- # sh:property [
- # sh:name "Test Directory" ;
- # sh:description """Data Entities representing directories MUST have "Directory" as a value for @type.""" ;
- # sh:path rdf:type ;
- # sh:hasValue ro-crate:File ;
- # sh:severity sh:Violation ;
- # ] ;
-
- # Expand data graph with triples from the file data entity
- sh:rule [
- a sh:TripleRule ;
- sh:subject sh:this ;
- sh:predicate rdf:type ;
- sh:object ro-crate:Directory ;
- ] ;
-
- # Expand data graph with triples from the directory data entity
- sh:rule [
- a sh:TripleRule ;
- sh:subject sh:this ;
- sh:predicate rdf:type ;
- sh:object ro-crate:DataEntity ;
- ] ;
-
- # Ensure that the directory data entity is a dataset
- sh:property [
- sh:name "Directory Data Entity: REQUIRED type" ;
- sh:description """Check if the Directory Data Entity has `Dataset` as `@type`.""" ;
- sh:path rdf:type ;
- sh:hasValue schema_org:Dataset ;
- sh:severity sh:Violation ;
- ] .
-
-ro-crate:DataEntityRequiredPropertiesShape a sh:NodeShape ;
- sh:name "Data Entity: REQUIRED properties" ;
- sh:description """A `DataEntity` MUST be linked, either directly or indirectly, from the Root Data Entity""" ;
- sh:targetClass ro-crate:DataEntity ;
- sh:property
- [
- a sh:PropertyShape ;
- sh:path [ sh:inversePath schema_org:hasPart ] ;
- sh:node schema_org:Dataset ;
- sh:minCount 1 ;
- sh:name "Data Entity MUST be directly referenced" ;
- sh:description """Check if the Data Entity is linked, either directly or indirectly, to the `Root Data Entity` using the `hasPart` (as defined in `schema.org`) property" """ ;
- # sh:message "A Data Entity MUST be directly or indirectly linked to the `Root Data Entity` through the `hasPart` property" ;
- ] .
-
-ro-crate:GenericDataEntityRequiredProperties a sh:NodeShape ;
- sh:name "Generic Data Entity: REQUIRED properties" ;
- sh:description """A Data Entity other than a File or a Directory MUST be a `DataEntity`""" ;
- sh:target [
- a sh:SPARQLTarget ;
- sh:prefixes ro-crate:sparqlPrefixes ;
- sh:select """
- SELECT ?this
- WHERE {
- ?root schema:hasPart ?this .
- ?metadatafile schema:about ?root .
- FILTER(contains(str(?metadatafile), "ro-crate-metadata.json"))
- FILTER(?this != ?root)
- FILTER(?this != ?metadatafile)
- FILTER NOT EXISTS {
- ?this a schema:MediaObject .
- ?this a schema:Dataset .
- }
- }
- """
- ] ;
-
- # Expand data graph with triples to mark the matching entities as GenericDataEntity instances
- sh:rule [
- a sh:TripleRule ;
- sh:subject sh:this ;
- sh:predicate rdf:type ;
- sh:object ro-crate:GenericDataEntity ;
- ] ;
-
- # Expand data graph with triples to mark the matching entities as DataEntity instances
- sh:rule [
- a sh:TripleRule ;
- sh:subject sh:this ;
- sh:predicate rdf:type ;
- sh:object ro-crate:DataEntity ;
- ] .
-
-
-# Uncomment for debugging
-# ro-crate:TestGenericDataEntity a sh:NodeShape ;
-# sh:disabled true ;
-# sh:targetClass ro-crate:GenericDataEntity ;
-# sh:name "Generic Data Entity: test invalid property";
-# sh:description """Check if the GenericDataEntity has the invalidProperty property""" ;
-# sh:property [
-# sh:minCount 1 ;
-# sh:maxCount 1 ;
-# sh:path ro-crate:invalidProperty ;
-# sh:severity sh:Violation ;
-# sh:message "Testing the generic data entity";
-# sh:datatype xsd:string ;
-# sh:message "Testing for the invalidProperty of the generic data entity";
-# ] .
-
-
-# Uncomment for debugging
-# ro:testDirectory a sh:NodeShape ;
-# sh:name "Definition of Test Directory" ;
-# sh:description """A Test Directory is a digital object that is stored in a file format""" ;
-# sh:targetClass ro-crate:Directory ;
-
-# sh:property [
-# sh:name "Test Directory instance" ;
-# sh:description """Check if the Directory DataEntity instance has the fake property ro-crate:foo""" ;
-# sh:path rdf:type ;
-# sh:hasValue ro-crate:foo ;
-# sh:severity sh:Violation ;
-# ] .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/must/5_web_data_entity_metadata.ttl b/tests/data/rocrate_validator_profiles/ro-crate/must/5_web_data_entity_metadata.ttl
deleted file mode 100644
index c547d60..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/must/5_web_data_entity_metadata.ttl
+++ /dev/null
@@ -1,50 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix rdf: .
-@prefix dct: .
-@prefix schema_org: .
-@prefix sh: .
-@prefix owl: .
-@prefix xsd: .
-@prefix validator: .
-
-
-ro-crate:WebBasedDataEntity a sh:NodeShape, validator:HiddenShape ;
- sh:name "Web-based Data Entity: REQUIRED properties" ;
- sh:description """A Web-based Data Entity is a `File` identified by an absolute URL""" ;
-
- sh:target [
- a sh:SPARQLTarget ;
- sh:prefixes ro-crate:sparqlPrefixes ;
- sh:select """
- SELECT ?this
- WHERE {
- ?this a schema:MediaObject .
- FILTER(?this != ro:ro-crate-metadata.json)
- FILTER regex(str(?this), "^(https?|ftps?)://", "i")
- }
- """
- ] ;
-
- # Expand data graph with triples which identify the web-based data entity
- sh:rule [
- a sh:TripleRule ;
- sh:subject sh:this ;
- sh:predicate rdf:type ;
- sh:object ro-crate:WebDataEntity ;
- ] .
-
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/must/6_contextual_entity.ttl b/tests/data/rocrate_validator_profiles/ro-crate/must/6_contextual_entity.ttl
deleted file mode 100644
index 9ecf87a..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/must/6_contextual_entity.ttl
+++ /dev/null
@@ -1,81 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix rdf: .
-@prefix schema: .
-@prefix sh: .
-@prefix xsd: .
-@prefix owl: .
-@prefix validator: .
-
-
-ro-crate:FindLicenseEntity a sh:NodeShape, validator:HiddenShape ;
- sh:name "Identify License Entity" ;
- sh:description """Mark a license entity any Data Entity referenced by the `schema:license` property.""" ;
- sh:target [
- a sh:SPARQLTarget ;
- sh:prefixes ro-crate:sparqlPrefixes ;
- sh:select """
- SELECT ?this
- WHERE {
- ?subject schema:license ?this .
- }
- """
- ] ;
-
- # Expand data graph with triples from the file data entity
- sh:rule [
- a sh:TripleRule ;
- sh:subject sh:this ;
- sh:predicate rdf:type ;
- sh:object ro-crate:ContextualEntity ;
- ] .
-
-
-ro-crate:WebSiteRecommendedProperties a sh:NodeShape ;
- sh:name "WebSite RECOMMENDED Properties" ;
- sh:description """A `WebSite` MUST be identified by a valid IRI and MUST have a `name` property.""" ;
- sh:targetClass schema:WebSite ;
- sh:property [
- sh:path [sh:inversePath rdf:type] ;
- sh:datType sh:IRI ;
- sh:name "WebSite: value restriction of its identifier" ;
- sh:description "Check if the WebSite has a valid IRI" ;
- sh:message "A WebSite MUST have a valid IRI" ;
- ] ;
- sh:property [
- sh:path schema:name ;
- sh:minCount 1 ;
- sh:dataType xsd:string ;
- sh:name "WebSite: REQUIRED `name` property" ;
- sh:description "Check if the WebSite has a `name` property" ;
- sh:message "A WebSite MUST have a `name` property" ;
- ] .
-
-
-ro-crate:CreativeWorkAuthorDefinition a sh:NodeShape, validator:HiddenShape ;
- sh:name "CreativeWork Author Definition" ;
- sh:description """Define the `CreativeWorkAuthor` as the `Person` object of the `schema:author` predicate.""" ;
- sh:targetObjectsOf schema:author ;
- sh:rule [
- a sh:TripleRule ;
- sh:subject sh:this ;
- sh:predicate rdf:type ;
- sh:object ro-crate:CreativeWorkAuthor ;
- sh:condition [
- sh:property [ sh:path rdf:type ; sh:hasValue schema:Person ; sh:minCount 1 ] ;
- ] ;
- ] .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/ontology.ttl b/tests/data/rocrate_validator_profiles/ro-crate/ontology.ttl
deleted file mode 100644
index 9af3a09..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/ontology.ttl
+++ /dev/null
@@ -1,67 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix owl: .
-@prefix rdf: .
-@prefix xml: .
-@prefix xsd: .
-@prefix rdfs: .
-@prefix schema: .
-@prefix rocrate: .
-@prefix bioschemas: .
-@prefix ro-crate: .
-# @base <./.> .
-
- rdf:type owl:Ontology ;
- owl:versionIRI .
-
-# # #################################################################
-# # # Classes
-# # #################################################################
-
-# Declare the RootDataEntity class
-ro-crate:RootDataEntity rdf:type owl:Class ;
- rdfs:subClassOf schema:Dataset ;
- rdfs:label "RootDataEntity"@en .
-
-### http://schema.org/CreativeWork
-schema:CreativeWork rdf:type owl:Class ;
- rdfs:label "CreativeWork"@en .
-
-### http://schema.org/MediaObject
-schema:MediaObject rdf:type owl:Class ;
- owl:equivalentClass ro-crate:File ;
- rdfs:label "MediaObject"@en .
-
-
-### http://schema.org/SoftwareSourceCode
-schema:SoftwareSourceCode rdf:type owl:Class ;
- rdfs:subClassOf schema:CreativeWork .
-
-
-### https://bioschemas.org/ComputationalWorkflow
-bioschemas:ComputationalWorkflow rdf:type owl:Class .
-
-
-### https://w3id.org/ro/crate/1.1/DataEntity
-ro-crate:DataEntity rdf:type owl:Class ;
- rdfs:subClassOf schema:CreativeWork ;
- rdfs:label "DataEntity"@en .
-
-
-# # ### https://w3id.org/ro/crate/1.1/Directory
-ro-crate:Directory rdf:type owl:Class ;
- rdfs:subClassOf schema:Dataset ;
- rdfs:label "Directory"@en .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/prefixes.ttl b/tests/data/rocrate_validator_profiles/ro-crate/prefixes.ttl
deleted file mode 100644
index 79006d5..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/prefixes.ttl
+++ /dev/null
@@ -1,49 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix sh: .
-@prefix xsd: .
-@prefix ro-crate: .
-
-# Define the prefixes used in the SPARQL queries
-ro-crate:sparqlPrefixes
- sh:declare [
- sh:prefix "schema" ;
- sh:namespace "http://schema.org/"^^xsd:anyURI ;
- ] ;
- sh:declare [
- sh:prefix "bioschemas" ;
- sh:namespace "https://bioschemas.org/"^^xsd:anyURI ;
- ] ;
- sh:declare [
- sh:prefix "bioschemas-cw" ;
- sh:namespace "https://bioschemas.org/ComputationalWorkflow#"^^xsd:anyURI ;
- ] ;
- sh:declare [
- sh:prefix "rocrate" ;
- sh:namespace "https://w3id.org/ro/crate/1.1/"^^xsd:anyURI ;
- ] ;
- sh:declare [
- sh:prefix "wfrun" ;
- sh:namespace "https://w3id.org/ro/terms/workflow-run#"^^xsd:anyURI ;
- ] ;
- sh:declare [
- sh:prefix "codemeta" ;
- sh:namespace "https://codemeta.github.io/terms/"^^xsd:anyURI ;
- ] ;
- sh:declare [
- sh:prefix "ro" ;
- sh:namespace "./"^^xsd:anyURI ;
- ] .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/profile.ttl b/tests/data/rocrate_validator_profiles/ro-crate/profile.ttl
deleted file mode 100644
index 47872a9..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/profile.ttl
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix dct: .
-@prefix prof: .
-@prefix role: .
-@prefix rdfs: .
-
-
-
- # a Profile; it's identifying URI
- a prof:Profile ;
-
- # common metadata for the Profile
-
- # the Profile's label
- rdfs:label "RO-Crate Metadata Specification 1.1" ;
-
- # regular metadata, a basic description of the Profile
- rdfs:comment """RO-Crate Metadata Specification."""@en ;
-
- # regular metadata, URI of publisher
- dct:publisher ;
-
- # this profile has a JSON-LD context resource
- prof:hasResource [
- a prof:ResourceDescriptor ;
-
- # it's in JSON-LD format
- dct:format ;
-
- # it conforms to JSON-LD, here referred to by its namespace URI as a Profile
- dct:conformsTo ;
-
- # this profile resource plays the role of "Vocabulary"
- # described in this ontology's accompanying Roles vocabulary
- prof:hasRole role:Vocabulary ;
-
- # this profile resource's actual file
- prof:hasArtifact ;
- ] ;
-
- # this profile has a human-readable documentation resource
- prof:hasResource [
- a prof:ResourceDescriptor ;
-
- # it's in HTML format
- dct:format ;
-
- # it conforms to HTML, here referred to by its namespace URI as a Profile
- dct:conformsTo ;
-
- # this profile resource plays the role of "Specification"
- # described in this ontology's accompanying Roles vocabulary
- prof:hasRole role:Specification ;
-
- # this profile resource's actual file
- prof:hasArtifact ;
- ] ;
-
- # a short code to refer to the Profile with when a URI can't be used
- prof:hasToken "ro-crate" ;
-.
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/should/2_root_data_entity_metadata.ttl b/tests/data/rocrate_validator_profiles/ro-crate/should/2_root_data_entity_metadata.ttl
deleted file mode 100644
index 7cb53f0..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/should/2_root_data_entity_metadata.ttl
+++ /dev/null
@@ -1,74 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix rdf: .
-@prefix schema_org: .
-@prefix sh: .
-@prefix validator: .
-
-ro-crate:RootDataEntityDirectRecommendedProperties a sh:NodeShape ;
- sh:name "RO-Crate Root Data Entity RECOMMENDED properties" ;
- sh:description """The Root Data Entity SHOULD have
- the properties `name`, `description` and `license` defined as described
- in the RO-Crate specification """;
- sh:targetClass ro-crate:RootDataEntity ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Root Data Entity: `license` SHOULD link to a Contextual Entity" ;
- sh:description """Check if the Root Data Entity includes a `license` property
- that links to a Contextual Entity with type `schema_org:CreativeWork` to describe the license.""" ;
- sh:nodeKind sh:BlankNodeOrIRI ;
- sh:class schema_org:CreativeWork ;
- sh:path schema_org:license;
- sh:minCount 1 ;
- sh:message """The Root Data Entity SHOULD have a link to a Contextual Entity representing the schema_org:license type""" ;
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Root Data Entity: `author` property" ;
- sh:description """Check if the Root Data Entity includes a `author` property (as specified by schema.org)
- to provide information about its author.""" ;
- sh:or (
- [ sh:class schema_org:Person ;]
- [ sh:class schema_org:Organization ;]
- ) ;
- sh:path schema_org:author;
- sh:minCount 1 ;
- sh:message """The Root Data Entity SHOULD have a link to a Contextual Entity representing the `author` of the RO-Crate""" ;
- ] ;
- sh:property [
- sh:minCount 1 ;
- sh:maxCount 1 ;
- sh:path schema_org:publisher ;
- sh:severity sh:Warning ;
- sh:name "Root Data Entity: `publisher` property" ;
- sh:description """Check if the Root Data Entity has a `publisher` property of type `Organization`.""" ;
- sh:message "The `publisher` property of a `Root Data Entity` SHOULD be an `Organization`";
- sh:nodeKind sh:IRI ;
- sh:class schema_org:Organization ;
- ] ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Root Data Entity: RECOMMENDED `datePublished` property" ;
- sh:description """Check if the Root Data Entity includes a `datePublished` (as specified by schema.org)
- to provide the date when the dataset was published. The datePublished MUST be a valid ISO 8601 date.
- It SHOULD be specified to at least the day level, but MAY include a time component.""" ;
- sh:minCount 1 ;
- sh:nodeKind sh:Literal ;
- sh:path schema_org:datePublished ;
- sh:pattern "^([\\+-]?\\d{4})((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))|W([0-4]\\d|5[0-2])(-?[1-7])|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)?[0-5]\\d)?|24:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)$" ;
- sh:message "The Root Data Entity MUST have a `datePublished` property (as specified by schema.org) with a valid ISO 8601 date and the precision of at least the day level" ;
- ] .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/should/2_root_data_entity_relative_uri.py b/tests/data/rocrate_validator_profiles/ro-crate/should/2_root_data_entity_relative_uri.py
deleted file mode 100644
index dd7d8fc..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/should/2_root_data_entity_relative_uri.py
+++ /dev/null
@@ -1,42 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from rocrate_validator.utils import log as logging
-from rocrate_validator.models import ValidationContext
-from rocrate_validator.requirements.python import (PyFunctionCheck, check,
- requirement)
-
-# set up logging
-logger = logging.getLogger(__name__)
-
-
-@requirement(name="RO-Crate Root Data Entity RECOMMENDED value")
-class RootDataEntityRelativeURI(PyFunctionCheck):
- """
- The Root Data Entity SHOULD be denoted by the string /
- """
-
- @check(name="Root Data Entity: RECOMMENDED value")
- def check_relative_uris(self, context: ValidationContext) -> bool:
- """Check if the Root Data Entity is denoted by the string `./` in the file descriptor JSON-LD"""
- try:
- if not context.ro_crate.metadata.get_root_data_entity().id == './':
- context.result.add_issue(
- 'Root Data Entity URI is not denoted by the string `./`', self)
- return False
- return True
- except Exception as e:
- context.result.add_issue(
- f'Error checking Root Data Entity URI: {str(e)}', self)
- return False
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/should/4_data_entity_existence.py b/tests/data/rocrate_validator_profiles/ro-crate/should/4_data_entity_existence.py
deleted file mode 100644
index f5742ac..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/should/4_data_entity_existence.py
+++ /dev/null
@@ -1,58 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from rocrate_validator.utils import log as logging
-from rocrate_validator.models import ValidationContext
-from rocrate_validator.requirements.python import (PyFunctionCheck, check,
- requirement)
-
-# set up logging
-logger = logging.getLogger(__name__)
-
-
-@requirement(name="Data Entity: RECOMMENDED resource availability")
-class DataEntityRecommendedChecker(PyFunctionCheck):
- """
- Data Entities with absolute URI paths SHOULD be available
- at the time of RO-Crate creation
- """
-
- @check(name="Data Entity: RECOMMENDED resource availability")
- def check_availability(self, context: ValidationContext) -> bool:
- """
- Check the availability of the Data Entity with absolute URI paths
- are available at the time of RO-Crate creation
- """
- # Skip the check in metadata-only mode
- if context.settings.metadata_only:
- logger.debug("Skipping file descriptor existence check in metadata-only mode")
- return True
- # Perform the check
- result = True
- for entity in [
- _ for _ in context.ro_crate.metadata.get_data_entities(exclude_web_data_entities=True)
- if _.has_absolute_path()]:
- assert entity.id is not None, "Entity has no @id"
- try:
- if not entity.is_available():
- context.result.add_issue(
- f'Data Entity {entity.id} is not available', self)
- result = False
- except Exception as e:
- context.result.add_issue(
- f'Web-based Data Entity {entity.id} is not available: {e}', self)
- result = False
- if not result and context.fail_fast:
- return result
- return result
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/should/4_data_entity_metadata.ttl b/tests/data/rocrate_validator_profiles/ro-crate/should/4_data_entity_metadata.ttl
deleted file mode 100644
index 188e3a8..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/should/4_data_entity_metadata.ttl
+++ /dev/null
@@ -1,69 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix rdf: .
-@prefix schema_org: .
-@prefix sh: .
-@prefix xsd: .
-
-ro-crate:FileRecommendedProperties a sh:NodeShape ;
- sh:targetClass ro-crate:File ;
- sh:name "File Data Entity: RECOMMENDED properties";
- sh:description """A `File` Data Entity SHOULD have detailed descriptions encodings through the `encodingFormat` property""" ;
- sh:property [
- sh:minCount 1 ;
- sh:maxCount 2 ;
- sh:path schema_org:encodingFormat ;
- sh:severity sh:Warning ;
- sh:name "File Data Entity: RECOMMENDED `encodingFormat` property" ;
- sh:description """Check if the File Data Entity has a detailed description of encodings through the `encodingFormat` property.
- The `encodingFormat` property SHOULD be a PRONOM identifier (e.g., application/pdf) or,
- to add more detail, SHOULD be linked using a `PRONOM` to a `Contextual Entity` of type `WebSite`
- (see [Adding detailed descriptions of encodings](https://www.researchobject.org/ro-crate/1.1/data-entities.html#adding-detailed-descriptions-of-encodings)).
- """ ;
- sh:message "Missing or invalid `encodingFormat` linked to the `File Data Entity`";
- sh:or (
- [
- sh:datatype xsd:string ;
- sh:pattern "^(\\w*)\\/(\\w[\\w\\.-]*)(?:\\+(\\w[\\w\\.-]*))?(?:;(\\w+=[^;]+))*$" ;
- sh:name "File Data Entity: RECOMMENDED `PRONOM` for the `encodingFormat` property" ;
- sh:description """Check if the File Data Entity is linked to its `encodingFormat` through a PRONOM identifier
- (e.g., application/pdf, application/text, image/svg+xml, image/svg;q=0.9,/;q=0.8,image/svg+xml;q=0.9,/;q=0.8, application/vnd.uplanet.listcmd-wbxml;charset=utf-8).
- """ ;
- sh:message "The `encodingFormat` SHOULD be linked using a PRONOM identifier (e.g., application/pdf).";
- ]
- [
- sh:nodeKind sh:IRI ;
- sh:class schema_org:WebSite ;
- sh:name "File Data Entity: RECOMMENDED `Contextual Entity` linked to the `encodingFormat` property";
- sh:description "Check if the File Data Entity `encodingFormat` is linked to a `Contextual Entity of type `WebSite`." ;
- sh:message "The `encodingFormat` SHOULD be linked to a `Contextual Entity` of type `Web Site`." ;
- ]
- )
- ] .
-
-ro-crate:DirectoryDataEntityRequiredValueRestriction a sh:NodeShape ;
- sh:name "Directory Data Entity: RECOMMENDED value restriction" ;
- sh:description """A Directory Data Entity SHOULD end with `/`""" ;
- sh:targetNode ro-crate:Directory ;
- sh:property [
- a sh:PropertyShape ;
- sh:name "Directory Data Entity: RECOMMENDED value restriction" ;
- sh:description """Check if the Directory Data Entity ends with `/`""" ;
- sh:path [ sh:inversePath rdf:type ] ;
- sh:message """Every Data Entity Directory URI SHOULD end with `/`""" ;
- sh:pattern "/$" ;
- ] .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/should/5_web_data_entity_metadata.py b/tests/data/rocrate_validator_profiles/ro-crate/should/5_web_data_entity_metadata.py
deleted file mode 100644
index 13ef914..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/should/5_web_data_entity_metadata.py
+++ /dev/null
@@ -1,73 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-from rocrate_validator.utils import log as logging
-from rocrate_validator.models import ValidationContext
-from rocrate_validator.requirements.python import (PyFunctionCheck, check,
- requirement)
-
-# set up logging
-logger = logging.getLogger(__name__)
-
-
-@requirement(name="Web-based Data Entity: RECOMMENDED resource availability")
-class WebDataEntityRecommendedChecker(PyFunctionCheck):
- """
- Web-based Data Entity instances SHOULD be available
- at the URIs specified in the `@id` property of the Web-based Data Entity.
- """
-
- @check(name="Web-based Data Entity: resource availability")
- def check_availability(self, context: ValidationContext) -> bool:
- """
- Check if the Web-based Data Entity is directly downloadable
- by a simple retrieval (e.g. HTTP GET) permitting redirection and HTTP/HTTPS URIs
- """
- result = True
- for entity in context.ro_crate.metadata.get_web_data_entities():
- assert entity.id is not None, "Entity has no @id"
- try:
- if not entity.is_available():
- context.result.add_issue(
- f'Web-based Data Entity {entity.id} is not available', self)
- result = False
- except Exception as e:
- context.result.add_issue(
- f'Web-based Data Entity {entity.id} is not available: {e}', self)
- result = False
- if not result and context.fail_fast:
- return result
- return result
-
- @check(name="Web-based Data Entity: `contentSize` property")
- def check_content_size(self, context: ValidationContext) -> bool:
- """
- Check if the Web-based Data Entity has a `contentSize` property
- and if it is set to actual size of the downloadable content
- """
- result = True
- for entity in context.ro_crate.metadata.get_web_data_entities():
- assert entity.id is not None, "Entity has no @id"
- if entity.is_available():
- content_size = entity.get_property("contentSize")
- if content_size and int(content_size) != context.ro_crate.get_external_file_size(entity.id):
- context.result.add_issue(
- f'The property contentSize={content_size} of the Web-based Data Entity '
- f'{entity.id} does not match the actual size of '
- f'the downloadable content, i.e., {entity.content_size} (bytes)', self,
- violatingEntity=entity.id, violatingProperty='contentSize', violatingPropertyValue=content_size)
- result = False
- if not result and context.fail_fast:
- return result
- return result
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/should/5_web_data_entity_metadata.ttl b/tests/data/rocrate_validator_profiles/ro-crate/should/5_web_data_entity_metadata.ttl
deleted file mode 100644
index e4f3cc5..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/should/5_web_data_entity_metadata.ttl
+++ /dev/null
@@ -1,63 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix rdf: .
-@prefix dct: .
-@prefix schema_org: .
-@prefix sh: .
-@prefix owl: .
-@prefix xsd: .
-@prefix validator: .
-
-
-ro-crate:WebBasedDataEntityRequiredValueRestriction a sh:NodeShape ;
- sh:name "Web-based Data Entity: RECOMMENDED properties" ;
- sh:description """A Web-based Data Entity MUST be identified by an absolute URL and
- SHOULD have a `contentSize` and `sdDatePublished` property""" ;
- sh:targetClass ro-crate:WebDataEntity ;
- # Check if the Web-based Data Entity has a contentSize property
- sh:property [
- a sh:PropertyShape ;
- sh:minCount 1 ;
- sh:name "Web-based Data Entity: `contentSize` property" ;
- sh:description """Check if the Web-based Data Entity has a `contentSize` property""" ;
- sh:path schema_org:contentSize ;
- sh:datatype xsd:string ;
- sh:severity sh:Warning ;
- sh:message """Web-based Data Entities SHOULD have a `contentSize` property""" ;
- sh:sparql [
- sh:message "If the value is a string it must be a string representing an integer." ;
- sh:select """
- SELECT ?this ?value
- WHERE {
- ?this schema:contentSize ?value .
- FILTER NOT EXISTS {
- FILTER (xsd:integer(?value) = ?value)
- }
- }
- """ ;
- ] ;
- ] ;
- # Check if the Web-based Data Entity has a sdDatePublished property
- sh:property [
- a sh:PropertyShape ;
- sh:minCount 1 ;
- sh:name "Web-based Data Entity: `sdDatePublished` property" ;
- sh:description """Check if the Web-based Data Entity has a `sdDatePublished` property""" ;
- sh:path schema_org:sdDatePublished ;
- sh:pattern "^([\\+-]?\\d{4})((-?)((0[1-9]|1[0-2])(\\3([12]\\d|0[1-9]|3[01]))|W([0-4]\\d|5[0-2])(-?[1-7])|(00[1-9]|0[1-9]\\d|[12]\\d{2}|3([0-5]\\d|6[1-6])))([T\\s]((([01]\\d|2[0-3])((:?)?[0-5]\\d)?|24:?00)([\\.,]\\d+(?!:))?)?(\\17[0-5]\\d([\\.,]\\d+)?)?([zZ]|([\\+-])([01]\\d|2[0-3]):?([0-5]\\d)?)?)?)$" ;
- sh:message """Web-based Data Entities SHOULD have a `sdDatePublished` property to indicate when the absolute URL was accessed""" ;
- ] .
diff --git a/tests/data/rocrate_validator_profiles/ro-crate/should/6_contextual_entity_metadata.ttl b/tests/data/rocrate_validator_profiles/ro-crate/should/6_contextual_entity_metadata.ttl
deleted file mode 100644
index a90dbc2..0000000
--- a/tests/data/rocrate_validator_profiles/ro-crate/should/6_contextual_entity_metadata.ttl
+++ /dev/null
@@ -1,75 +0,0 @@
-# Copyright (c) 2024-2026 CRS4
-#
-# Licensed under the Apache License, Version 2.0 (the "License");
-# you may not use this file except in compliance with the License.
-# You may obtain a copy of the License at
-#
-# http://www.apache.org/licenses/LICENSE-2.0
-#
-# Unless required by applicable law or agreed to in writing, software
-# distributed under the License is distributed on an "AS IS" BASIS,
-# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-# See the License for the specific language governing permissions and
-# limitations under the License.
-
-@prefix ro: <./> .
-@prefix ro-crate: .
-@prefix schema: .
-@prefix sh: .
-@prefix xsd: .
-
-ro-crate:CreativeWorkAuthorMinimumRecommendedProperties a sh:NodeShape ;
- sh:name "CreativeWork Author: minimum RECOMMENDED properties" ;
- sh:description """The minimum recommended properties for a `CreativeWork Author` are `name` and `affiliation`.""" ;
- sh:targetClass ro-crate:CreativeWorkAuthor ;
- sh:property [
- sh:path schema:name ;
- sh:minCount 1 ;
- sh:dataType xsd:string ;
- sh:name "CreativeWork Author: RECOMMENDED name property" ;
- sh:description "Check if the author has a name." ;
- sh:message "The author SHOULD have a name." ;
- ] ;
- sh:property [
- sh:path schema:affiliation ;
- sh:minCount 1 ;
- sh:or (
- [ sh:dataType xsd:string ; ]
- [ sh:class schema:Organization ;]
- ) ;
- sh:severity sh:Warning ;
- sh:name "CreativeWork Author: RECOMMENDED affiliation property" ;
- sh:description "Check if the author has an organizational affiliation." ;
- sh:message "The author SHOULD have an organizational affiliation." ;
- ] ;
- sh:property [
- sh:path schema:affiliation ;
- sh:minCount 1 ;
- sh:class schema:Organization ;
- sh:severity sh:Warning ;
- sh:name "CreativeWork Author: RECOMMENDED Contextual Entity linked for the organizational `affiliation` property" ;
- sh:description "Check if the author has a Contextual Entity for the organizational `affiliation` property." ;
- sh:message "The author SHOULD have a Contextual Entity which specifies the organizational `affiliation`." ;
- ] .
-
-
-ro-crate:OrganizationRecommendedProperties a sh:NodeShape ;
- sh:name "Organization: RECOMMENDED properties" ;
- sh:description """The recommended properties for an `Organization` are `name` and `url`.""" ;
- sh:targetClass schema:Organization ;
- sh:property [
- sh:path schema:name ;
- sh:minCount 1 ;
- sh:dataType xsd:string ;
- sh:name "Organization: RECOMMENDED name property" ;
- sh:description "Check if the `organization` has a name." ;
- sh:message "The organization SHOULD have a name." ;
- ] ;
- sh:property [
- sh:path schema:url ;
- sh:minCount 1 ;
- sh:dataType xsd:anyURI ;
- sh:name "Organization: RECOMMENDED url property" ;
- sh:description "Check if the `organization` has a URL." ;
- sh:message "The organization SHOULD have a URL." ;
- ] .
From e97cab9662a993be273bc1a7a73b9798749848cb Mon Sep 17 00:00:00 2001
From: Douglas Lowe <10961945+douglowe@users.noreply.github.com>
Date: Fri, 6 Nov 2026 10:24:28 +0000
Subject: [PATCH 4/5] docs update for default ro-crate version
---
docs/api.md | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/docs/api.md b/docs/api.md
index 8b41fb7..1e85ca8 100644
--- a/docs/api.md
+++ b/docs/api.md
@@ -14,10 +14,10 @@ This validates the contents of an `ro-crate-metadata.json` document and returns
| Field | Required | Description |
|-------|----------|-------------|
| `crate_json` | yes | The metadata document, as a JSON string |
-| `profile_name` | no | Profile to validate against, e.g. `ro-crate-1.2`. The validator will default to `ro-crate-1.1` when this is omitted |
+| `profile_name` | no | Profile to validate against, e.g. `ro-crate-1.2`. The validator will default to `ro-crate-1.3` when this is omitted |
!!! warning
- Currently, the validation profile is not detected from the RO-Crate. In other words, a `conformsTo` declaration in the metadata does not influence which validation profile is used by the validator, and the validation always runs against `profile_name`, or `ro-crate-1.1` when it is omitted.
+ Currently, the validation profile is not detected from the RO-Crate. In other words, a `conformsTo` declaration in the metadata does not influence which validation profile is used by the validator, and the validation always runs against `profile_name`, or `ro-crate-1.3` when it is omitted.
To validate an ro-crate metadata file:
@@ -47,7 +47,7 @@ This queues validation of an RO-Crate held in the object store. The RO-Crate is
| Field | Required | Description |
|-------|----------|-------------|
-| `profile_name` | no | Profile to validate against; defaults to `ro-crate-1.1` when omitted |
+| `profile_name` | no | Profile to validate against; defaults to `ro-crate-1.3` when omitted |
| `webhook_url` | no | URL that receives the result when validation finishes |
```bash
@@ -101,7 +101,7 @@ An RO-Crate's `status` can be:
| `error` | The validation could not run; the reason is in an `error` field. No `detail` field is provided |
!!! note
- `detail` contains the complete validation report. `created_at` is the UTC time of a stored-crate validation, and `null` for metadata-only validation, which does not set it. `profile` is the requested profile name, or `null` when the default (`ro-crate-1.1`) was used.
+ `detail` contains the complete validation report. `created_at` is the UTC time of a stored-crate validation, and `null` for metadata-only validation, which does not set it. `profile` is the requested profile name, or `null` when the default (`ro-crate-1.3`) was used.
For stored RO-Crates the same object is saved to `{S3_RESULTS_PREFIX}/.json` and returned by the GET endpoint.
From 748648724d93ff951ad530ff0ffbf5f71d1da294 Mon Sep 17 00:00:00 2001
From: Alex Hambley <33315205+alexhambley@users.noreply.github.com>
Date: Fri, 6 Nov 2026 10:57:14 +0000
Subject: [PATCH 5/5] docs: EXTRA_PROFILES_PATH fix
---
README.md | 5 +++--
docs/five-safes.md | 6 +++---
docs/installation.md | 4 ++--
3 files changed, 8 insertions(+), 7 deletions(-)
diff --git a/README.md b/README.md
index 9d9b35d..ed7b36a 100644
--- a/README.md
+++ b/README.md
@@ -318,8 +318,9 @@ The RustFS console is at `http://localhost:9001` (default credentials are
To validate against profiles other than the bundled ones, mount a profiles
directory into **both** the flask and worker containers (metadata validation
-runs in flask; crate validation runs in the worker) and set `PROFILES_PATH` to
-the mounted path. See `docker-compose-develop.yml` for a working example.
+runs in flask; crate validation runs in the worker) and set `EXTRA_PROFILES_PATH`
+to the mounted path. This keeps the bundled base profiles available to the
+custom profiles. See `docker-compose-develop.yml` for a working example.
## Development
diff --git a/docs/five-safes.md b/docs/five-safes.md
index e998c96..a57e2bf 100644
--- a/docs/five-safes.md
+++ b/docs/five-safes.md
@@ -18,14 +18,14 @@ The `five-safes-crate` profile itself is defined in the [eScienceLab rocrate-val
A [complete example crate](https://github.com/eScienceLab/rocrate-validator/blob/five-safes-0.7.4-beta/tests/data/crates/valid/five-safes-crate-result/ro-crate-metadata.json) is available in the fork's test data.
!!! note
- The current `-fivesafes-profile` image pairs the profile with a base profile for RO-Crate 1.1, whilst the profile itself expects RO-Crate 1.2. The walkthrough below mounts the matched profile set instead.
+ The repository's profile directory contains custom profiles that depend on the validator's bundled RO-Crate profiles. Use `EXTRA_PROFILES_PATH` to keep those base profiles available.
-For this walkthrough, run the service from a checkout of this repository, with the repository's profile set mounted in place of the bundled profiles:
+For this walkthrough, run the service from a checkout of this repository, with the repository's custom profiles mounted alongside the bundled profiles:
```bash
docker run --rm -p 5001:5000 \
-e FLASK_APP=wsgi.py \
- -e PROFILES_PATH=/app/profiles \
+ -e EXTRA_PROFILES_PATH=/app/profiles \
-v "$PWD/tests/data/rocrate_validator_profiles:/app/profiles:ro" \
ghcr.io/esciencelab/ro-crate-validation-service-fivesafes-profile:latest
```
diff --git a/docs/installation.md b/docs/installation.md
index bcbb0c3..44cfee6 100644
--- a/docs/installation.md
+++ b/docs/installation.md
@@ -102,10 +102,10 @@ The following can all be set as environment variables for the service using an `
The validator comes with several RO-Crate profiles, and for the Five Safes RO-Crate, the prebuilt `ghcr.io/esciencelab/ro-crate-validation-service-fivesafes-profile` image has the `five-safes-crate` profile already included; see [Five Safes validation](five-safes.md).
-Other profiles can be provided by mounting the directory containing these profiles as a volume for the `flask` container. Mount the same directory as a volume for the `celery_worker` container as well if you have enabled stored-crate validation. Then set either the `EXTRA_PROFILES_PATH` or `PROFILES_PATH` environment variable to match the volume path. There is a working example in `docker-compose-develop.yml`.
+Other profiles can be provided by mounting the directory containing these profiles as a volume for the `flask` container. Mount the same directory as a volume for the `celery_worker` container as well if you have enabled stored-crate validation. Then set `EXTRA_PROFILES_PATH` to match the volume path so the bundled base profiles remain available. There is a working example in `docker-compose-develop.yml`.
!!! note
- `EXTRA_PROFILES_PATH` adds the directory to the bundled profiles, whereas `PROFILES_PATH` replaces them entirely. The two can be set together, in which case the validator takes profiles from both locations.
+ `EXTRA_PROFILES_PATH` adds the directory to the bundled profiles. Use `PROFILES_PATH` only when supplying a complete set of replacement profiles, including any base profiles your custom profiles depend on. The two can be set together, in which case the validator takes profiles from both locations.
## Offline validation