diff --git a/CHANGELOG.md b/CHANGELOG.md index e9e11f9..aa4197c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Python Validate: - add more information to errors - Add unique ID checks + - Add path to output ## [0.4.0] - 2022-11-09 diff --git a/libcoveofds/python_validate.py b/libcoveofds/python_validate.py index 5db8824..58b430d 100644 --- a/libcoveofds/python_validate.py +++ b/libcoveofds/python_validate.py @@ -8,34 +8,34 @@ def __init__(self, schema_object: OFDSSchema): self._additional_check_results: list = [] self._schema_object: OFDSSchema = schema_object - def check_node_first_pass(self, node: dict): + def check_node_first_pass(self, node: dict, path: str): pass - def check_span_first_pass(self, span: dict): + def check_span_first_pass(self, span: dict, path: str): pass - def check_phase_first_pass(self, phase: dict): + def check_phase_first_pass(self, phase: dict, path: str): pass - def check_organisation_first_pass(self, organisation: dict): + def check_organisation_first_pass(self, organisation: dict, path: str): pass - def check_contract_first_pass(self, contract: dict): + def check_contract_first_pass(self, contract: dict, path: str): pass - def check_node_second_pass(self, node: dict): + def check_node_second_pass(self, node: dict, path: str): pass - def check_span_second_pass(self, span: dict): + def check_span_second_pass(self, span: dict, path: str): pass - def check_phase_second_pass(self, phase: dict): + def check_phase_second_pass(self, phase: dict, path: str): pass - def check_organisation_second_pass(self, organisation: dict): + def check_organisation_second_pass(self, organisation: dict, path: str): pass - def check_contract_second_pass(self, contract: dict): + def check_contract_second_pass(self, contract: dict, path: str): pass def get_additional_check_results(self): @@ -53,12 +53,12 @@ def __init__(self, schema_object: OFDSSchema): super().__init__(schema_object) self._node_ids_seen: list = [] - def check_node_first_pass(self, node: dict): + def check_node_first_pass(self, node: dict, path: str): id = node.get("id") if id: self._node_ids_seen.append(id) - def check_span_second_pass(self, span: dict): + def check_span_second_pass(self, span: dict, path: str): span_id = span.get("id") start = span.get("start") if start: @@ -81,7 +81,7 @@ def skip_if_any_links_have_external_span_data(self) -> bool: class NodesLocationAndSpansRouteAdditionalCheckForNetwork(AdditionalCheckForNetwork): - def check_node_first_pass(self, node: dict): + def check_node_first_pass(self, node: dict, path: str): location = node.get("location") if location: type = location.get("type") @@ -91,6 +91,7 @@ def check_node_first_pass(self, node: dict): "type": "node_location_type_incorrect", "node_id": node.get("id"), "incorrect_type": type, + "path": path + "/location/type", } ) if not self._is_json_coordinates(location.get("coordinates")): @@ -102,7 +103,7 @@ def check_node_first_pass(self, node: dict): } ) - def check_span_first_pass(self, span: dict): + def check_span_first_pass(self, span: dict, path: str): location = span.get("route") if location: type = location.get("type") @@ -145,13 +146,13 @@ def __init__(self, schema_object: OFDSSchema): super().__init__(schema_object) self._phases: dict = {} - def check_phase_first_pass(self, phase: dict): + def check_phase_first_pass(self, phase: dict, path: str): id = phase.get("id") name = phase.get("name") if id: self._phases[id] = name - def check_node_second_pass(self, node: dict): + def check_node_second_pass(self, node: dict, path: str): if "phase" in node and isinstance(node["phase"], dict): self._check_related_phase_object( node["phase"], @@ -169,7 +170,7 @@ def check_node_second_pass(self, node: dict): }, ) - def check_span_second_pass(self, span: dict): + def check_span_second_pass(self, span: dict, path: str): if "phase" in span and isinstance(span["phase"], dict): self._check_related_phase_object( span["phase"], @@ -187,7 +188,7 @@ def check_span_second_pass(self, span: dict): }, ) - def check_contract_second_pass(self, contract: dict): + def check_contract_second_pass(self, contract: dict, path: str): if "relatedPhases" in contract and isinstance(contract["relatedPhases"], list): for related_phase in contract["relatedPhases"]: if isinstance(related_phase, dict): @@ -245,13 +246,13 @@ def __init__(self, schema_object: OFDSSchema): super().__init__(schema_object) self._organisations: dict = {} - def check_organisation_first_pass(self, organisation: dict): + def check_organisation_first_pass(self, organisation: dict, path: str): id = organisation.get("id") name = organisation.get("name") if id: self._organisations[id] = name - def check_node_second_pass(self, node: dict): + def check_node_second_pass(self, node: dict, path: str): if "physicalInfrastructureProvider" in node and isinstance( node["physicalInfrastructureProvider"], dict ): @@ -293,7 +294,7 @@ def check_node_second_pass(self, node: dict): }, ) - def check_span_second_pass(self, span: dict): + def check_span_second_pass(self, span: dict, path: str): if "physicalInfrastructureProvider" in span and isinstance( span["physicalInfrastructureProvider"], dict ): @@ -354,7 +355,7 @@ def check_span_second_pass(self, span: dict): }, ) - def check_phase_second_pass(self, phase: dict): + def check_phase_second_pass(self, phase: dict, path: str): if "funders" in phase and isinstance(phase["funders"], list): for funder in phase["funders"]: if isinstance(funder, dict): @@ -413,7 +414,7 @@ def _check_related_organisation_object( class NodeInternationalConnectionCountryAdditionalCheckForNetwork( AdditionalCheckForNetwork ): - def check_node_first_pass(self, node: dict): + def check_node_first_pass(self, node: dict, path: str): if "internationalConnections" in node and isinstance( node["internationalConnections"], list ): @@ -434,7 +435,7 @@ def __init__(self, schema_object: OFDSSchema): super().__init__(schema_object) self._node_ids_used_in_spans: list = [] - def check_span_first_pass(self, span: dict): + def check_span_first_pass(self, span: dict, path: str): start = span.get("start") if start and start not in self._node_ids_used_in_spans: self._node_ids_used_in_spans.append(start) @@ -442,7 +443,7 @@ def check_span_first_pass(self, span: dict): if end and end not in self._node_ids_used_in_spans: self._node_ids_used_in_spans.append(end) - def check_node_second_pass(self, node: dict): + def check_node_second_pass(self, node: dict, path: str): id = node.get("id") if id and id not in self._node_ids_used_in_spans: self._additional_check_results.append( @@ -468,7 +469,7 @@ def __init__(self, schema_object: OFDSSchema): self._organisation_ids_seen: list = [] self._contract_ids_seen: list = [] - def check_node_first_pass(self, node: dict): + def check_node_first_pass(self, node: dict, path: str): id = node.get("id") if id and isinstance(id, str): if id in self._node_ids_seen: @@ -483,7 +484,7 @@ def check_node_first_pass(self, node: dict): pass - def check_span_first_pass(self, span: dict): + def check_span_first_pass(self, span: dict, path: str): id = span.get("id") if id and isinstance(id, str): if id in self._span_ids_seen: @@ -496,7 +497,7 @@ def check_span_first_pass(self, span: dict): else: self._span_ids_seen.append(id) - def check_phase_first_pass(self, phase: dict): + def check_phase_first_pass(self, phase: dict, path: str): id = phase.get("id") if id and isinstance(id, str): if id in self._phase_ids_seen: @@ -511,7 +512,7 @@ def check_phase_first_pass(self, phase: dict): pass - def check_organisation_first_pass(self, organisation: dict): + def check_organisation_first_pass(self, organisation: dict, path: str): id = organisation.get("id") if id and isinstance(id, str): if id in self._organisation_ids_seen: @@ -526,7 +527,7 @@ def check_organisation_first_pass(self, organisation: dict): pass - def check_contract_first_pass(self, contract: dict): + def check_contract_first_pass(self, contract: dict, path: str): id = contract.get("id") if id and isinstance(id, str): if id in self._contract_ids_seen: @@ -568,7 +569,7 @@ def validate(self, json_data: dict) -> list: # For each Network networks = json_data.get("networks") if isinstance(networks, list): - for network in networks: + for network_idx, network in enumerate(networks): if isinstance(network, dict): additional_check_instances = [ x(self._schema) for x in ADDITIONAL_CHECK_CLASSES_FOR_NETWORK @@ -626,35 +627,87 @@ def validate(self, json_data: dict) -> list: contracts = contracts if isinstance(contracts, list) else [] # First pass for additional_check_instance in additional_check_instances: - for node in nodes: - additional_check_instance.check_node_first_pass(node) - for span in spans: - additional_check_instance.check_span_first_pass(span) - for phase in phases: - additional_check_instance.check_phase_first_pass(phase) - for organisation in organisations: + for node_idx, node in enumerate(nodes): + additional_check_instance.check_node_first_pass( + node, + "/networks/" + + str(network_idx) + + "/nodes/" + + str(node_idx), + ) + for span_idx, span in enumerate(spans): + additional_check_instance.check_span_first_pass( + span, + "/networks/" + + str(network_idx) + + "/spans/" + + str(span_idx), + ) + for phase_idx, phase in enumerate(phases): + additional_check_instance.check_phase_first_pass( + phase, + "/networks/" + + str(network_idx) + + "/phases/" + + str(phase_idx), + ) + for organisation_idx, organisation in enumerate(organisations): additional_check_instance.check_organisation_first_pass( - organisation + organisation, + "/networks/" + + str(network_idx) + + "/organisations/" + + str(organisation_idx), ) - for contract in contracts: + for contract_idx, contract in enumerate(contracts): additional_check_instance.check_contract_first_pass( - contract + contract, + "/networks/" + + str(network_idx) + + "/contracts/" + + str(contract_idx), ) # Second pass for additional_check_instance in additional_check_instances: - for node in nodes: - additional_check_instance.check_node_second_pass(node) - for span in spans: - additional_check_instance.check_span_second_pass(span) - for phase in phases: - additional_check_instance.check_phase_second_pass(phase) - for organisation in organisations: + for node_idx, node in enumerate(nodes): + additional_check_instance.check_node_second_pass( + node, + "/networks/" + + str(network_idx) + + "/nodes/" + + str(node_idx), + ) + for span_idx, span in enumerate(spans): + additional_check_instance.check_span_second_pass( + span, + "/networks/" + + str(network_idx) + + "/spans/" + + str(span_idx), + ) + for phase_idx, phase in enumerate(phases): + additional_check_instance.check_phase_second_pass( + phase, + "/networks/" + + str(network_idx) + + "/phases/" + + str(phase_idx), + ) + for organisation_idx, organisation in enumerate(organisations): additional_check_instance.check_organisation_second_pass( - organisation + organisation, + "/networks/" + + str(network_idx) + + "/organisations/" + + str(organisation_idx), ) - for contract in contracts: + for contract_idx, contract in enumerate(contracts): additional_check_instance.check_contract_second_pass( - contract + contract, + "/networks/" + + str(network_idx) + + "/contracts/" + + str(contract_idx), ) # Results for additional_check_instance in additional_check_instances: diff --git a/tests/fixtures/pythonvalidate/node_location_type_incorrect_1.expected.json b/tests/fixtures/pythonvalidate/node_location_type_incorrect_1.expected.json index d71a38d..be4cbdf 100644 --- a/tests/fixtures/pythonvalidate/node_location_type_incorrect_1.expected.json +++ b/tests/fixtures/pythonvalidate/node_location_type_incorrect_1.expected.json @@ -3,6 +3,7 @@ "type": "node_location_type_incorrect", "node_id": "1", "incorrect_type": "LineString", + "path": "/networks/0/nodes/0/location/type", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } ]