com.networknt.oas.validator.ValidatorBase Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of openapi-parser Show documentation
Show all versions of openapi-parser Show documentation
A light-weight, fast OpenAPI 3.0 parser and validator
/*******************************************************************************
* Copyright (c) 2017 ModelSolv, Inc. and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* ModelSolv, Inc. - initial API and implementation and/or initial documentation
*******************************************************************************/
package com.networknt.oas.validator;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.*;
import com.networknt.jsonoverlay.*;
import javax.mail.internet.AddressException;
import javax.mail.internet.InternetAddress;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
import java.util.*;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException;
import java.util.stream.Collectors;
import static com.networknt.oas.validator.BaseValidationMessages.*;
import static com.networknt.oas.validator.msg.Messages.msg;
public abstract class ValidatorBase implements Validator {
protected Overlay value;
protected ValidationResults results;
@Override
public final void validate(Overlay value) {
this.value = value;
this.results = ValidationContext.getValidationResults();
checkJsonType(value, getAllowedJsonTypes(value), results);
runValidations();
}
public abstract void runValidations();
public Overlay validateBooleanField(String name, boolean required) {
return validateField(name, required, Boolean.class, null);
}
public Overlay validateStringField(String name, boolean required) {
return validateStringField(name, required, (Pattern) null);
}
public Overlay validateStringField(String name, boolean required, String pattern) {
return validateStringField(name, required, Pattern.compile(pattern));
}
@SafeVarargs
public final Overlay validateStringField(String name, boolean required, Pattern pattern,
Consumer>... otherChecks) {
Overlay field = validateField(name, required, String.class, null);
checkMissing(field, required);
if (field != null && field.isPresent()) {
if (pattern != null) {
checkPattern(field, pattern);
}
for (Consumer> otherCheck : otherChecks) {
otherCheck.accept(field);
}
}
return field;
}
void checkPattern(Overlay field, Pattern pattern) {
if (!pattern.matcher(field.get()).matches()) {
results.addError(msg(PatternMatchFail, field.get(), pattern), field);
}
}
public Overlay validatePatternField(String name, boolean required) {
return validateStringField(name, required, null, this::checkRegex);
}
private void checkRegex(Overlay field) {
String regex = field.get();
try {
Pattern.compile(regex);
} catch (PatternSyntaxException e) {
results.addWarning(msg(BadPattern, regex), field);
}
}
public Overlay validateUrlField(String name, boolean required, boolean allowRelative, boolean allowVars) {
return validateUrlField(name, required, allowRelative, allowVars, (Pattern) null);
}
public Overlay validateUrlField(String name, boolean required, boolean allowRelative, boolean allowVars,
String pattern) {
return validateUrlField(name, required, allowRelative, allowVars, Pattern.compile(pattern));
}
public Overlay validateUrlField(String name, boolean required, boolean allowRelative, boolean allowVars,
Pattern pattern) {
return validateStringField(name, required, pattern, field -> checkUrl(field, allowRelative, allowVars));
}
private static String FAKE_SCHEME = "oasparser.fake.scheme";
private static URLStreamHandler fakeHandler = new URLStreamHandler() {
@Override
protected URLConnection openConnection(URL u) throws IOException {
return null;
}
};
private void checkUrl(Overlay overlay, boolean allowRelative, boolean allowVars) {
// TODO Q: Any help from spec in being able to validate URLs with vars? E.g is
// our treatment here valid? We assume vars can only appear where simple text
// can appear, so handling vars means relacing {.*} with "1" and testing for URL
// validity. We use a digit instead of a letter because it covers vars in the
// port, and elsewhere digits are always allowed where letters are.
String origUrl = overlay.get();
String url = origUrl;
boolean fake = false;
if (allowVars) {
url = url.replaceAll("\\{[^}]+\\}", "1");
if (url.startsWith("1:")) {
// "1" is not a valid scheme name, so we need to replace it with special scheme,
// for which we provide a do-nothing protocol handler implementation
url = FAKE_SCHEME + url.substring(1);
fake = true;
}
}
try {
new URL(null, url, fake ? fakeHandler : null);
} catch (MalformedURLException e) {
try {
URL context = new URL(null, FAKE_SCHEME + ":/", fakeHandler);
new URL(context, url);
if (!allowRelative) {
results.addError(msg(BaseValidationMessages.NoRelUrl, origUrl, e.toString()), overlay);
}
} catch (MalformedURLException e1) {
results.addError(msg(BadUrl, origUrl, e.toString()), overlay);
}
}
}
public Overlay validateEmailField(String name, boolean required) {
return validateEmailField(name, required, (Pattern) null);
}
public Overlay validateUrlField(String name, boolean required, String pattern) {
return validateEmailField(name, required, Pattern.compile(pattern));
}
public Overlay validateEmailField(String name, boolean required, Pattern pattern) {
return validateStringField(name, required, pattern, this::checkEmail);
}
private void checkEmail(Overlay overlay) {
String email = overlay.get();
try {
InternetAddress addr = new InternetAddress();
addr.setAddress(email);
addr.validate();
} catch (AddressException e) {
results.addError(msg(BadEmail, email, e.toString()), overlay);
}
}
public Overlay validatePositiveField(String name, boolean required) {
return validateNumericField(name, required, NumericUtils::isPositive, "be positive");
}
public Overlay validateNonNegativeField(String name, boolean required) {
return validateNumericField(name, required, NumericUtils::isNonNegative, "not be positive");
}
public Overlay validateNumericField(String name, boolean required, Function test,
String desc) {
Overlay field = validateField(name, required, Number.class, null);
checkMissing(field, required);
if (field != null && field.isPresent() && test != null) {
Number n = field.get();
if (!test.apply(n)) {
results.addError(msg(NumberConstraint, desc, n), field);
}
}
return field;
}
@SafeVarargs
public final Overlay validateField(String name, boolean required, Class fieldClass,
Validator validator, Consumer>... otherChecks) {
@SuppressWarnings("unchecked")
PropertiesOverlay propValue = (PropertiesOverlay) value.get();
Overlay field = Overlay.of(propValue, name, fieldClass);
checkJsonType(field, getAllowedJsonTypes(field), results);
checkMissing(field, required);
if (field != null && field.isPresent() && validator != null) {
validator.validate(field);
for (Consumer> otherCheck : otherChecks) {
otherCheck.accept(field);
}
}
return field;
}
public Overlay> validateListField(String name, boolean nonEmpty, boolean unique, Class itemClass,
Validator itemValidator) {
@SuppressWarnings("unchecked")
Overlay> list = (Overlay>) (Object) Overlay.of((PropertiesOverlay) value.get(), name,
List.class);
validateList(list, nonEmpty, unique, itemValidator);
return list;
}
private void validateList(Overlay> list, boolean nonEmpty, boolean unique, Validator itemValidator) {
new ListValidator(itemValidator).validate(list);
checkListNotEmpty(list, nonEmpty);
checkListUnique(list, unique);
}
private void checkListNotEmpty(Overlay> list, boolean nonEmpty) {
if (nonEmpty) {
ListOverlay listOverlay = Overlay.getListOverlay(list);
if (list != null && !list.isPresent()) {
if (nonEmpty && listOverlay.size() == 0) {
results.addError(msg(EmptyList), list);
}
}
}
}
private void checkListUnique(Overlay> list, boolean unique) {
if (unique) {
ListOverlay listOverlay = Overlay.getListOverlay(list);
Set seen = new HashSet<>();
for (int i = 0; i < listOverlay.size(); i++) {
X item = listOverlay.get(i);
if (seen.contains(item)) {
results.addError(msg(DuplicateValue, item, i), Overlay.of(listOverlay, i));
} else {
seen.add(item);
}
}
}
}
public Overlay
© 2015 - 2025 Weber Informatics LLC | Privacy Policy