Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,14 @@ public void init() {
throw new ConfigurationException("validator/@skipFaults only makes sense with validator/@wsdl.");
}

/**
* Selects and constructs the appropriate MessageValidator based on configured attributes
* (wsdl, schema, jsonSchema, schematron) or a SOAP proxy's WSDL.
*
* @return the constructed MessageValidator for the selected validation source
* @throws RuntimeException if no validator configuration or SOAP proxy WSDL is available
* @throws Exception if an error occurs while constructing the chosen validator
*/
private MessageValidator getMessageValidator() throws Exception {
if (wsdl != null) {
return new WSDLValidator(resourceResolver, combine(getBaseLocation(), wsdl), serviceName, createFailureHandler(), skipFaults);
Expand Down Expand Up @@ -207,28 +215,39 @@ public String getJsonSchema() {
}

/**
* @description The JSON Schema (URL or file) to validate against.
* @example examples/validation/json-schema/schema2000.json
* Specifies the JSON or YAML Schema location used for validation.
*
* @param jsonSchema the schema location — a URL or file system path pointing to a JSON or YAML schema
*/
@MCAttribute
public void setJsonSchema(String jsonSchema) {
this.jsonSchema = jsonSchema;
}

/**
* The schema version identifier used for JSON/YAML schema validation.
*
* @return the configured schema version string (for example, "2020-12")
*/
public String getSchemaVersion() {
return schemaVersion;
}

/**
* @description The version of the Schema.
* @example 04, 05, 06, 07, 2019-09, 2020-12
* @default 2020-12
* Set the JSON/YAML Schema version identifier used when validating JSON/YAML schemas.
*
* @param version the schema version identifier (examples: "04", "05", "06", "07", "2019-09", "2020-12"); defaults to "2020-12"
*/
@MCAttribute
public void setSchemaVersion(String version) {
this.schemaVersion = version;
}

/**
* Returns the configured Schematron schema location used for validation.
*
* @return the Schematron location as a string, or `null` if not configured
*/
public String getSchematron() {
return schematron;
}
Expand Down Expand Up @@ -279,6 +298,14 @@ public String getShortDescription() {
return validator.getInvalid() + " of " + (validator.getValid() + validator.getInvalid()) + " messages have been invalid.";
}

/**
* Constructs a descriptive string that lists which validation artifacts were used and provides links to them.
*
* The description starts from the short summary and, when configured, appends references and clickable links
* for the WSDL, XML Schema, JSON Schema, and Schematron resources used for validation.
*
* @return a human-readable description of validation outcomes including links to configured validation resources
*/
@Override
public String getLongDescription() {
StringBuilder sb = new StringBuilder(getShortDescription());
Expand Down Expand Up @@ -319,4 +346,4 @@ private FailureHandler createFailureHandler() {
throw new IllegalArgumentException("Unknown failureHandler type: " + failureHandler);
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,28 @@

public class JSONSchemaVersionParser {

/**
* Resolve a JSON Schema version alias to its corresponding SpecVersion.VersionFlag.
*
* @param version a version alias such as "04", "draft-04", "07", "draft-2019-09" or "2020-12"
* @return the SpecVersion.VersionFlag that corresponds to the given alias
* @throws com.predic8.membrane.core.util.ConfigurationException if the alias is not a recognized JSON Schema version
*/
public static SpecVersion.VersionFlag parse(String version) {
return SpecVersion.VersionFlag.fromId(aliasToSpecId(version)).get();
}

/**
* Resolve a JSON Schema version alias to the internal SchemaId string.
*
* <p>Recognized aliases include short numeric forms (e.g. "04", "06", "07"),
* draft-prefixed forms (e.g. "draft-04", "draft-07") and calendar-year forms
* (e.g. "2019-09", "2020-12" or "draft-2019-09", "draft-2020-12").</p>
*
* @param alias the user-facing version alias to resolve (e.g. "draft-07", "2019-09")
* @return the corresponding internal SchemaId string (one of V4, V6, V7, V201909, V202012)
* @throws ConfigurationException if the alias is not one of the recognized versions
*/
static String aliasToSpecId(String alias) {
return switch (alias) {
case "04","draft-04" -> V4;
Expand All @@ -23,4 +41,4 @@ static String aliasToSpecId(String alias) {
default -> throw new ConfigurationException("Unknown JSON Schema version: " + alias);
};
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -59,22 +59,50 @@ public class JSONYAMLSchemaValidator extends AbstractMessageValidator {
*/
JsonSchema schema;

/**
* Construct a JSONYAMLSchemaValidator configured with a resolver, the JSON Schema source, a failure handler, and the schema version.
*
* @param resolver resolver used to load referenced schemas and resources
* @param jsonSchema the schema location or JSON/YAML schema content to validate against
* @param failureHandler handler used to build problem-detail responses on validation failures
* @param schemaVersion JSON Schema version identifier (e.g., "2020-12"); parsed into the validator's internal schemaId
*/
public JSONYAMLSchemaValidator(Resolver resolver, String jsonSchema, FailureHandler failureHandler, String schemaVersion) {
this.resolver = resolver;
this.jsonSchema = jsonSchema;
this.failureHandler = failureHandler;
this.schemaId = JSONSchemaVersionParser.parse( schemaVersion);
}

/**
* Creates a JSONYAMLSchemaValidator using the default JSON Schema version "2020-12".
*
* @param resolver resolver used to load external schemas or resources referenced by the JSON Schema
* @param jsonSchema schema location or the schema content to validate messages against
* @param failureHandler handler used to build and set problem-detail responses for validation failures
*/
public JSONYAMLSchemaValidator(Resolver resolver, String jsonSchema, FailureHandler failureHandler) {
this(resolver, jsonSchema, failureHandler, "2020-12");
}

/**
* Provides the validator's display name.
*
* @return the name "JSON Schema Validator"
*/
@Override
public String getName() {
return "JSON Schema Validator";
}

/**
* Initializes the JSON Schema validation infrastructure for this validator.
*
* <p>Configures a thread-safe JsonSchemaFactory (using the configured schema version and the
* instance's Resolver), builds the SchemaValidatorsConfig, loads the JsonSchema from the
* configured schema location, and initializes its validators so the schema instance is ready
* for concurrent validation calls.</p>
*/
@Override
public void init() {
super.init();
Expand All @@ -97,10 +125,27 @@ public void init() {

}

/**
* Validates the message body of the given exchange against the configured JSON Schema using UTF-8 encoding.
*
* @param exc the exchange containing the message to validate
* @param flow the flow (REQUEST/RESPONSE) indicating which message direction to validate
* @return an Outcome indicating whether processing should continue (`CONTINUE`) or be aborted (`ABORT`)
*/
public Outcome validateMessage(Exchange exc, Flow flow) throws Exception {
return validateMessage(exc, flow, UTF_8);
}

/**
* Validates the message body for the given flow against the configured JSON Schema and sets a problem-details
* response on the exchange when validation fails.
*
* @param exc the exchange containing the message to validate
* @param flow the flow (e.g. REQUEST or RESPONSE) whose message is validated
* @param ignored unused charset parameter (kept for API compatibility)
* @return `CONTINUE` if the message conforms to the schema, `ABORT` if validation failed and a
* problem-details response was set on the exchange
*/
public Outcome validateMessage(Exchange exc, Flow flow, Charset ignored) throws Exception {

Set<ValidationMessage> assertions = schema.validate(exc.getMessage(flow).getBodyAsStringDecoded(), JSON);
Expand All @@ -124,10 +169,31 @@ public Outcome validateMessage(Exchange exc, Flow flow, Charset ignored) throws
return ABORT;
}

/**
* Convert a set of schema validation messages into a list of problem-detail maps.
*
* @param assertions the validation messages produced by JSON Schema validation
* @return a list of maps where each map contains problem-detail fields derived from a validation message
*/
private @NotNull List<Map<String, Object>> getMapForProblemDetails(Set<ValidationMessage> assertions) {
return assertions.stream().map(this::validationMessageToProblemDetailsMap).toList();
}

/**
* Converts a ValidationMessage into an ordered map of problem-detail fields suitable for inclusion
* in a Problem Details response.
*
* @param vm the ValidationMessage to convert
* @return a LinkedHashMap with the following keys:
* "message" (human-readable message),
* "code" (validation code),
* "key" (message key),
* "details" (optional additional details),
* "type" (message type),
* "error" (error identifier),
* "pointer" (RFC 6901 JSON Pointer to the failing location),
* "node" (the JSON node instance related to the message)
*/
private @NotNull Map<String, Object> validationMessageToProblemDetailsMap(ValidationMessage vm) {
Map<String, Object> m = new LinkedHashMap<>();
m.put("message", vm.getMessage());
Expand All @@ -142,6 +208,12 @@ public Outcome validateMessage(Exchange exc, Flow flow, Charset ignored) throws
return m;
}

/**
* Builds an RFC 6901 JSON Pointer string for the given evaluation path.
*
* @param evaluationPath the JSON node path to convert; may be null or empty
* @return the JSON Pointer string (empty string if the path is null or has no components)
*/
private String getPointer(JsonNodePath evaluationPath) {
if (evaluationPath == null || evaluationPath.getNameCount() == 0) {
return "";
Expand All @@ -160,16 +232,31 @@ private String getPointer(JsonNodePath evaluationPath) {
return sb.toString();
}

/**
* Provides the count of successfully validated messages.
*
* @return the number of messages that passed validation
*/
@Override
public long getValid() {
return valid.get();
}

/**
* Current count of messages that failed JSON Schema validation.
*
* @return the number of messages that failed validation
*/
@Override
public long getInvalid() {
return invalid.get();
}

/**
* Provides the human-readable title used for JSON validation error responses.
*
* @return the error title "JSON validation failed"
*/
@Override
public String getErrorTitle() {
return "JSON validation failed";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,21 @@ public class MembraneSchemaLoader implements SchemaLoader {

Resolver resolver;

/**
* Create a MembraneSchemaLoader that uses the given Resolver to locate schema resources.
*
* @param resolver the Resolver used to resolve schema IRIs to input streams containing schema content
*/
public MembraneSchemaLoader(Resolver resolver) {
this.resolver = resolver;
}

/**
* Create an InputStreamSource for the schema identified by the provided absolute IRI.
*
* @param absoluteIri the absolute IRI of the JSON schema to load
* @return an InputStreamSource that provides an InputStream for the schema content resolved from the given IRI
*/
@Override
public InputStreamSource getSchema(AbsoluteIri absoluteIri) {

Expand All @@ -37,4 +48,4 @@ public InputStreamSource getSchema(AbsoluteIri absoluteIri) {


}
}
}