Skip to content

Commit

Permalink
fix: better access into json validator for subclasses
Browse files Browse the repository at this point in the history
  • Loading branch information
bbortt committed Apr 26, 2024
1 parent e94d8f8 commit 4255b31
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 82 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -34,45 +34,35 @@
* <p>
* JSONArray as well as nested JSONObjects are supported, too.
* <p>
* Validator offers two different modes to operate. By default strict mode is set and the validator will also check the exact amount of
* Validator offers two different modes to operate. By default, strict mode is set and the validator will also check the exact amount of
* control object fields to match. No additional fields in received JSON data structure will be accepted. In soft mode validator
* allows additional fields in received JSON data structure so the control JSON object can be a partial subset.
*
* @author Christoph Deppisch
*/
public class JsonTextMessageValidator extends AbstractMessageValidator<JsonMessageValidationContext> {

/**
* Should also check exact amount of object fields
*/
private boolean strict = JsonSettings.isStrict();

/**
* Permissive mode to use on the Json parser
*/
private int permissiveMode = JsonSettings.getPermissiveMoe();

/**
* Schema validator
*/
private JsonSchemaValidation jsonSchemaValidation = new JsonSchemaValidation();

private JsonElementValidator.Provider elementValidatorProvider = JsonElementValidator.Provider.DEFAULT;

@Override
public void validateMessage(
Message receivedMessage,
Message controlMessage,
TestContext context,
JsonMessageValidationContext validationContext
) {
public void validateMessage(Message receivedMessage,
Message controlMessage,
TestContext context,
JsonMessageValidationContext validationContext) {
logger.debug("Start JSON message validation ...");

if (validationContext.isSchemaValidationEnabled()) {
jsonSchemaValidation.validate(receivedMessage, context, validationContext);
}

String receivedJsonText = receivedMessage.getPayload(String.class);
String controlJsonText = context.replaceDynamicContentInString(controlMessage.getPayload(String.class));
var receivedJsonText = receivedMessage.getPayload(String.class);
var controlJsonText = context.replaceDynamicContentInString(controlMessage.getPayload(String.class));

if (!hasText(controlJsonText)) {
logger.debug("Skip message payload validation as no control message was defined");
Expand All @@ -81,9 +71,9 @@ public void validateMessage(
throw new ValidationException("Validation failed - expected message contents, but received empty message!");
}

elementValidatorProvider.getValidator(strict, context, validationContext).validate(
parseJson(permissiveMode, receivedJsonText, controlJsonText)
);
elementValidatorProvider.getValidator(strict, context, validationContext)
.validate(parseJson(permissiveMode, receivedJsonText, controlJsonText));

logger.debug("JSON message validation successful: All values OK");
}

Expand All @@ -97,41 +87,19 @@ public boolean supportsMessageType(String messageType, Message message) {
return messageType.equalsIgnoreCase(JSON.name()) && hasJsonPayload(message);
}

/**
* Set the validator strict mode.
*
* @param strict
*/
public void setStrict(boolean strict) {
this.strict = strict;
}

/**
* Set the validator strict mode.
*
* @param strict
* @return this object for chaining
*/
public JsonTextMessageValidator strict(boolean strict) {
setStrict(strict);
return this;
}

/**
* Sets the json schema validation.
*
* @param jsonSchemaValidation
*/
void setJsonSchemaValidation(JsonSchemaValidation jsonSchemaValidation) {
public void setJsonSchemaValidation(JsonSchemaValidation jsonSchemaValidation) {
this.jsonSchemaValidation = jsonSchemaValidation;
}

/**
* Sets the json schema validation.
*
* @param jsonSchemaValidation
* @return this object for chaining
*/
public JsonTextMessageValidator jsonSchemaValidation(JsonSchemaValidation jsonSchemaValidation) {
setJsonSchemaValidation(jsonSchemaValidation);
return this;
Expand All @@ -146,21 +114,10 @@ public JsonTextMessageValidator elementValidatorProvider(JsonElementValidator.Pr
return this;
}

/**
* Sets the permissive mode.
*
* @param permissiveMode
*/
public void setPermissiveMode(int permissiveMode) {
this.permissiveMode = permissiveMode;
}

/**
* Sets the permissive mode
*
* @param permissiveMode
* @return this object for chaining
*/
public JsonTextMessageValidator permissiveMode(int permissiveMode) {
setPermissiveMode(permissiveMode);
return this;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@

package org.citrusframework.validation.json.schema;

import static java.util.Collections.emptySet;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.networknt.schema.ValidationMessage;
Expand All @@ -41,27 +43,21 @@

/**
* This class is responsible for the validation of json messages against json schemas / json schema repositories.
*
* @since 2.7.3
*/
public class JsonSchemaValidation implements SchemaValidator<JsonMessageValidationContext> {

private static final Logger logger = LoggerFactory.getLogger(JsonSchemaValidation.class);

private final JsonSchemaFilter jsonSchemaFilter;
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();

/** Object Mapper to convert the message for validation*/
private ObjectMapper objectMapper = new ObjectMapper();
private final JsonSchemaFilter jsonSchemaFilter;

/**
* Default constructor using default filter.
*/
public JsonSchemaValidation() {
this(new JsonSchemaFilter());
}

/**
* Constructor using filter implementation.
*/
public JsonSchemaValidation(JsonSchemaFilter jsonSchemaFilter) {
this.jsonSchemaFilter = jsonSchemaFilter;
}
Expand All @@ -71,9 +67,9 @@ public void validate(Message message, TestContext context, JsonMessageValidation
logger.debug("Starting Json schema validation ...");

GraciousProcessingReport report = validate(message,
findSchemaRepositories(context),
validationContext,
context.getReferenceResolver());
findSchemaRepositories(context),
validationContext,
context.getReferenceResolver());

if (!report.isSuccess()) {
logger.error("Failed to validate Json schema for message:\n{}", message.getPayload(String.class));
Expand All @@ -90,7 +86,7 @@ public void validate(Message message, TestContext context, JsonMessageValidation
* @param report The report containing the error message
* @return A string representation of all messages contained in the report
*/
private String constructErrorMessage(GraciousProcessingReport report) {
protected String constructErrorMessage(GraciousProcessingReport report) {
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.append("Json validation failed: ");
report.getValidationMessages().forEach(processingMessage -> stringBuilder.append("\n\t").append(processingMessage.getMessage()));
Expand All @@ -114,9 +110,9 @@ private List<JsonSchemaRepository> findSchemaRepositories(TestContext context) {
* @return A report holding the results of the validation
*/
public GraciousProcessingReport validate(Message message,
List<JsonSchemaRepository> schemaRepositories,
JsonMessageValidationContext validationContext,
ReferenceResolver referenceResolver) {
List<JsonSchemaRepository> schemaRepositories,
JsonMessageValidationContext validationContext,
ReferenceResolver referenceResolver) {
return validate(message, jsonSchemaFilter.filter(schemaRepositories, validationContext, referenceResolver));
}

Expand Down Expand Up @@ -147,15 +143,13 @@ private GraciousProcessingReport validate(Message message, List<SimpleJsonSchema
*/
private Set<ValidationMessage> validate(Message message, SimpleJsonSchema simpleJsonSchema) {
try {
JsonNode receivedJson = objectMapper.readTree(message.getPayload(String.class));
JsonNode receivedJson = OBJECT_MAPPER.readTree(message.getPayload(String.class));
if (receivedJson.isEmpty()) {
return Collections.emptySet();
return emptySet();
} else {
return simpleJsonSchema.getSchema().validate(
objectMapper.readTree(
message.getPayload(String.class)
)
);
OBJECT_MAPPER.readTree(
message.getPayload(String.class)));
}
} catch (IOException e) {
throw new CitrusRuntimeException("Failed to validate Json schema", e);
Expand All @@ -172,6 +166,7 @@ private Set<ValidationMessage> validate(Message message, SimpleJsonSchema simple
*/
@Override
public boolean supportsMessageType(String messageType, Message message) {
return "JSON".equals(messageType) || (message != null && IsJsonPredicate.getInstance().test(message.getPayload(String.class)));
return "JSON".equals(messageType)
|| (message != null && IsJsonPredicate.getInstance().test(message.getPayload(String.class)));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

package org.citrusframework.validation.json;

import net.minidev.json.parser.JSONParser;
import net.minidev.json.parser.ParseException;
import org.citrusframework.UnitTestSupport;
import org.citrusframework.exceptions.CitrusRuntimeException;
Expand All @@ -36,6 +35,8 @@
import java.util.List;
import java.util.Set;

import static net.minidev.json.parser.JSONParser.MODE_JSON_SIMPLE;
import static net.minidev.json.parser.JSONParser.MODE_RFC4627;
import static org.assertj.core.api.Assertions.*;
import static org.mockito.Mockito.*;

Expand Down Expand Up @@ -92,7 +93,7 @@ public void testUseSchemaRepositoryValidatorIfSchemaValidationIsEnabled() {

JsonSchemaValidation jsonSchemaValidation = mock(JsonSchemaValidation.class);
when(jsonSchemaValidation.validate(any(), anyList(), any(), any())).thenReturn(new GraciousProcessingReport((true)));
fixture.setJsonSchemaValidation(jsonSchemaValidation);
fixture.jsonSchemaValidation(jsonSchemaValidation);

JsonSchemaRepository jsonSchemaRepository = mock(JsonSchemaRepository.class);
context.getReferenceResolver().bind("jsonSchemaRepository", jsonSchemaRepository);
Expand Down Expand Up @@ -124,7 +125,7 @@ public void shouldFindProperValidationContext() {

@Test
public void testPermissiveModeSimple() {
fixture.setPermissiveMode(JSONParser.MODE_JSON_SIMPLE);
fixture.permissiveMode(MODE_JSON_SIMPLE);

var actualMessage = new DefaultMessage("{\"text\":\"Hello World!\",, \"index\":5, \"id\":\"x123456789x\",}");
var expectedMessage = new DefaultMessage("{\"text\":\"Hello World!\", \"index\":5, \"id\":\"x123456789x\"}");
Expand Down Expand Up @@ -152,7 +153,7 @@ public void shouldUseCustomElementValidator() {

@Test
public void testPermissiveModeStrict() {
fixture.setPermissiveMode(JSONParser.MODE_RFC4627);
fixture.permissiveMode(MODE_RFC4627);

var actualMessage = new DefaultMessage("{\"text\":\"Hello World!\",, \"index\":5, \"id\":\"x123456789x\",}");
var expectedMessage = new DefaultMessage("{\"text\":\"Hello World!\", \"index\":5, \"id\":\"x123456789x\"}");
Expand Down

0 comments on commit 4255b31

Please sign in to comment.