Skip to content

Commit 9783ac1

Browse files
committed
Introduce optional pre/post_validation_schemas into the schema model and support its usage in the validationToMessages endpoint
1 parent ff9cdaa commit 9783ac1

3 files changed

Lines changed: 140 additions & 63 deletions

File tree

esdlvalidator/api/controller/validationToMessages.py

Lines changed: 95 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
from flask_restx import Resource
66

77
from esdlvalidator.api import app
8-
from esdlvalidator.api.controller import validationService
8+
from esdlvalidator.api.controller import validationService, schemaService
99
from esdlvalidator.core.exceptions import SchemaNotFound
1010

1111
parser = app.api.parser()
@@ -48,74 +48,108 @@ def post(self):
4848
if "schemas" not in request.args:
4949
return "Bad Request: Required 'schemas' parameter missing", 400
5050

51-
schema_list = [id for id in request.args["schemas"].split(",")]
51+
# Main schema refs requested by user
52+
main_schema_refs = [id_or_name for id_or_name in request.args["schemas"].split(",")]
5253

5354
try:
54-
result = validationService.validateContents(file, schema_list)
55+
main_schema_defs = schemaService.get_by_id_or_name(main_schema_refs)
56+
57+
# Collect all schemas to run
58+
pre_refs = []
59+
post_refs = []
60+
for schema in main_schema_defs:
61+
pre_refs.extend(schema.get("pre_validation_schemas", []))
62+
post_refs.extend(schema.get("post_validation_schemas", []))
63+
64+
# Remove duplicates while preserving order
65+
def unique(seq):
66+
return list(dict.fromkeys(seq))
67+
68+
pre_refs = unique(pre_refs)
69+
post_refs = unique(post_refs)
70+
71+
# Run validations in order
72+
results = []
73+
74+
if pre_refs:
75+
pre_defs = schemaService.get_by_id_or_name(pre_refs)
76+
pre_ids = [schema["id"] for schema in pre_defs]
77+
results.append(validationService.validateContents(file, pre_ids))
78+
79+
main_ids = [schema["id"] for schema in main_schema_defs]
80+
results.append(validationService.validateContents(file, main_ids))
81+
82+
if post_refs:
83+
post_defs = schemaService.get_by_id_or_name(post_refs)
84+
post_ids = [schema["id"] for schema in post_defs]
85+
results.append(validationService.validateContents(file, post_ids))
86+
5587
except SchemaNotFound as e:
5688
return e.message, e.statusCode
5789

5890
json_result: list[AssetMessage] = []
5991

60-
for schema in result["schemas"]:
61-
if "validations" not in schema:
62-
continue
63-
64-
for validation in schema["validations"]:
65-
for severity_key in ["errors", "warnings"]:
66-
severity = severity_key.rstrip("s").upper()
67-
68-
if severity_key in validation:
69-
for issue in validation[severity_key]:
70-
asset_id = issue.get("offending_asset")
71-
72-
if asset_id is None:
73-
raise KeyError(
74-
f"Can not associate message with an asset as 'offending_asset' key not found."
75-
)
76-
77-
message = issue.get("message")
78-
if isinstance(message, dict):
79-
validation_msg = message.get("validation_message", "")
80-
check_msgs = message.get("check_result_message", [])
81-
if isinstance(check_msgs, str):
82-
check_msgs = [check_msgs]
83-
else:
84-
validation_msg = ""
85-
check_msgs = [str(message)]
86-
87-
asset_message = self.get_asset_message(asset_id, json_result)
88-
89-
if asset_message is None:
90-
new_asset_message = AssetMessage(
91-
assetID=asset_id,
92-
messages=[
93-
ValidationResult(
94-
severity=severity,
95-
validation_message=validation_msg,
96-
check_result_messages=check_msgs,
97-
)
98-
],
99-
)
100-
json_result.append(new_asset_message)
101-
102-
else:
103-
# Try to find existing validation_message entry and extend its check_result_messages
104-
matched = False
105-
for v_result in asset_message["messages"]:
106-
if v_result["validation_message"] == validation_msg:
107-
v_result["check_result_messages"].extend(check_msgs)
108-
matched = True
109-
break
110-
111-
if not matched:
112-
asset_message["messages"].append(
113-
ValidationResult(
114-
severity=severity,
115-
validation_message=validation_msg,
116-
check_result_messages=check_msgs,
117-
)
92+
for result in results:
93+
for schema in result.get("schemas", []):
94+
if "validations" not in schema:
95+
continue
96+
97+
for validation in schema["validations"]:
98+
for severity_key in ["errors", "warnings"]:
99+
severity = severity_key.rstrip("s").upper()
100+
101+
if severity_key in validation:
102+
for issue in validation[severity_key]:
103+
# TODO: handle issue is a string
104+
asset_id = issue.get("offending_asset")
105+
106+
if asset_id is None:
107+
raise KeyError(
108+
f"Can not associate message with an asset as 'offending_asset' key not found."
109+
)
110+
111+
message = issue.get("message")
112+
if isinstance(message, dict):
113+
validation_msg = message.get("validation_message", "")
114+
check_msgs = message.get("check_result_message", [])
115+
if isinstance(check_msgs, str):
116+
check_msgs = [check_msgs]
117+
else:
118+
validation_msg = ""
119+
check_msgs = [str(message)]
120+
121+
asset_message = self.get_asset_message(asset_id, json_result)
122+
123+
if asset_message is None:
124+
new_asset_message = AssetMessage(
125+
assetID=asset_id,
126+
messages=[
127+
ValidationResult(
128+
severity=severity,
129+
validation_message=validation_msg,
130+
check_result_messages=check_msgs,
131+
)
132+
],
118133
)
134+
json_result.append(new_asset_message)
135+
136+
else:
137+
# Try to find existing validation_message entry and extend its check_result_messages
138+
matched = False
139+
for v_result in asset_message["messages"]:
140+
if v_result["validation_message"] == validation_msg:
141+
v_result["check_result_messages"].extend(check_msgs)
142+
matched = True
143+
break
144+
145+
if not matched:
146+
asset_message["messages"].append(
147+
ValidationResult(
148+
severity=severity,
149+
validation_message=validation_msg,
150+
check_result_messages=check_msgs,
151+
)
152+
)
119153

120154
return Response(response=json.dumps(json_result), status=200, mimetype="text/json")
121155

esdlvalidator/api/models.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,7 @@ class ValidationType(Enum):
4242
"id": fields.String(required=False, description="ID of the validation schema", example="6027a402e658599189817ba2"),
4343
"name": fields.String(required=True, description="Name of the validation schema", example="My validation schema"),
4444
"description": fields.String(required=True, description="Description of the validation schema", example='The is a user defined validation schema'),
45+
"pre_validation_schemas": fields.List(fields.String, required=False, description="An optional list of schema IDs or names to run before this schema", example=["Schema A"]),
46+
"post_validation_schemas": fields.List(fields.String, required=False, description="An optional list of schema IDs or names to run after this schema", example=["Schema C"]),
4547
"validations": fields.List(fields.Nested(schema_validation), required=True, description="List of validations")
4648
})

esdlvalidator/api/service/schema.py

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import json
22

33
from esdlvalidator.validation.abstract_repository import SchemaRepository
4+
from esdlvalidator.core.exceptions import SchemaNotFound
45

56

67
class SchemaService:
@@ -17,8 +18,10 @@ def get_all(self):
1718
"""
1819

1920
schemas = self.repo.get_all()
20-
return [{"id": str(schema["id"]), "name": schema["name"], "description": schema["description"]} for schema in
21-
schemas]
21+
return [
22+
{"id": str(schema["id"]), "name": schema["name"], "description": schema["description"]}
23+
for schema in schemas
24+
]
2225

2326
def get_by_id(self, id: str):
2427
"""Get a schema by schema id
@@ -50,6 +53,44 @@ def get_by_name(self, name: str):
5053

5154
return self.repo.get_by_name(name)
5255

56+
def get_by_id_or_name(self, ids_or_names: list[str]):
57+
"""
58+
Resolve a list of schema identifiers (IDs or names) into full schema objects.
59+
60+
Args:
61+
ids_or_names: List of schema IDs or names, e.g. ["123", "Schema A"]
62+
63+
Returns:
64+
List of schema objects
65+
66+
Raises:
67+
SchemaNotFound: If any schema cannot be resolved
68+
"""
69+
resolved = []
70+
71+
for value in ids_or_names:
72+
schema = None
73+
74+
# Try ID
75+
try:
76+
schema = self.get_by_id(value)
77+
except Exception:
78+
pass
79+
80+
# Try name
81+
if schema is None:
82+
try:
83+
schema = self.get_by_name(value)
84+
except Exception:
85+
pass
86+
87+
if schema is None:
88+
raise SchemaNotFound(f"Schema '{value}' not found")
89+
90+
resolved.append(schema)
91+
92+
return resolved
93+
5394
def insert(self, schema: str):
5495
"""Insert a schema into the database
5596

0 commit comments

Comments
 (0)