Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ProviderValidators to Schema Builder for customize the behavior of CustomValidator dynamically #366

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
78 changes: 78 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,84 @@ Schema schema = schemaLoader.load().build(); // the schema is created using the
schema.validate(jsonDocument); // the document validation happens here
```

### Custom Provider Validators
The library use `DefaultProviderValidators` that collects all the validators used in the process of validation of json.
To use a custom Provider of Validators basically you:

* create your own ProviderValidators in a class implementing the `org.everit.json.schema.loader.ProviderValidators` interface
* bind your ProviderValidators in a `org.everit.json.schema.loader.SchemaLoader.SchemaLoaderBuilder.builder(IstanceOfNewProviderValidators` instance before loading the actual schema

#### Example for validate a field with a function javascript

Assume a schema with a simple function of validation that return true if the subject is equals to number 5:
```java
....
"age": {
...
"format": "javascript: subject == 5"
},
...
```

Let's create a custom ProviderValidators that catch the intent of launch a javascript function and istantiate a new custom validator
```java
class ExampleDefaultProviderValidators extends DefaultProviderValidators {
@Override
public FormatValidator getFormatValidator(String formatName) {

if (!this.getFormatValidators().containsKey(formatName)
&& formatName.startsWith("javascript:")) {
String script = formatName.substring(formatName.lastIndexOf("javascript:"),formatName.length());
this.addFormatValidator(formatName, new JavascriptFormatValidator(formatName, script));
}
return super.getFormatValidator(formatName);
}
}

class JavascriptFormatValidator implements FormatValidator {

String script;
String formatName;

public JavascriptFormatValidator(String formatName, String script) {
this.formatName = formatName;
this.script = script;
}

@Override
public Optional<String> validate(String subject) {

ScriptEngine javaScriptEngine = new ScriptEngineManager().getEngineByName("js");
javaScriptEngine.put("subject",subject);
try {
Boolean result = (Boolean) javaScriptEngine.eval(script);
if (!result) {
return Optional.of(String.format("the length of string [%s] is not equal 5", subject));
}
} catch (ScriptException e) {
e.printStackTrace();
return Optional.of(String.format("Error on evalutation of [%s] ", subject));
}
return Optional.empty();
}

@Override
public String formatName() {
return formatName;
}
}
```

In the builder we can set this new ProviderValidators and launch the validation process.
```java
ExampleDefaultProviderValidators customProviderValidators = new ExampleDefaultProviderValidators();
SchemaLoader schemaLoader = SchemaLoader.builder(customProviderValidators)
.......
.build();
Schema schema = schemaLoader.load().build(); // the schema is created using the above created configuration
schema.validate(jsonDocument); // the document validation happens here
```


## $ref resolution

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import java.util.Map;

import org.everit.json.schema.FormatValidator;
import org.everit.json.schema.loader.internal.DefaultProviderValidators;
import org.everit.json.schema.loader.internal.DefaultSchemaClient;
import org.everit.json.schema.regexp.JavaUtilRegexpFactory;
import org.everit.json.schema.regexp.RegexpFactory;
Expand All @@ -21,12 +22,13 @@
class LoaderConfig {

static LoaderConfig defaultV4Config() {
return new LoaderConfig(new DefaultSchemaClient(), DRAFT_4.defaultFormatValidators(), DRAFT_4, false);
return new LoaderConfig(new DefaultSchemaClient(), new DefaultProviderValidators(DRAFT_4.defaultFormatValidators()), DRAFT_4, false);
}

final SchemaClient schemaClient;

final Map<String, FormatValidator> formatValidators;
//final Map<String, FormatValidator> formatValidators;
final ProviderValidators providerValidators;

final Map<URI, Object> schemasByURI;

Expand All @@ -38,17 +40,17 @@ static LoaderConfig defaultV4Config() {

final RegexpFactory regexpFactory;

LoaderConfig(SchemaClient schemaClient, Map<String, FormatValidator> formatValidators,
LoaderConfig(SchemaClient schemaClient, ProviderValidators providerValidators,
SpecificationVersion specVersion, boolean useDefaults) {
this(schemaClient, formatValidators, emptyMap(), specVersion, useDefaults, false, new JavaUtilRegexpFactory());
this(schemaClient, providerValidators, emptyMap(), specVersion, useDefaults, false, new JavaUtilRegexpFactory());
}

LoaderConfig(SchemaClient schemaClient, Map<String, FormatValidator> formatValidators,
LoaderConfig(SchemaClient schemaClient, ProviderValidators providerValidators,
Map<URI, Object> schemasByURI,
SpecificationVersion specVersion, boolean useDefaults, boolean nullableSupport,
RegexpFactory regexpFactory) {
this.schemaClient = requireNonNull(schemaClient, "schemaClient cannot be null");
this.formatValidators = requireNonNull(formatValidators, "formatValidators cannot be null");
this.providerValidators = requireNonNull(providerValidators, "providerValidators cannot be null");
if (schemasByURI == null) {
this.schemasByURI = new HashMap<>();
} else {
Expand All @@ -71,7 +73,7 @@ SchemaLoader.SchemaLoaderBuilder initLoader() {
.useDefaults(this.useDefaults)
.regexpFactory(this.regexpFactory)
.nullableSupport(this.nullableSupport)
.formatValidators(new HashMap<>(this.formatValidators));
.formatValidators(new HashMap<>(this.providerValidators.getFormatValidators()));
loaderBuilder.schemasByURI = schemasByURI;
if (DRAFT_6.equals(specVersion)) {
loaderBuilder.draftV6Support();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package org.everit.json.schema.loader;

import org.everit.json.schema.FormatValidator;
import java.util.Map;

/**
* Interface for the provider of validators used in Schema
*/
public interface ProviderValidators {

/**
* Return a FormatValidator with a name
*
* @param formatName Name of the FormatValidator
* @return
*/
FormatValidator getFormatValidator(String formatName);

/**
* Add a FormatValidator with a name
*
* @param formatName Name of the FormatValidator
* @param formatValidator The FormatValidator to add
*/
void addFormatValidator(String formatName, FormatValidator formatValidator);

/**
* Add a FormatValidator with a name if is or is not present
*
* @param formatName Name of the FormatValidator
* @param formatValidator The FormatValidator to add
* @param addIfAbsent If true, the FormatValidator can add on map
*/
void addFormatValidator(String formatName, FormatValidator formatValidator, boolean addIfAbsent);

/**
* Add a map of FormatValidator to the base map
*
* @param formatValidators All the FormatValidator to add on a base map
*/
void addAllFormatValidators(Map<String, FormatValidator> formatValidators);

/**
* Initialize the map of FormatValidator
*
* @param formatValidators the map of FormatValidator
*/
void initAllFormatValidators(Map<String, FormatValidator> formatValidators);

/**
* Return all the validators
*
* @return The map of all Validators
*/
Map<String, FormatValidator> getFormatValidators();
}
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ NumberSchema.Builder buildNumberSchema() {

StringSchema.Builder buildStringSchema() {
PropertySnifferSchemaExtractor.STRING_SCHEMA_PROPS.forEach(consumedKeys::keyConsumed);
return new StringSchemaLoader(schemaJson.ls, config().formatValidators).load();
return new StringSchemaLoader(schemaJson.ls, config().providerValidators).load();
}

abstract List<Schema.Builder<?>> extract();
Expand Down
30 changes: 21 additions & 9 deletions core/src/main/java/org/everit/json/schema/loader/SchemaLoader.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
import org.everit.json.schema.SchemaException;
import org.everit.json.schema.SchemaLocation;
import org.everit.json.schema.TrueSchema;
import org.everit.json.schema.loader.internal.DefaultProviderValidators;
import org.everit.json.schema.loader.internal.DefaultSchemaClient;
import org.everit.json.schema.loader.internal.WrappingFormatValidator;
import org.everit.json.schema.regexp.JavaUtilRegexpFactory;
Expand Down Expand Up @@ -61,7 +62,8 @@ public static class SchemaLoaderBuilder {

SchemaLocation pointerToCurrentObj = SchemaLocation.empty();

Map<String, FormatValidator> formatValidators = new HashMap<>();
//Map<String, FormatValidator> formatValidators = new HashMap<>();
ProviderValidators providerValidators = new DefaultProviderValidators();

SpecificationVersion specVersion;

Expand All @@ -81,6 +83,11 @@ public SchemaLoaderBuilder() {
setSpecVersion(DRAFT_4);
}

public SchemaLoaderBuilder(final ProviderValidators providerValidators) {
setSpecVersion(DRAFT_4);
this.providerValidators = providerValidators;
}

/**
* Registers a format validator with the name returned by {@link FormatValidator#formatName()}.
*
Expand All @@ -89,7 +96,7 @@ public SchemaLoaderBuilder() {
* @return {@code this}
*/
public SchemaLoaderBuilder addFormatValidator(FormatValidator formatValidator) {
formatValidators.put(formatValidator.formatName(), formatValidator);
providerValidators.addFormatValidator(formatValidator.formatName(), formatValidator);
return this;
}

Expand All @@ -106,9 +113,9 @@ public SchemaLoaderBuilder addFormatValidator(FormatValidator formatValidator) {
public SchemaLoaderBuilder addFormatValidator(String formatName,
final FormatValidator formatValidator) {
if (!Objects.equals(formatName, formatValidator.formatName())) {
formatValidators.put(formatName, new WrappingFormatValidator(formatName, formatValidator));
providerValidators.addFormatValidator(formatName, new WrappingFormatValidator(formatName, formatValidator));
} else {
formatValidators.put(formatName, formatValidator);
providerValidators.addFormatValidator(formatName, formatValidator);
}
return this;
}
Expand Down Expand Up @@ -154,10 +161,10 @@ private void addBuiltInFormatValidators() {

if (enableOverrideOfBuiltInFormatValidators) {
for (Entry<String, FormatValidator> entry : defaultFormatValidators.entrySet()) {
formatValidators.putIfAbsent(entry.getKey(), entry.getValue());
providerValidators.addFormatValidator(entry.getKey(), entry.getValue(), true);
}
} else {
formatValidators.putAll(defaultFormatValidators);
providerValidators.addAllFormatValidators(defaultFormatValidators);
}
}
@Deprecated
Expand Down Expand Up @@ -229,7 +236,7 @@ public SchemaLoaderBuilder schemaJson(Object schema) {
}

SchemaLoaderBuilder formatValidators(Map<String, FormatValidator> formatValidators) {
this.formatValidators = formatValidators;
this.providerValidators.initAllFormatValidators(formatValidators);
return this;
}

Expand Down Expand Up @@ -279,6 +286,10 @@ public static SchemaLoaderBuilder builder() {
return new SchemaLoaderBuilder();
}

public static SchemaLoaderBuilder builder(ProviderValidators providerValidators) {
return new SchemaLoaderBuilder(providerValidators);
}

/**
* Loads a JSON schema to a schema validator using a {@link DefaultSchemaClient default HTTP
* client}.
Expand Down Expand Up @@ -342,7 +353,8 @@ public SchemaLoader(SchemaLoaderBuilder builder) {
specVersion = builder.specVersion;
}
this.config = new LoaderConfig(builder.schemaClient,
builder.formatValidators,
//builder.formatValidators,
builder.providerValidators,
builder.schemasByURI,
specVersion,
builder.useDefaults,
Expand Down Expand Up @@ -477,7 +489,7 @@ SpecificationVersion specVersion() {
*/
@Deprecated
Optional<FormatValidator> getFormatValidator(String formatName) {
return Optional.ofNullable(config.formatValidators.get(formatName));
return Optional.ofNullable(config.providerValidators.getFormatValidator(formatName));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

import org.everit.json.schema.FormatValidator;
import org.everit.json.schema.StringSchema;
import org.everit.json.schema.loader.internal.DefaultProviderValidators;

/**
* @author erosb
Expand All @@ -16,21 +17,22 @@ public class StringSchemaLoader {

private LoadingState ls;

private Map<String, FormatValidator> formatValidators;
//private Map<String, FormatValidator> formatValidators;
private ProviderValidators providerValidators;

/**
* Creates an instance with {@link SpecificationVersion#defaultFormatValidators()} draft v4 format validators}.
*
* @deprecated explicitly specify the format validators with {@link #StringSchemaLoader(LoadingState, Map)} instead
* @deprecated explicitly specify the format validators with {@link #StringSchemaLoader(LoadingState, ProviderValidators)} instead
*/
@Deprecated
public StringSchemaLoader(LoadingState ls) {
this(ls, DRAFT_4.defaultFormatValidators());
this(ls, new DefaultProviderValidators(DRAFT_4.defaultFormatValidators()));
}

StringSchemaLoader(LoadingState ls, Map<String, FormatValidator> formatValidators) {
StringSchemaLoader(LoadingState ls, ProviderValidators providerValidators) {
this.ls = requireNonNull(ls, "ls cannot be null");
this.formatValidators = unmodifiableMap(requireNonNull(formatValidators, "formatValidators cannot be null"));
this.providerValidators = requireNonNull(providerValidators, "providerValidators cannot be null");
}

public StringSchema.Builder load() {
Expand All @@ -46,7 +48,7 @@ public StringSchema.Builder load() {
}

private void addFormatValidator(StringSchema.Builder builder, String formatName) {
FormatValidator formatValidator = formatValidators.get(formatName);
FormatValidator formatValidator = providerValidators.getFormatValidator(formatName);
if (formatValidator != null) {
builder.formatValidator(formatValidator);
}
Expand Down