From 5d58f68a288fddcb9cb48eca39e6e67d0b8b11fa Mon Sep 17 00:00:00 2001 From: Davide Armand Date: Thu, 26 Sep 2024 13:19:09 +0200 Subject: [PATCH] feat: return all schema validation errors --- src/karapace/schema_registry.py | 6 ++++-- src/karapace/schema_registry_apis.py | 3 ++- .../test_dependencies_compatibility_protobuf.py | 10 +++++----- tests/integration/test_schema.py | 10 +++++++--- tests/integration/test_schema_protobuf.py | 4 ++-- 5 files changed, 20 insertions(+), 13 deletions(-) diff --git a/src/karapace/schema_registry.py b/src/karapace/schema_registry.py index ee0ae88cd..67f58fddd 100644 --- a/src/karapace/schema_registry.py +++ b/src/karapace/schema_registry.py @@ -352,12 +352,14 @@ async def write_new_schema_local( result = self.check_schema_compatibility(new_schema, subject) if is_incompatible(result): - message = set(result.messages).pop() if result.messages else "" LOG.warning( "Incompatible schema: %s, incompatibilities: %s", result.compatibility, result.incompatibilities ) compatibility_mode = self.get_compatibility_mode(subject=subject) - raise IncompatibleSchema(f"Incompatible schema, compatibility_mode={compatibility_mode.value} {message}") + raise IncompatibleSchema( + f"Incompatible schema, compatibility_mode={compatibility_mode.value}. " + f"Incompatibilities: {', '.join(result.messages)[:300]}" + ) # We didn't find an existing schema and the schema is compatible so go and create one version = self.database.get_next_version(subject=subject) diff --git a/src/karapace/schema_registry_apis.py b/src/karapace/schema_registry_apis.py index f6d4593f7..a37a3ff9f 100644 --- a/src/karapace/schema_registry_apis.py +++ b/src/karapace/schema_registry_apis.py @@ -412,7 +412,8 @@ async def compatibility_check( result = SchemaCompatibility.check_compatibility(old_schema, new_schema, compatibility_mode) if is_incompatible(result): - self.r({"is_compatible": False}, content_type) + maybe_truncated_error = ", ".join(result.messages)[:300] + self.r({"is_compatible": False, "incompatibilities": maybe_truncated_error}, content_type) self.r({"is_compatible": True}, content_type) async def schemas_list(self, content_type: str, *, request: HTTPRequest, user: User | None = None): diff --git a/tests/integration/test_dependencies_compatibility_protobuf.py b/tests/integration/test_dependencies_compatibility_protobuf.py index 2bacbdf7b..725611b5c 100644 --- a/tests/integration/test_dependencies_compatibility_protobuf.py +++ b/tests/integration/test_dependencies_compatibility_protobuf.py @@ -183,7 +183,7 @@ async def test_protobuf_schema_compatibility_dependencies(registry_async_client: json={"schemaType": "PROTOBUF", "schema": evolved_schema, "references": evolved_references}, ) assert res.status_code == 200 - assert res.json() == {"is_compatible": False} + assert res.json().get("is_compatible") is False @pytest.mark.parametrize("trail", ["", "/"]) @@ -271,7 +271,7 @@ async def test_protobuf_schema_compatibility_dependencies1(registry_async_client json={"schemaType": "PROTOBUF", "schema": evolved_schema, "references": evolved_references}, ) assert res.status_code == 200 - assert res.json() == {"is_compatible": False} + assert res.json().get("is_compatible") is False # Do compatibility check when message field is altered from referenced type to google type @@ -339,7 +339,7 @@ async def test_protobuf_schema_compatibility_dependencies1g(registry_async_clien json={"schemaType": "PROTOBUF", "schema": evolved_schema}, ) assert res.status_code == 200 - assert res.json() == {"is_compatible": False} + assert res.json().get("is_compatible") is False # Do compatibility check when message field is altered from google type to referenced type @@ -407,7 +407,7 @@ async def test_protobuf_schema_compatibility_dependencies1g_otherway(registry_as json={"schemaType": "PROTOBUF", "schema": evolved_schema, "references": container_references}, ) assert res.status_code == 200 - assert res.json() == {"is_compatible": False} + assert res.json().get("is_compatible") is False @pytest.mark.parametrize("trail", ["", "/"]) @@ -491,7 +491,7 @@ async def test_protobuf_schema_compatibility_dependencies2(registry_async_client json={"schemaType": "PROTOBUF", "schema": evolved_schema, "references": evolved_references}, ) assert res.status_code == 200 - assert res.json() == {"is_compatible": False} + assert res.json().get("is_compatible") is False SIMPLE_SCHEMA = """\ diff --git a/tests/integration/test_schema.py b/tests/integration/test_schema.py index 546c19e0b..62fc71833 100644 --- a/tests/integration/test_schema.py +++ b/tests/integration/test_schema.py @@ -332,7 +332,8 @@ async def test_compatibility_endpoint(registry_async_client: Client, trail: str) json={"schema": json.dumps(schema)}, ) assert res.status_code == 200 - assert res.json() == {"is_compatible": False} + assert not res.json().get("is_compatible") + assert res.json().get("incompatibilities") == "reader type: string not compatible with writer type: int" @pytest.mark.parametrize("trail", ["", "/"]) @@ -536,7 +537,7 @@ def _test_cases(): json={"schema": json.dumps(schema)}, ) assert res.status_code == 200 - assert res.json() == {"is_compatible": expected} + assert res.json().get("is_compatible") == expected @pytest.mark.parametrize("trail", ["", "/"]) @@ -3253,7 +3254,10 @@ async def test_schema_non_compliant_name_in_existing( assert res.status_code == 409 assert res.json() == { "error_code": 409, - "message": "Incompatible schema, compatibility_mode=BACKWARD expected: compliant_name_test.test-schema", + "message": ( + "Incompatible schema, compatibility_mode=BACKWARD. " + "Incompatibilities: expected: compliant_name_test.test-schema" + ), } # Send compatibility configuration for subject that disabled backwards compatibility. diff --git a/tests/integration/test_schema_protobuf.py b/tests/integration/test_schema_protobuf.py index 4b4471cb2..55825fb92 100644 --- a/tests/integration/test_schema_protobuf.py +++ b/tests/integration/test_schema_protobuf.py @@ -1123,8 +1123,8 @@ async def test_protobuf_error(registry_async_client: Client) -> None: expected=409, expected_msg=( # ACTUALLY THERE NO MESSAGE_DROP!!! - "Incompatible schema, compatibility_mode=BACKWARD " - "Incompatible modification Modification.MESSAGE_DROP found" + "Incompatible schema, compatibility_mode=BACKWARD. " + "Incompatibilities: Incompatible modification Modification.MESSAGE_DROP found" ), ) print(f"Adding new schema, subject: '{testdata.subject}'\n{testdata.schema_str}")