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/docs/additional_checks.rst b/docs/additional_checks.rst index 9057130..e1762e9 100644 --- a/docs/additional_checks.rst +++ b/docs/additional_checks.rst @@ -93,4 +93,14 @@ Node not used in any spans Produces additional checks of type: -* `node_not_used_in_any_spans` \ No newline at end of file +* `node_not_used_in_any_spans` + +Unique ID's +----------- + +* `duplicate_node_id` +* `duplicate_span_id` +* `duplicate_phase_id` +* `duplicate_organisation_id` +* `duplicate_contract_id` + diff --git a/libcoveofds/python_validate.py b/libcoveofds/python_validate.py index 5db8824..7817012 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,24 +53,32 @@ 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: - self._check_node_id_valid(start, "span_start_node_not_found", span_id) + if start and isinstance(start, str) and not start in self._node_ids_seen: + self._additional_check_results.append( + { + "type": "span_start_node_not_found", + "missing_node_id": start, + "span_id": span_id, + "path": path + "/start", + } + ) end = span.get("end") - if end: - self._check_node_id_valid(end, "span_end_node_not_found", span_id) - - def _check_node_id_valid(self, node_id, error_type, span_id): - if not node_id in self._node_ids_seen: + if end and isinstance(end, str) and not end in self._node_ids_seen: self._additional_check_results.append( - {"type": error_type, "missing_node_id": node_id, "span_id": span_id} + { + "type": "span_end_node_not_found", + "missing_node_id": end, + "span_id": span_id, + "path": path + "/end", + } ) def skip_if_any_links_have_external_node_data(self) -> bool: @@ -81,7 +89,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 +99,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")): @@ -99,10 +108,11 @@ def check_node_first_pass(self, node: dict): "type": "node_location_coordinates_incorrect", "node_id": node.get("id"), "incorrect_coordinates": location.get("coordinates"), + "path": path + "/location/coordinates", } ) - 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") @@ -112,6 +122,7 @@ def check_span_first_pass(self, span: dict): "type": "span_route_type_incorrect", "span_id": span.get("id"), "incorrect_type": type, + "path": path + "/route/type", } ) if not self._is_json_list_coordinates(location.get("coordinates")): @@ -120,6 +131,7 @@ def check_span_first_pass(self, span: dict): "type": "span_route_coordinates_incorrect", "span_id": span.get("id"), "incorrect_coordinates": location.get("coordinates"), + "path": path + "/route/coordinates", } ) @@ -145,65 +157,85 @@ 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"], { "type": "node_phase_reference_id_not_found", "node_id": node.get("id"), + "path": path + "/phase/id", }, { "type": "node_phase_reference_name_does_not_match", "node_id": node.get("id"), + "path": path + "/phase/name", }, { "type": "node_phase_reference_name_set_but_not_in_original", "node_id": node.get("id"), + "path": path + "/phase/name", }, ) - 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"], { "type": "span_phase_reference_id_not_found", "span_id": span.get("id"), + "path": path + "/phase/id", }, { "type": "span_phase_reference_name_does_not_match", "span_id": span.get("id"), + "path": path + "/phase/name", }, { "type": "span_phase_reference_name_set_but_not_in_original", "span_id": span.get("id"), + "path": path + "/phase/name", }, ) - 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"]: + for related_phase_idx, related_phase in enumerate( + contract["relatedPhases"] + ): if isinstance(related_phase, dict): self._check_related_phase_object( related_phase, { "type": "contract_related_phase_reference_id_not_found", "contract_id": contract.get("id"), + "path": path + + "/relatedPhases/" + + str(related_phase_idx) + + "/id", }, { "type": "contract_related_phase_reference_name_does_not_match", "contract_id": contract.get("id"), + "path": path + + "/relatedPhases/" + + str(related_phase_idx) + + "/name", }, { "type": "contract_related_phase_reference_name_set_but_not_in_original", "contract_id": contract.get("id"), + "path": path + + "/relatedPhases/" + + str(related_phase_idx) + + "/name", }, ) @@ -245,13 +277,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 ): @@ -261,16 +293,19 @@ def check_node_second_pass(self, node: dict): "type": "node_organisation_reference_id_not_found", "node_id": node.get("id"), "field": "physicalInfrastructureProvider", + "path": path + "/physicalInfrastructureProvider/id", }, { "type": "node_organisation_reference_name_does_not_match", "node_id": node.get("id"), "field": "physicalInfrastructureProvider", + "path": path + "/physicalInfrastructureProvider/name", }, { "type": "node_organisation_reference_name_set_but_not_in_original", "node_id": node.get("id"), "field": "physicalInfrastructureProvider", + "path": path + "/physicalInfrastructureProvider/name", }, ) if "networkProvider" in node and isinstance(node["networkProvider"], dict): @@ -280,20 +315,23 @@ def check_node_second_pass(self, node: dict): "type": "node_organisation_reference_id_not_found", "node_id": node.get("id"), "field": "networkProvider", + "path": path + "/networkProvider/id", }, { "type": "node_organisation_reference_name_does_not_match", "node_id": node.get("id"), "field": "networkProvider", + "path": path + "/networkProvider/name", }, { "type": "node_organisation_reference_name_set_but_not_in_original", "node_id": node.get("id"), "field": "networkProvider", + "path": path + "/networkProvider/name", }, ) - 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 ): @@ -303,16 +341,19 @@ def check_span_second_pass(self, span: dict): "type": "span_organisation_reference_id_not_found", "span_id": span.get("id"), "field": "physicalInfrastructureProvider", + "path": path + "/physicalInfrastructureProvider/id", }, { "type": "span_organisation_reference_name_does_not_match", "span_id": span.get("id"), "field": "physicalInfrastructureProvider", + "path": path + "/physicalInfrastructureProvider/name", }, { "type": "span_organisation_reference_name_set_but_not_in_original", "span_id": span.get("id"), "field": "physicalInfrastructureProvider", + "path": path + "/physicalInfrastructureProvider/name", }, ) if "networkProvider" in span and isinstance(span["networkProvider"], dict): @@ -322,16 +363,19 @@ def check_span_second_pass(self, span: dict): "type": "span_organisation_reference_id_not_found", "span_id": span.get("id"), "field": "networkProvider", + "path": path + "/networkProvider/id", }, { "type": "span_organisation_reference_name_does_not_match", "span_id": span.get("id"), "field": "networkProvider", + "path": path + "/networkProvider/name", }, { "type": "span_organisation_reference_name_set_but_not_in_original", "span_id": span.get("id"), "field": "networkProvider", + "path": path + "/networkProvider/name", }, ) if "supplier" in span and isinstance(span["supplier"], dict): @@ -341,36 +385,42 @@ def check_span_second_pass(self, span: dict): "type": "span_organisation_reference_id_not_found", "span_id": span.get("id"), "field": "supplier", + "path": path + "/supplier/id", }, { "type": "span_organisation_reference_name_does_not_match", "span_id": span.get("id"), "field": "supplier", + "path": path + "/supplier/name", }, { "type": "span_organisation_reference_name_set_but_not_in_original", "span_id": span.get("id"), "field": "supplier", + "path": path + "/supplier/name", }, ) - 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"]: + for funder_idx, funder in enumerate(phase["funders"]): if isinstance(funder, dict): self._check_related_organisation_object( funder, { "type": "phase_organisation_reference_id_not_found", "phase_id": phase.get("id"), + "path": path + "/funders/" + str(funder_idx) + "/id", }, { "type": "phase_organisation_reference_name_does_not_match", "phase_id": phase.get("id"), + "path": path + "/funders/" + str(funder_idx) + "/name", }, { "type": "phase_organisation_reference_name_set_but_not_in_original", "phase_id": phase.get("id"), + "path": path + "/funders/" + str(funder_idx) + "/name", }, ) @@ -413,11 +463,13 @@ 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 ): - for international_connection in node["internationalConnections"]: + for international_connection_idx, international_connection in enumerate( + node["internationalConnections"] + ): if isinstance( international_connection, dict ) and not international_connection.get("country"): @@ -425,6 +477,9 @@ def check_node_first_pass(self, node: dict): { "type": "node_international_connections_country_not_set", "node_id": node.get("id"), + "path": path + + "/internationalConnections/" + + str(international_connection_idx), } ) @@ -434,7 +489,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,13 +497,14 @@ 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( { "type": "node_not_used_in_any_spans", "node_id": node.get("id"), + "path": path, } ) @@ -468,50 +524,41 @@ 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: self._additional_check_results.append( - { - "type": "duplicate_node_id", - "node_id": id, - } + {"type": "duplicate_node_id", "node_id": id, "path": path} ) else: self._node_ids_seen.append(id) 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: self._additional_check_results.append( - { - "type": "duplicate_span_id", - "span_id": id, - } + {"type": "duplicate_span_id", "span_id": id, "path": path} ) 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: self._additional_check_results.append( - { - "type": "duplicate_phase_id", - "phase_id": id, - } + {"type": "duplicate_phase_id", "phase_id": id, "path": path} ) else: self._phase_ids_seen.append(id) 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: @@ -519,6 +566,7 @@ def check_organisation_first_pass(self, organisation: dict): { "type": "duplicate_organisation_id", "organisation_id": id, + "path": path, } ) else: @@ -526,15 +574,12 @@ 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: self._additional_check_results.append( - { - "type": "duplicate_contract_id", - "contract_id": id, - } + {"type": "duplicate_contract_id", "contract_id": id, "path": path} ) else: self._contract_ids_seen.append(id) @@ -568,7 +613,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 @@ -591,6 +636,7 @@ def validate(self, json_data: dict) -> list: { "network_id": network.get("id"), "type": "has_links_with_external_node_data", + "path": "/networks/" + str(network_idx), } ) links_with_external_spans = [ @@ -610,6 +656,7 @@ def validate(self, json_data: dict) -> list: { "network_id": network.get("id"), "type": "has_links_with_external_span_data", + "path": "/networks/" + str(network_idx), } ) nodes = network.get("nodes", []) @@ -626,35 +673,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/end_node_not_found_1.expected.json b/tests/fixtures/pythonvalidate/end_node_not_found_1.expected.json index 07b7492..26e5af6 100644 --- a/tests/fixtures/pythonvalidate/end_node_not_found_1.expected.json +++ b/tests/fixtures/pythonvalidate/end_node_not_found_1.expected.json @@ -3,6 +3,7 @@ "type": "span_end_node_not_found", "missing_node_id": "2467", "span_id": "1", + "path": "/networks/0/spans/0/end", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } ] diff --git a/tests/fixtures/pythonvalidate/node_international_connections_country_not_set_1.expected.json b/tests/fixtures/pythonvalidate/node_international_connections_country_not_set_1.expected.json index 664f52b..9619f75 100644 --- a/tests/fixtures/pythonvalidate/node_international_connections_country_not_set_1.expected.json +++ b/tests/fixtures/pythonvalidate/node_international_connections_country_not_set_1.expected.json @@ -2,6 +2,7 @@ { "type": "node_international_connections_country_not_set", "node_id": "1", + "path": "/networks/0/nodes/0/internationalConnections/0", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } ] diff --git a/tests/fixtures/pythonvalidate/node_location_coordinates_incorrect_1.expected.json b/tests/fixtures/pythonvalidate/node_location_coordinates_incorrect_1.expected.json index 3856293..779cbd7 100644 --- a/tests/fixtures/pythonvalidate/node_location_coordinates_incorrect_1.expected.json +++ b/tests/fixtures/pythonvalidate/node_location_coordinates_incorrect_1.expected.json @@ -56,6 +56,7 @@ 6.713 ] ], + "path": "/networks/0/nodes/0/location/coordinates", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } ] 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" } ] diff --git a/tests/fixtures/pythonvalidate/node_not_used_in_any_spans_1.expected.json b/tests/fixtures/pythonvalidate/node_not_used_in_any_spans_1.expected.json index 9c3f240..213d676 100644 --- a/tests/fixtures/pythonvalidate/node_not_used_in_any_spans_1.expected.json +++ b/tests/fixtures/pythonvalidate/node_not_used_in_any_spans_1.expected.json @@ -2,6 +2,7 @@ { "type": "node_not_used_in_any_spans", "node_id": "3", + "path": "/networks/0/nodes/2", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } ] diff --git a/tests/fixtures/pythonvalidate/organisation_id_not_found_1.expected.json b/tests/fixtures/pythonvalidate/organisation_id_not_found_1.expected.json index af2ed64..def8e4c 100644 --- a/tests/fixtures/pythonvalidate/organisation_id_not_found_1.expected.json +++ b/tests/fixtures/pythonvalidate/organisation_id_not_found_1.expected.json @@ -3,6 +3,7 @@ "type": "node_organisation_reference_id_not_found", "node_id": "1", "field": "physicalInfrastructureProvider", + "path": "/networks/0/nodes/0/physicalInfrastructureProvider/id", "organisation_id_not_found": "2", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, @@ -10,6 +11,7 @@ "type": "node_organisation_reference_id_not_found", "node_id": "1", "field": "networkProvider", + "path": "/networks/0/nodes/0/networkProvider/id", "organisation_id_not_found": "3", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, @@ -17,6 +19,7 @@ "type": "span_organisation_reference_id_not_found", "span_id": "1", "field": "physicalInfrastructureProvider", + "path": "/networks/0/spans/0/physicalInfrastructureProvider/id", "organisation_id_not_found": "4", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, @@ -24,6 +27,7 @@ "type": "span_organisation_reference_id_not_found", "span_id": "1", "field": "networkProvider", + "path": "/networks/0/spans/0/networkProvider/id", "organisation_id_not_found": "5", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, @@ -31,12 +35,14 @@ "type": "span_organisation_reference_id_not_found", "span_id": "1", "field": "supplier", + "path": "/networks/0/spans/0/supplier/id", "organisation_id_not_found": "6", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "phase_organisation_reference_id_not_found", "phase_id": "1", + "path": "/networks/0/phases/0/funders/0/id", "organisation_id_not_found": "7", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } diff --git a/tests/fixtures/pythonvalidate/organisation_name_not_match_1.expected.json b/tests/fixtures/pythonvalidate/organisation_name_not_match_1.expected.json index ceb178a..82dfddb 100644 --- a/tests/fixtures/pythonvalidate/organisation_name_not_match_1.expected.json +++ b/tests/fixtures/pythonvalidate/organisation_name_not_match_1.expected.json @@ -3,6 +3,7 @@ "type": "node_organisation_reference_name_does_not_match", "node_id": "1", "field": "physicalInfrastructureProvider", + "path": "/networks/0/nodes/0/physicalInfrastructureProvider/name", "name_in_reference": "Nothing Nowhere A", "name_should_be": "Everything Everywhere", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" @@ -11,6 +12,7 @@ "type": "node_organisation_reference_name_does_not_match", "node_id": "1", "field": "networkProvider", + "path": "/networks/0/nodes/0/networkProvider/name", "name_in_reference": "Nothing Nowhere B", "name_should_be": "Everything Everywhere", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" @@ -19,6 +21,7 @@ "type": "span_organisation_reference_name_does_not_match", "span_id": "1", "field": "physicalInfrastructureProvider", + "path": "/networks/0/spans/0/physicalInfrastructureProvider/name", "name_in_reference": "Nothing Nowhere C", "name_should_be": "Everything Everywhere", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" @@ -27,6 +30,7 @@ "type": "span_organisation_reference_name_does_not_match", "span_id": "1", "field": "networkProvider", + "path": "/networks/0/spans/0/networkProvider/name", "name_in_reference": "Nothing Nowhere D", "name_should_be": "Everything Everywhere", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" @@ -35,6 +39,7 @@ "type": "span_organisation_reference_name_does_not_match", "span_id": "1", "field": "supplier", + "path": "/networks/0/spans/0/supplier/name", "name_in_reference": "Nothing Nowhere E", "name_should_be": "Everything Everywhere", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" @@ -42,6 +47,7 @@ { "type": "phase_organisation_reference_name_does_not_match", "phase_id": "1", + "path": "/networks/0/phases/0/funders/0/name", "name_in_reference": "Nothing Nowhere F", "name_should_be": "Everything Everywhere", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" diff --git a/tests/fixtures/pythonvalidate/organisation_reference_name_set_but_not_in_original_1.expected.json b/tests/fixtures/pythonvalidate/organisation_reference_name_set_but_not_in_original_1.expected.json index dadfe80..611eadc 100644 --- a/tests/fixtures/pythonvalidate/organisation_reference_name_set_but_not_in_original_1.expected.json +++ b/tests/fixtures/pythonvalidate/organisation_reference_name_set_but_not_in_original_1.expected.json @@ -3,6 +3,7 @@ "type": "node_organisation_reference_name_set_but_not_in_original", "node_id": "1", "field": "physicalInfrastructureProvider", + "path": "/networks/0/nodes/0/physicalInfrastructureProvider/name", "name_in_reference": "Nothing Nowhere A", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, @@ -10,6 +11,7 @@ "type": "node_organisation_reference_name_set_but_not_in_original", "node_id": "1", "field": "networkProvider", + "path": "/networks/0/nodes/0/networkProvider/name", "name_in_reference": "Nothing Nowhere B", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, @@ -17,6 +19,7 @@ "type": "span_organisation_reference_name_set_but_not_in_original", "span_id": "1", "field": "physicalInfrastructureProvider", + "path": "/networks/0/spans/0/physicalInfrastructureProvider/name", "name_in_reference": "Nothing Nowhere C", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, @@ -24,6 +27,7 @@ "type": "span_organisation_reference_name_set_but_not_in_original", "span_id": "1", "field": "networkProvider", + "path": "/networks/0/spans/0/networkProvider/name", "name_in_reference": "Nothing Nowhere D", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, @@ -31,12 +35,14 @@ "type": "span_organisation_reference_name_set_but_not_in_original", "span_id": "1", "field": "supplier", + "path": "/networks/0/spans/0/supplier/name", "name_in_reference": "Nothing Nowhere E", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "phase_organisation_reference_name_set_but_not_in_original", "phase_id": "1", + "path": "/networks/0/phases/0/funders/0/name", "name_in_reference": "Nothing Nowhere F", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } diff --git a/tests/fixtures/pythonvalidate/phase_id_not_found_1.expected.json b/tests/fixtures/pythonvalidate/phase_id_not_found_1.expected.json index cef310a..bb8ddd0 100644 --- a/tests/fixtures/pythonvalidate/phase_id_not_found_1.expected.json +++ b/tests/fixtures/pythonvalidate/phase_id_not_found_1.expected.json @@ -2,18 +2,21 @@ { "type": "node_phase_reference_id_not_found", "node_id": "1", + "path": "/networks/0/nodes/0/phase/id", "phase_id_not_found": "2", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "span_phase_reference_id_not_found", "span_id": "1", + "path": "/networks/0/spans/0/phase/id", "phase_id_not_found": "3", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "contract_related_phase_reference_id_not_found", "contract_id": "1", + "path": "/networks/0/contracts/0/relatedPhases/0/id", "phase_id_not_found": "4", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } diff --git a/tests/fixtures/pythonvalidate/phase_name_not_match_1.expected.json b/tests/fixtures/pythonvalidate/phase_name_not_match_1.expected.json index 2ad8f9f..1755fb3 100644 --- a/tests/fixtures/pythonvalidate/phase_name_not_match_1.expected.json +++ b/tests/fixtures/pythonvalidate/phase_name_not_match_1.expected.json @@ -2,6 +2,7 @@ { "type": "node_phase_reference_name_does_not_match", "node_id": "1", + "path": "/networks/0/nodes/0/phase/name", "name_in_reference": "I forget", "name_should_be": "NextGen Phase 1", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" @@ -9,6 +10,7 @@ { "type": "span_phase_reference_name_does_not_match", "span_id": "1", + "path": "/networks/0/spans/0/phase/name", "name_in_reference": "I forget again", "name_should_be": "NextGen Phase 1", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" @@ -16,6 +18,7 @@ { "type": "contract_related_phase_reference_name_does_not_match", "contract_id": "1", + "path": "/networks/0/contracts/0/relatedPhases/0/name", "name_in_reference": "I forget, really I'm so bad", "name_should_be": "NextGen Phase 1", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" diff --git a/tests/fixtures/pythonvalidate/phase_reference_name_set_but_not_in_original_1.expected.json b/tests/fixtures/pythonvalidate/phase_reference_name_set_but_not_in_original_1.expected.json index 63cd341..29eac1a 100644 --- a/tests/fixtures/pythonvalidate/phase_reference_name_set_but_not_in_original_1.expected.json +++ b/tests/fixtures/pythonvalidate/phase_reference_name_set_but_not_in_original_1.expected.json @@ -2,24 +2,28 @@ { "type": "node_phase_reference_name_set_but_not_in_original", "node_id": "1", + "path": "/networks/0/nodes/0/phase/name", "name_in_reference": "I forget", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "node_phase_reference_name_set_but_not_in_original", "node_id": "2", + "path": "/networks/0/nodes/1/phase/name", "name_in_reference": "I forget again", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "span_phase_reference_name_set_but_not_in_original", "span_id": "1", + "path": "/networks/0/spans/0/phase/name", "name_in_reference": "I forget more", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "contract_related_phase_reference_name_set_but_not_in_original", "contract_id": "1", + "path": "/networks/0/contracts/0/relatedPhases/0/name", "name_in_reference": "I forget, did I even know?", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } diff --git a/tests/fixtures/pythonvalidate/span_route_coordinates_incorrect_1.expected.json b/tests/fixtures/pythonvalidate/span_route_coordinates_incorrect_1.expected.json index 79221ff..0aa7667 100644 --- a/tests/fixtures/pythonvalidate/span_route_coordinates_incorrect_1.expected.json +++ b/tests/fixtures/pythonvalidate/span_route_coordinates_incorrect_1.expected.json @@ -6,6 +6,7 @@ -1.628, 6.711 ], + "path": "/networks/0/spans/0/route/coordinates", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } ] diff --git a/tests/fixtures/pythonvalidate/span_route_type_incorrect_1.expected.json b/tests/fixtures/pythonvalidate/span_route_type_incorrect_1.expected.json index 25e585b..e79d849 100644 --- a/tests/fixtures/pythonvalidate/span_route_type_incorrect_1.expected.json +++ b/tests/fixtures/pythonvalidate/span_route_type_incorrect_1.expected.json @@ -3,6 +3,7 @@ "type": "span_route_type_incorrect", "span_id": "1", "incorrect_type": "Point", + "path": "/networks/0/spans/0/route/type", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } ] diff --git a/tests/fixtures/pythonvalidate/start_node_not_found_1.expected.json b/tests/fixtures/pythonvalidate/start_node_not_found_1.expected.json index 8deaa93..0363a9d 100644 --- a/tests/fixtures/pythonvalidate/start_node_not_found_1.expected.json +++ b/tests/fixtures/pythonvalidate/start_node_not_found_1.expected.json @@ -3,6 +3,7 @@ "type": "span_start_node_not_found", "missing_node_id": "167", "span_id": "1", + "path": "/networks/0/spans/0/start", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } ] diff --git a/tests/fixtures/pythonvalidate/start_node_not_found_but_has_external_nodes_1.expected.json b/tests/fixtures/pythonvalidate/start_node_not_found_but_has_external_nodes_1.expected.json index 1fccb07..fccaef9 100644 --- a/tests/fixtures/pythonvalidate/start_node_not_found_but_has_external_nodes_1.expected.json +++ b/tests/fixtures/pythonvalidate/start_node_not_found_but_has_external_nodes_1.expected.json @@ -1,6 +1,7 @@ [ { "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4", - "type": "has_links_with_external_node_data" + "type": "has_links_with_external_node_data", + "path": "/networks/0" } ] diff --git a/tests/fixtures/pythonvalidate/unique_ids_1.expected.json b/tests/fixtures/pythonvalidate/unique_ids_1.expected.json index afbda94..008be48 100644 --- a/tests/fixtures/pythonvalidate/unique_ids_1.expected.json +++ b/tests/fixtures/pythonvalidate/unique_ids_1.expected.json @@ -2,26 +2,31 @@ { "type": "duplicate_node_id", "node_id": "2", + "path": "/networks/0/nodes/2", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "duplicate_span_id", "span_id": "1", + "path": "/networks/0/spans/1", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "duplicate_phase_id", "phase_id": "2", + "path": "/networks/0/phases/1", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "duplicate_organisation_id", "organisation_id": "2", + "path": "/networks/0/organisations/1", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" }, { "type": "duplicate_contract_id", "contract_id": "2", + "path": "/networks/0/contracts/1", "network_id": "a096d627-72e1-4f9b-b129-951b1737bff4" } ]