All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.restassured.module.jsv.JsonSchemaValidator Maven / Gradle / Ivy

There is a newer version: 5.5.0
Show newest version
/*
 * Copyright 2019 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.restassured.module.jsv;

import com.fasterxml.jackson.databind.JsonNode;
import com.github.fge.jackson.JsonLoader;
import com.github.fge.jsonschema.core.report.ProcessingMessage;
import com.github.fge.jsonschema.core.report.ProcessingReport;
import com.github.fge.jsonschema.main.JsonSchema;
import com.github.fge.jsonschema.main.JsonSchemaFactory;
import com.google.common.collect.Lists;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.TypeSafeMatcher;

import java.io.*;
import java.net.MalformedURLException;
import java.net.URI;
import java.net.URL;
import java.util.List;

/**
 * A Hamcrest matcher that can be used to validate that a JSON document matches a given JSON schema.
 * Typical use-case in REST Assured:
 * 
 * get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json"));
 * 
*

* The {@link #matchesJsonSchemaInClasspath(String)} is defined in this class and validates that the response body of the request to "/products" * matches the products-schema.json schema located in classpath. It's also possible to supply some settings, for example the {@link com.github.fge.jsonschema.main.JsonSchemaFactory} * that the matcher will use when validating the schema: *

 * JsonSchemaFactory jsonSchemaFactory = JsonSchemaFactory.newBuilder().setValidationConfiguration(ValidationConfiguration.newBuilder().setDefaultVersion(DRAFTV4).freeze()).freeze();
 * get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json").using(jsonSchemaFactory));
 * 
* or: *
 * get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json").using(settings().with().checkedValidation(false)))
 * 
* where "settings" is found in {@link JsonSchemaValidatorSettings#settings()}. *

* It's also possible to specify static configuration that is reused for all matcher invocations. For example if you never wish to use checked validation you can configure * that {@link JsonSchemaValidator} like this: *

*

 * JsonSchemaValidator.settings = settings().with().checkedValidation(false);
 * 
* This means that *
 * get("/products").then().assertThat().body(matchesJsonSchemaInClasspath("products-schema.json"));
 * 
* will use unchecked validation (since it was configured statically). *

* To reset the {@link JsonSchemaValidator} to its default state you can call {@link #reset()}: *

*
 * JsonSchemaValidator.reset();
 * 
*

*/ public class JsonSchemaValidator extends TypeSafeMatcher { /** * Default json schema factory instance */ public static JsonSchemaValidatorSettings settings; private final Object schema; private final JsonSchemaValidatorSettings instanceSettings; private ProcessingReport report; private JsonSchemaValidator(Object schema, JsonSchemaValidatorSettings jsonSchemaValidatorSettings) { if (jsonSchemaValidatorSettings == null) { throw new IllegalArgumentException(JsonSchemaValidatorSettings.class.getSimpleName() + " cannot be null."); } this.schema = schema; this.instanceSettings = jsonSchemaValidatorSettings; } /** * Creates a Hamcrest matcher that validates that a JSON document conforms to the JSON schema provided to this method. * * @param schema The string defining the JSON schema * @return A Hamcrest matcher */ public static JsonSchemaValidator matchesJsonSchema(String schema) { return new JsonSchemaValidatorFactory() { @Override JsonNode createSchemaInstance(String input) throws IOException { return JsonLoader.fromString(input); } }.create(schema); } /** * Creates a Hamcrest matcher that validates that a JSON document conforms to the JSON schema provided to this method. * * @param pathToSchemaInClasspath The string that points to a JSON schema in classpath. * @return A Hamcrest matcher */ public static JsonSchemaValidator matchesJsonSchemaInClasspath(String pathToSchemaInClasspath) { return matchesJsonSchema(Thread.currentThread().getContextClassLoader().getResource(pathToSchemaInClasspath)); } /** * Creates a Hamcrest matcher that validates that a JSON document conforms to the JSON schema provided to this method. * * @param schema The input stream that points to a JSON schema * @return A Hamcrest matcher */ public static JsonSchemaValidator matchesJsonSchema(InputStream schema) { return matchesJsonSchema(new InputStreamReader(schema)); } /** * Creates a Hamcrest matcher that validates that a JSON document conforms to the JSON schema provided to this method. * * @param schema The reader that points to a JSON schema * @return A Hamcrest matcher */ public static JsonSchemaValidator matchesJsonSchema(Reader schema) { return new JsonSchemaValidatorFactory() { @Override JsonNode createSchemaInstance(Reader input) throws IOException { return JsonLoader.fromReader(input); } }.create(schema); } /** * Creates a Hamcrest matcher that validates that a JSON document conforms to the JSON schema provided to this method. * * @param file The file that points to a JSON schema * @return A Hamcrest matcher */ public static JsonSchemaValidator matchesJsonSchema(File file) { return new JsonSchemaValidatorFactory() { @Override JsonNode createSchemaInstance(File input) throws IOException { return JsonLoader.fromFile(input); } }.create(file); } public static JsonSchemaValidator matchesJsonSchema(URL url) { return new JsonSchemaValidatorFactory() { @Override Object createSchemaInstance(URL input) throws IOException { return input; } }.create(url); } /** * Creates a Hamcrest matcher that validates that a JSON document conforms to the JSON schema loaded by the supplied URI. *

* Note: Converts the URI to a URL and loads this URL. *

* * @param uri The URI that points to a JSON schema * @return A Hamcrest matcher */ public static JsonSchemaValidator matchesJsonSchema(URI uri) { return matchesJsonSchema(toURL(uri)); } /** * Validate the JSON document using the supplied jsonSchemaFactory instance. * * @param jsonSchemaFactory The json schema factory instance to use. * @return A Hamcrest matcher */ public Matcher using(JsonSchemaFactory jsonSchemaFactory) { return new JsonSchemaValidator(schema, instanceSettings.jsonSchemaFactory(jsonSchemaFactory)); } /** * Validate the JSON document using the supplied jsonSchemaValidatorSettings instance. * * @param jsonSchemaValidatorSettings The json schema validator settings instance to use. * @return A Hamcrest matcher */ public Matcher using(JsonSchemaValidatorSettings jsonSchemaValidatorSettings) { return new JsonSchemaValidator(schema, jsonSchemaValidatorSettings); } private static URL toURL(URI uri) { validateSchemaIsNotNull(uri); try { return uri.toURL(); } catch (MalformedURLException e) { throw new IllegalArgumentException("Can't convert the supplied URI to a URL", e); } } @Override protected boolean matchesSafely(String content) { try { JsonNode contentAsJsonNode = JsonLoader.fromString(content); JsonSchemaFactory jsonSchemaFactory = instanceSettings.jsonSchemaFactory(); Schema loadedSchema = loadSchema(schema, instanceSettings); final JsonSchema jsonSchema; if (loadedSchema.hasType(JsonNode.class)) { jsonSchema = jsonSchemaFactory.getJsonSchema(JsonNode.class.cast(loadedSchema.schema)); } else if (loadedSchema.hasType(String.class)) { jsonSchema = jsonSchemaFactory.getJsonSchema(String.class.cast(loadedSchema.schema)); } else { throw new RuntimeException("Internal error when loading schema from factory. Type was " + loadedSchema.schema.getClass().getName()); } if (instanceSettings.shouldUseCheckedValidation()) { report = jsonSchema.validate(contentAsJsonNode); } else { report = jsonSchema.validateUnchecked(contentAsJsonNode); } return report.isSuccess(); } catch (Exception e) { throw new JsonSchemaValidationException(e); } } public void describeTo(Description description) { if (report != null) { description.appendText("The content to match the given JSON schema.\n"); List messages = Lists.newArrayList(report); if (!messages.isEmpty()) { for (final ProcessingMessage message : messages) description.appendText(message.toString()); } } } private Schema loadSchema(Object input, JsonSchemaValidatorSettings instanceSettings) { if (input instanceof JsonNode) { return new Schema(input); } else if (input instanceof URL) { final Object loadedSchema; if (instanceSettings.shouldParseUriAndUrlsAsJsonNode()) { try { loadedSchema = JsonLoader.fromURL((URL) input); } catch (IOException e) { throw new RuntimeException(e); } } else { loadedSchema = input.toString(); } return new Schema(loadedSchema); } else { throw new RuntimeException("Internal error when loading schema: Input was instance of " + input.getClass().getName()); } } private static void validateSchemaIsNotNull(Object schema) { if (schema == null) { throw new IllegalArgumentException("Schema to use cannot be null"); } } private static abstract class JsonSchemaValidatorFactory { private JsonSchemaValidatorSettings createSettings() { return settings == null ? new JsonSchemaValidatorSettings() : settings; } public JsonSchemaValidator create(T schema) { validateSchemaIsNotNull(schema); Object loadedSchema; try { loadedSchema = createSchemaInstance(schema); } catch (IOException e) { throw new JsonSchemaValidationException(e); } return new JsonSchemaValidator(loadedSchema, createSettings()); } abstract Object createSchemaInstance(T input) throws IOException; } private static class Schema { private final Object schema; private Schema(Object result) { this.schema = result; } public boolean hasType(Class type) { return type.isAssignableFrom(schema.getClass()); } public T as(Class type) { return type.cast(schema); } } /** * Reset the static {@link #settings} to null. */ public static void reset() { settings = null; } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy