diff --git a/changelog/464.housekeeping.md b/changelog/464.housekeeping.md new file mode 100644 index 00000000..9478ed71 --- /dev/null +++ b/changelog/464.housekeeping.md @@ -0,0 +1 @@ +Handle error gracefully when loading schema instead of failing with an exception diff --git a/infrahub_sdk/ctl/schema.py b/infrahub_sdk/ctl/schema.py index 8c18b395..0e6ce548 100644 --- a/infrahub_sdk/ctl/schema.py +++ b/infrahub_sdk/ctl/schema.py @@ -77,7 +77,18 @@ def display_schema_load_errors(response: dict[str, Any], schemas_data: list[Sche elif len(loc_path) > 6: loc_type = loc_path[5] - input_label = node[loc_type][loc_path[6]].get("name", None) + error_data = node[loc_type] + attribute = loc_path[6] + + if isinstance(attribute, str): + input_label = None + for data in error_data: + if data.get(attribute) is not None: + input_label = data.get("name", None) + break + else: + input_label = error_data[attribute].get("name", None) + input_str = error.get("input", None) error_message = f"{loc_type[:-1].title()}: {input_label} ({input_str}) | {error['msg']} ({error['type']})" console.print(f" Node: {node.get('namespace', None)}{node.get('name', None)} | {error_message}") diff --git a/tests/unit/sdk/test_schema.py b/tests/unit/sdk/test_schema.py index f5d1ce10..64c2984b 100644 --- a/tests/unit/sdk/test_schema.py +++ b/tests/unit/sdk/test_schema.py @@ -392,3 +392,63 @@ async def test_display_schema_load_errors_details_namespace(mock_get_node) -> No Node: OuTInstance | namespace (OuT) | String should match pattern '^[A-Z]+$' (string_pattern_mismatch) """ assert output == expected_console + + +@mock.patch( + "infrahub_sdk.ctl.schema.get_node", + return_value={ + "name": "TailscaleSSHRule", + "namespace": "Security", + "icon": "mdi:security", + "inherit_from": ["SecurityRule"], + "attributes": [ + { + "name": "check_period", + "kind": "Number", + "optional": True, + "default_value": 720, + "min_value": 0, + "max_value": 10080, + }, + {"name": "accept_env", "kind": "List", "optional": True}, + { + "name": "action", + "optional": True, + "kind": "Dropdown", + "default_value": "allow", + "choices": [ + {"label": "allow", "name": "allow"}, + {"label": "check", "name": "check"}, + ], + }, + ], + }, +) +async def test_display_schema_load_errors_details_when_error_is_in_attribute_or_relationship(mock_get_node) -> None: + """Validate error message with details when loading schema and errors are in attribute or relationship.""" + error = { + "detail": [ + { + "type": "extra_forbidden", + "loc": ["body", "schemas", 0, "nodes", 4, "attributes", "min_value"], + "msg": "Extra inputs are not permitted", + "input": 0, + }, + { + "type": "extra_forbidden", + "loc": ["body", "schemas", 0, "nodes", 4, "attributes", "max_value"], + "msg": "Extra inputs are not permitted", + "input": 10080, + }, + ] + } + + with mock.patch("infrahub_sdk.ctl.schema.console", Console(file=StringIO(), width=1000)) as console: + display_schema_load_errors(response=error, schemas_data=[]) + assert mock_get_node.call_count == 2 + output = console.file.getvalue() + expected_console = """Unable to load the schema: + Node: SecurityTailscaleSSHRule | Attribute: check_period (0) | Extra inputs are not permitted (extra_forbidden) + Node: SecurityTailscaleSSHRule | Attribute: check_period (10080) | Extra inputs are not permitted (extra_forbidden) +""" + assert output == expected_console