From e273ae11ab4fbfa95239b6ae5ff7490548d8b88c Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Wed, 12 Nov 2025 12:23:52 +0100 Subject: [PATCH 1/2] Update to 3.1.2 --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 9c39b6a..bad582e 100644 --- a/pom.xml +++ b/pom.xml @@ -75,7 +75,7 @@ 3.5.0 3.2.1 3.11.0 - 3.0.7 + 3.1.2 1.5.1 20240303 From efe179ed205ccdc143ad574791d169cc9d5db0bd Mon Sep 17 00:00:00 2001 From: Carles Arnal Date: Thu, 20 Nov 2025 07:34:39 +0100 Subject: [PATCH 2/2] Improve error messages --- .../validation/json/JsonValidationResult.java | 4 +- .../schema/validation/json/JsonValidator.java | 60 +++++++++++++++++-- .../protobuf/ProtobufValidator.java | 58 ++++++++++++++++-- 3 files changed, 109 insertions(+), 13 deletions(-) diff --git a/jsonschema/src/main/java/io/apicurio/schema/validation/json/JsonValidationResult.java b/jsonschema/src/main/java/io/apicurio/schema/validation/json/JsonValidationResult.java index c660d71..8a4bd28 100644 --- a/jsonschema/src/main/java/io/apicurio/schema/validation/json/JsonValidationResult.java +++ b/jsonschema/src/main/java/io/apicurio/schema/validation/json/JsonValidationResult.java @@ -23,7 +23,7 @@ */ public class JsonValidationResult { - protected static final JsonValidationResult SUCCESS = successfull(); + protected static final JsonValidationResult SUCCESS = successful(); private boolean success; private List validationErrors; @@ -54,7 +54,7 @@ public static JsonValidationResult fromErrors(List errors) { return new JsonValidationResult(errors); } - public static JsonValidationResult successfull() { + public static JsonValidationResult successful() { return new JsonValidationResult(null); } diff --git a/jsonschema/src/main/java/io/apicurio/schema/validation/json/JsonValidator.java b/jsonschema/src/main/java/io/apicurio/schema/validation/json/JsonValidator.java index fe1031d..b2147d3 100644 --- a/jsonschema/src/main/java/io/apicurio/schema/validation/json/JsonValidator.java +++ b/jsonschema/src/main/java/io/apicurio/schema/validation/json/JsonValidator.java @@ -31,6 +31,7 @@ import io.apicurio.registry.resolver.config.SchemaResolverConfig; import io.apicurio.registry.resolver.data.Record; import io.apicurio.registry.resolver.strategy.ArtifactReference; +import io.apicurio.registry.rest.client.models.ProblemDetails; import io.apicurio.registry.types.ArtifactType; import io.apicurio.registry.utils.IoUtil; import org.json.JSONObject; @@ -86,9 +87,15 @@ protected JsonValidator() { */ public JsonValidationResult validateByArtifactReference(Object bean) { Objects.requireNonNull(this.artifactReference, "ArtifactReference must be provided when creating JsonValidator in order to use this feature"); - SchemaLookupResult schema = this.schemaResolver.resolveSchemaByArtifactReference(this.artifactReference); - JsonNode jsonPayload = createJSONObject(bean); - return validate(schema.getParsedSchema().getParsedSchema(), jsonPayload); + try { + SchemaLookupResult schema = this.schemaResolver.resolveSchemaByArtifactReference(this.artifactReference); + JsonNode jsonPayload = createJSONObject(bean); + return validate(schema.getParsedSchema().getParsedSchema(), jsonPayload); + } catch (Exception e) { + return JsonValidationResult.fromErrors(List.of( + new ValidationError("Failed to resolve schema from registry: " + extractErrorMessage(e), "SCHEMA_RESOLUTION_ERROR") + )); + } } /** @@ -101,9 +108,15 @@ public JsonValidationResult validateByArtifactReference(Object bean) { * @return JsonValidationResult */ public JsonValidationResult validate(Record record) { - SchemaLookupResult schema = this.schemaResolver.resolveSchema(record); - JsonNode jsonPayload = createJSONObject(record.payload()); - return validate(schema.getParsedSchema().getParsedSchema(), jsonPayload); + try { + SchemaLookupResult schema = this.schemaResolver.resolveSchema(record); + JsonNode jsonPayload = createJSONObject(record.payload()); + return validate(schema.getParsedSchema().getParsedSchema(), jsonPayload); + } catch (Exception e) { + return JsonValidationResult.fromErrors(List.of( + new ValidationError("Failed to resolve schema from registry: " + extractErrorMessage(e), "SCHEMA_RESOLUTION_ERROR") + )); + } } protected JsonValidationResult validate(JsonSchema schema, JsonNode jsonPayload) { @@ -142,6 +155,41 @@ private List extractValidationErrors(Set val return errors; } + private String extractErrorMessage(Exception e) { + StringBuilder errorMessage = new StringBuilder(); + + // Start with the exception type and message + errorMessage.append(e.getClass().getSimpleName()); + String message = getDetailedMessage(e); + if (message != null && !message.isEmpty()) { + errorMessage.append(": ").append(message); + } + + // Add cause chain for more context + Throwable cause = e.getCause(); + while (cause != null) { + errorMessage.append(" | Caused by: ").append(cause.getClass().getSimpleName()); + String causeMessage = getDetailedMessage(cause); + if (causeMessage != null && !causeMessage.isEmpty()) { + errorMessage.append(": ").append(causeMessage); + } + cause = cause.getCause(); + } + + return errorMessage.toString(); + } + + private String getDetailedMessage(Throwable throwable) { + // Special handling for ProblemDetails from Apicurio Registry REST client + if (throwable instanceof ProblemDetails) { + String detail = ((ProblemDetails) throwable).getDetail(); + if (detail != null && !detail.isEmpty()) { + return detail; + } + } + return throwable.getMessage(); + } + public static class JsonSchemaParser implements SchemaParser { @Override public String artifactType() { diff --git a/protobuf/src/main/java/io/apicurio/schema/validation/protobuf/ProtobufValidator.java b/protobuf/src/main/java/io/apicurio/schema/validation/protobuf/ProtobufValidator.java index 7ef43c0..efe2192 100644 --- a/protobuf/src/main/java/io/apicurio/schema/validation/protobuf/ProtobufValidator.java +++ b/protobuf/src/main/java/io/apicurio/schema/validation/protobuf/ProtobufValidator.java @@ -6,6 +6,7 @@ import io.apicurio.registry.resolver.config.SchemaResolverConfig; import io.apicurio.registry.resolver.data.Record; import io.apicurio.registry.resolver.strategy.ArtifactReference; +import io.apicurio.registry.rest.client.models.ProblemDetails; import io.apicurio.registry.rules.compatibility.protobuf.ProtobufCompatibilityCheckerLibrary; import io.apicurio.registry.utils.protobuf.schema.ProtobufFile; import io.apicurio.registry.utils.protobuf.schema.ProtobufSchema; @@ -54,9 +55,15 @@ protected ProtobufValidator() { public ProtobufValidationResult validateByArtifactReference(Message bean) { Objects.requireNonNull(this.artifactReference, "ArtifactReference must be provided when creating JsonValidator in order to use this feature"); - SchemaLookupResult schema = this.schemaResolver.resolveSchemaByArtifactReference( - this.artifactReference); - return validate(schema.getParsedSchema(), new ProtobufRecord(bean, null)); + try { + SchemaLookupResult schema = this.schemaResolver.resolveSchemaByArtifactReference( + this.artifactReference); + return validate(schema.getParsedSchema(), new ProtobufRecord(bean, null)); + } catch (Exception e) { + return ProtobufValidationResult.fromErrors(List.of( + new ValidationError("Failed to resolve schema from registry: " + extractErrorMessage(e), "SCHEMA_RESOLUTION_ERROR") + )); + } } /** @@ -69,8 +76,14 @@ public ProtobufValidationResult validateByArtifactReference(Message bean) { * @return ProtobufValidationResult */ public ProtobufValidationResult validate(Record record) { - SchemaLookupResult schema = this.schemaResolver.resolveSchema(record); - return validate(schema.getParsedSchema(), record); + try { + SchemaLookupResult schema = this.schemaResolver.resolveSchema(record); + return validate(schema.getParsedSchema(), record); + } catch (Exception e) { + return ProtobufValidationResult.fromErrors(List.of( + new ValidationError("Failed to resolve schema from registry: " + extractErrorMessage(e), "SCHEMA_RESOLUTION_ERROR") + )); + } } protected ProtobufValidationResult validate(ParsedSchema schema, Record record) { @@ -100,4 +113,39 @@ private List validate(ParsedSchema schemaFro fileAfter); return checker.findDifferences(); } + + private String extractErrorMessage(Exception e) { + StringBuilder errorMessage = new StringBuilder(); + + // Start with the exception type and message + errorMessage.append(e.getClass().getSimpleName()); + String message = getDetailedMessage(e); + if (message != null && !message.isEmpty()) { + errorMessage.append(": ").append(message); + } + + // Add cause chain for more context + Throwable cause = e.getCause(); + while (cause != null) { + errorMessage.append(" | Caused by: ").append(cause.getClass().getSimpleName()); + String causeMessage = getDetailedMessage(cause); + if (causeMessage != null && !causeMessage.isEmpty()) { + errorMessage.append(": ").append(causeMessage); + } + cause = cause.getCause(); + } + + return errorMessage.toString(); + } + + private String getDetailedMessage(Throwable throwable) { + // Special handling for ProblemDetails from Apicurio Registry REST client + if (throwable instanceof ProblemDetails) { + String detail = ((ProblemDetails) throwable).getDetail(); + if (detail != null && !detail.isEmpty()) { + return detail; + } + } + return throwable.getMessage(); + } } \ No newline at end of file