Skip to content

Commit ab69396

Browse files
authored
Merge branch 'develop' into dependabot/pip/werkzeug-3.1.6
2 parents 7c86dfe + dab78e3 commit ab69396

9 files changed

Lines changed: 150 additions & 122 deletions

File tree

app/ro_crates/routes/post_routes.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,4 +111,6 @@ def validate_ro_crate_metadata(json_data) -> tuple[Response, int]:
111111
else:
112112
profile_name = None
113113

114-
return queue_ro_crate_metadata_validation_task(crate_json, profile_name)
114+
profiles_path = current_app.config["PROFILES_PATH"]
115+
116+
return queue_ro_crate_metadata_validation_task(crate_json, profile_name, profiles_path=profiles_path)

app/services/validation_service.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,14 +61,15 @@ def queue_ro_crate_validation_task(
6161

6262

6363
def queue_ro_crate_metadata_validation_task(
64-
crate_json: str, profile_name=None, webhook_url=None
64+
crate_json: str, profile_name=None, webhook_url=None, profiles_path=None
6565
) -> tuple[Response, int]:
6666
"""
6767
Queues an RO-Crate for validation with Celery.
6868
6969
:param crate_id: The ID of the RO-Crate to validate.
7070
:param profile_name: The profile to validate against.
7171
:param webhook_url: The URL to POST the validation results to.
72+
:param profiles_path: A path to the profile definition directory.
7273
:return: A tuple containing a JSON response and an HTTP status code.
7374
:raises: Exception: If an error occurs whilst queueing the task.
7475
"""
@@ -90,7 +91,8 @@ def queue_ro_crate_metadata_validation_task(
9091
result = process_validation_task_by_metadata.delay(
9192
crate_json,
9293
profile_name,
93-
webhook_url
94+
webhook_url,
95+
profiles_path
9496
)
9597
if webhook_url:
9698
return jsonify({"message": "Validation in progress"}), 202

app/tasks/validation_tasks.py

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import logging
88
import os
99
import shutil
10+
import json
1011
from typing import Optional
1112

1213
from rocrate_validator import services
@@ -22,7 +23,6 @@
2223
find_validation_object_on_minio
2324
)
2425
from app.utils.webhook_utils import send_webhook_notification
25-
from app.utils.file_utils import build_metadata_only_rocrate
2626

2727
logger = logging.getLogger(__name__)
2828

@@ -98,32 +98,27 @@ def process_validation_task_by_id(
9898

9999
@celery.task
100100
def process_validation_task_by_metadata(
101-
crate_json: str, profile_name: str | None, webhook_url: str | None
101+
crate_json: str, profile_name: str | None, webhook_url: str | None, profiles_path: Optional[str] = None
102102
) -> ValidationResult | str:
103103
"""
104104
Background task to process the RO-Crate validation for a given json metadata string.
105105
106106
:param crate_json: A string containing the RO-Crate JSON metadata to validate.
107107
:param profile_name: The name of the validation profile to use. Defaults to None.
108108
:param webhook_url: The webhook URL to send notifications to. Defaults to None.
109+
:param profiles_path: The path to the profiles definition directory. Defaults to None.
109110
:raises Exception: If an error occurs during the validation process.
110111
111112
:todo: Replace the Crate ID with a more comprehensive system, and replace profile name with URI.
112113
"""
113114

114-
skip_checks_list = ['ro-crate-1.1_12.1']
115-
file_path = None
116-
117115
try:
118-
# Fetch the RO-Crate from MinIO using the provided ID:
119-
file_path = build_metadata_only_rocrate(crate_json)
120-
121-
logging.info(f"Processing validation task for {file_path}")
116+
logging.info("Processing validation task for provided metadata string")
122117

123118
# Perform validation:
124-
validation_result = perform_ro_crate_validation(file_path,
119+
validation_result = perform_metadata_validation(crate_json,
125120
profile_name,
126-
skip_checks_list
121+
profiles_path
127122
)
128123

129124
if isinstance(validation_result, str):
@@ -132,9 +127,9 @@ def process_validation_task_by_metadata(
132127
raise Exception(f"Validation failed: {validation_result}")
133128

134129
if not validation_result.has_issues():
135-
logging.info(f"RO Crate {file_path} is valid.")
130+
logging.info("RO Crate metadata is valid.")
136131
else:
137-
logging.info(f"RO Crate {file_path} is invalid.")
132+
logging.info("RO Crate metadata is invalid.")
138133

139134
if webhook_url:
140135
send_webhook_notification(webhook_url, validation_result.to_json())
@@ -148,10 +143,6 @@ def process_validation_task_by_metadata(
148143
send_webhook_notification(webhook_url, error_data)
149144

150145
finally:
151-
# Clean up the temporary file if it was created:
152-
if file_path and os.path.exists(file_path):
153-
shutil.rmtree(file_path)
154-
155146
if isinstance(validation_result, str):
156147
return validation_result
157148
else:
@@ -196,6 +187,39 @@ def perform_ro_crate_validation(
196187
return str(e)
197188

198189

190+
def perform_metadata_validation(
191+
crate_json: str, profile_name: str | None, skip_checks_list: Optional[list] = None, profiles_path: Optional[str] = None
192+
) -> ValidationResult | str:
193+
"""
194+
Validates only RO-Crate metadata provided as a json string.
195+
196+
:param crate_json: The JSON string containing the metadata
197+
:param profile_name: The name of the validation profile to use. Defaults to None. If None, the CRS4 validator will
198+
attempt to determine the profile.
199+
:param profiles_path: The path to the profiles definition directory
200+
:param skip_checks_list: A list of checks to skip, if needed
201+
:return: The validation result.
202+
:raises Exception: If an error occurs during the validation process.
203+
"""
204+
205+
try:
206+
logging.info(f"Validating ro-crate metadata with profile {profile_name}")
207+
208+
settings = services.ValidationSettings(
209+
**({"metadata_only": True}),
210+
**({"metadata_dict": json.loads(crate_json)}),
211+
**({"profile_identifier": profile_name} if profile_name else {}),
212+
**({"skip_checks": skip_checks_list} if skip_checks_list else {}),
213+
**({"profiles_path": profiles_path} if profiles_path else {})
214+
)
215+
216+
return services.validate(settings)
217+
218+
except Exception as e:
219+
logging.error(f"Unexpected error during validation: {e}")
220+
return str(e)
221+
222+
199223
def check_ro_crate_exists(
200224
minio_client: object,
201225
bucket_name: str,

app/utils/file_utils.py

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

requirements.in

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
celery==5.6.2
22
minio==7.2.20
33
requests==2.32.5
4-
Flask==3.1.2
4+
Flask==3.1.3
55
Werkzeug==3.1.6
6-
redis==7.1.1
6+
redis==7.2.0
77
python-dotenv==1.2.1
88
apiflask==3.0.2
99
roc-validator==0.8

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ email-validator==2.3.0
5959
# via pydantic
6060
enum-tools==0.12.0
6161
# via roc-validator
62-
flask==3.1.2
62+
flask==3.1.3
6363
# via
6464
# -r requirements.in
6565
# apiflask
@@ -145,7 +145,7 @@ rdflib[html]==7.1.4
145145
# owlrl
146146
# pyshacl
147147
# roc-validator
148-
redis==7.1.1
148+
redis==7.2.0
149149
# via -r requirements.in
150150
requests==2.32.5
151151
# via

tests/test_api_routes.py

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -141,24 +141,26 @@ def test_validate_fails_missing_elements(client: FlaskClient, crate_id: str, pay
141141

142142
# Test POST API: /v1/ro_crates/validate_metadata
143143

144+
# TODO: Write tests for profiles_path environment variable. This will require a refactoring of the create_app function.
144145
@pytest.mark.parametrize(
145-
"payload, status_code, response_json",
146+
"payload, status_code, response_json, profiles_path",
146147
[
147148
(
148149
{
149150
"crate_json": '{"@context": "https://w3id.org/ro/crate/1.1/context"}',
150151
"profile_name": "default"
151-
}, 200, {"status": "success"}
152+
}, 200, {"status": "success"}, None
152153
),
153154
(
154155
{
155156
"crate_json": '{"@context": "https://w3id.org/ro/crate/1.1/context"}',
156-
}, 200, {"status": "success"}
157+
}, 200, {"status": "success"}, None
157158
),
158159
],
159160
ids=["success_with_all_fields", "success_without_profile_name"]
160161
)
161-
def test_validate_metadata_success(client: FlaskClient, payload: dict, status_code: int, response_json: dict):
162+
def test_validate_metadata_success(client: FlaskClient, payload: dict, status_code: int,
163+
response_json: dict, profiles_path: str):
162164
with patch("app.ro_crates.routes.post_routes.queue_ro_crate_metadata_validation_task") as mock_queue:
163165
mock_queue.return_value = (response_json, status_code)
164166

@@ -167,7 +169,7 @@ def test_validate_metadata_success(client: FlaskClient, payload: dict, status_co
167169
crate_json = payload["crate_json"] if "crate_json" in payload else None
168170
profile_name = payload["profile_name"] if "profile_name" in payload else None
169171

170-
mock_queue.assert_called_once_with(crate_json, profile_name)
172+
mock_queue.assert_called_once_with(crate_json, profile_name, profiles_path=profiles_path)
171173
assert response.status_code == status_code
172174
assert response.json == response_json
173175

tests/test_services.py

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -141,32 +141,32 @@ def test_queue_ro_crate_validation_task_failure(
141141
# Test function: queue_ro_crate_metadata_validation_task
142142

143143
@pytest.mark.parametrize(
144-
"crate_json, profile, webhook, status_code, return_value, response_json, delay_side_effect",
144+
"crate_json, profile, webhook, status_code, return_value, response_json, delay_side_effect, profiles_path",
145145
[
146146
(
147147
'{"@context": "https://w3id.org/ro/crate/1.1/context"}',
148148
"default", "http://webhook",
149149
202, None, {"message": "Validation in progress"},
150-
None
150+
None, None
151151
),
152152
(
153153
'{"@context": "https://w3id.org/ro/crate/1.1/context"}',
154154
"default", None,
155155
200, {"status": "ok"}, {"result": {"status": "ok"}},
156-
None
156+
None, None
157157
),
158158
(
159159
'{"@context": "https://w3id.org/ro/crate/1.1/context"}',
160160
"default", "http://webhook",
161161
500, None, {"error": "Celery error"},
162-
Exception("Celery error")
162+
Exception("Celery error"), None
163163
),
164164
],
165165
ids=["success_with_webhook", "success_without_webhook", "failure_celery_error"]
166166
)
167167
def test_queue_metadata(flask_app, crate_json: dict, profile: str, webhook: str,
168168
status_code: int, return_value: dict, response_json: dict,
169-
delay_side_effect: Exception):
169+
delay_side_effect: Exception, profiles_path: str):
170170
with patch("app.services.validation_service.process_validation_task_by_metadata.delay",
171171
side_effect=delay_side_effect) as mock_delay:
172172
mock_result = MagicMock()
@@ -175,9 +175,9 @@ def test_queue_metadata(flask_app, crate_json: dict, profile: str, webhook: str,
175175
if delay_side_effect is None:
176176
mock_delay.return_value = mock_result
177177

178-
response, status = queue_ro_crate_metadata_validation_task(crate_json, profile, webhook)
178+
response, status = queue_ro_crate_metadata_validation_task(crate_json, profile, webhook, profiles_path)
179179

180-
mock_delay.assert_called_once_with(crate_json, profile, webhook)
180+
mock_delay.assert_called_once_with(crate_json, profile, webhook, profiles_path)
181181
assert status == status_code
182182
assert response.json == response_json
183183

@@ -197,7 +197,8 @@ def test_queue_metadata(flask_app, crate_json: dict, profile: str, webhook: str,
197197
"{}",
198198
422, "Required parameter crate_json is empty"
199199
),
200-
]
200+
],
201+
ids=["missing_crate_json","invalid_json","empty_json"]
201202
)
202203
def test_queue_metadata_json_errors(flask_app, crate_json: str, status_code: int, response_error: str):
203204
response, status = queue_ro_crate_metadata_validation_task(crate_json)

0 commit comments

Comments
 (0)