All Downloads are FREE. Search and download functionalities are using the official Maven repository.
Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
io.vertx.ext.web.api.contract.openapi3.impl.OpenApi3Utils Maven / Gradle / Ivy
package io.vertx.ext.web.api.contract.openapi3.impl;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import com.networknt.schema.JsonSchema;
import com.networknt.schema.JsonSchemaFactory;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.media.ComposedSchema;
import io.swagger.v3.oas.models.media.MediaType;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.parser.ObjectMapperFactory;
import io.swagger.v3.parser.core.models.ParseOptions;
import io.vertx.core.json.Json;
import io.vertx.core.json.JsonArray;
import io.vertx.core.json.JsonObject;
import io.vertx.ext.web.api.validation.SpecFeatureNotSupportedException;
import java.util.*;
import java.util.function.Predicate;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* @author Francesco Guardiani @slinkydeveloper
*/
public class OpenApi3Utils {
public static ParseOptions getParseOptions() {
ParseOptions options = new ParseOptions();
options.setResolve(true);
options.setResolveCombinators(false);
options.setResolveFully(true);
return options;
}
public static boolean isParameterArrayType(Parameter parameter) {
if (parameter.getSchema() != null && parameter.getSchema().getType() != null)
return parameter.getSchema().getType().equals("array");
else return false;
}
public static boolean isParameterObjectOrAllOfType(Parameter parameter) {
return isSchemaObjectOrAllOfType(parameter.getSchema());
}
public static boolean isSchemaObjectOrAllOfType(Schema schema) {
return isSchemaObject(schema) || isAllOfSchema(schema);
}
public static boolean isSchemaObject(Schema schema) {
return schema != null && ("object".equals(schema.getType()) || schema.getProperties() != null);
}
public static boolean isRequiredParam(Schema schema, String parameterName) {
return schema != null && schema.getRequired() != null && schema.getRequired().contains(parameterName);
}
public static boolean isRequiredParam(Parameter param) {
return (param != null) ? param.getRequired() : false;
}
public static String resolveStyle(Parameter param) {
if (param.getStyle() != null) return param.getStyle().toString();
else switch (param.getIn()) {
case "query":
return "form";
case "path":
return "simple";
case "header":
return "simple";
case "cookie":
return "form";
default:
return null;
}
}
public static boolean isOneOfSchema(Schema schema) {
if (!(schema instanceof ComposedSchema)) return false;
ComposedSchema composedSchema = (ComposedSchema) schema;
return (composedSchema.getOneOf() != null && composedSchema.getOneOf().size() != 0);
}
public static boolean isAnyOfSchema(Schema schema) {
if (!(schema instanceof ComposedSchema)) return false;
ComposedSchema composedSchema = (ComposedSchema) schema;
return (composedSchema.getAnyOf() != null && composedSchema.getAnyOf().size() != 0);
}
public static boolean isAllOfSchema(Schema schema) {
if (!(schema instanceof ComposedSchema)) return false;
ComposedSchema composedSchema = (ComposedSchema) schema;
return (composedSchema.getAllOf() != null && composedSchema.getAllOf().size() != 0);
}
public static boolean resolveAllowEmptyValue(Parameter parameter) {
if (parameter.getAllowEmptyValue() != null) {
// As OAS says: This is valid only for query parameters and allows sending a parameter with an empty value. Default value is false. If style is used, and if behavior is n/a (cannot be serialized), the value of allowEmptyValue SHALL be ignored
if (!"form".equals(resolveStyle(parameter)))
return false;
else
return parameter.getAllowEmptyValue();
} else return false;
}
// Thank you StackOverflow :) https://stackoverflow
// .com/questions/28332924/case-insensitive-matching-of-a-string-to-a-java-enum :)
public static > T searchEnum(Class enumeration, String search) {
for (T each : enumeration.getEnumConstants()) {
if (each.name().compareToIgnoreCase(search) == 0) {
return each;
}
}
return null;
}
public static String resolveContentTypeRegex(String listContentTypes) {
// Check if it's list
if (listContentTypes.contains(",")) {
StringBuilder stringBuilder = new StringBuilder();
String[] contentTypes = listContentTypes.split(",");
for (String contentType : contentTypes)
stringBuilder.append(Pattern.quote(contentType.trim()) + "|");
stringBuilder.deleteCharAt(stringBuilder.length() - 1);
return stringBuilder.toString();
} else return Pattern.quote(listContentTypes);
}
public static List mergeParameters(List operationParameters, List parentParameters) {
if (parentParameters == null && operationParameters == null) {
return new ArrayList<>();
} else if (operationParameters == null) {
return new ArrayList<>(parentParameters);
} else if (parentParameters == null) {
return new ArrayList<>(operationParameters);
} else {
List result = new ArrayList<>(operationParameters);
List actualParams = new ArrayList<>(operationParameters);
for (int i = 0; i < parentParameters.size(); i++) {
for (int j = 0; j < actualParams.size(); j++) {
Parameter parentParam = parentParameters.get(i);
Parameter actualParam = actualParams.get(j);
if (!(parentParam.getIn().equalsIgnoreCase(actualParam.getIn()) && parentParam.getName().equals(actualParam
.getName())))
result.add(parentParam);
}
}
return result;
}
}
protected static class ObjectField {
Schema schema;
boolean required;
public ObjectField(Schema schema, String name, Schema superSchema) {
this.schema = schema;
this.required = superSchema.getRequired() != null && superSchema.getRequired().contains(name);
}
public Schema getSchema() {
return schema;
}
public boolean isRequired() {
return required;
}
}
/* This function resolve all properties inside an allOf array of schemas */
public static Map resolveAllOfArrays(List allOfSchemas) {
Map properties = new HashMap<>();
for (Schema schema : allOfSchemas) {
if (schema.getType() != null && !schema.getType().equals("object"))
throw new SpecFeatureNotSupportedException("allOf only allows inner object types");
for (Map.Entry entry : ((Map) schema.getProperties()).entrySet()) {
properties.put(entry.getKey(), new OpenApi3Utils.ObjectField(entry.getValue(), entry.getKey(), schema));
}
}
return properties;
}
/* This function check if schema is an allOf array or an object and returns a map of properties */
public static Map solveObjectParameters(Schema schema) {
if (OpenApi3Utils.isSchemaObjectOrAllOfType(schema)) {
if (OpenApi3Utils.isAllOfSchema(schema)) {
// allOf case
ComposedSchema composedSchema = (ComposedSchema) schema;
return resolveAllOfArrays(new ArrayList<>(composedSchema.getAllOf()));
} else {
// type object case
Map properties = new HashMap<>();
for (Map.Entry entry : ((Map) schema.getProperties()).entrySet()) {
properties.put(entry.getKey(), new OpenApi3Utils.ObjectField(entry.getValue(), entry.getKey(), schema));
}
return properties;
}
} else return null;
}
private final static Pattern COMPONENTS_REFS_MATCHER = Pattern.compile("^\\#\\/components\\/schemas\\/(.+)$");
private final static String COMPONENTS_REFS_SUBSTITUTION = "\\#\\/definitions\\/$1";
public static JsonNode generateSanitizedJsonSchemaNode(Schema s, OpenAPI oas) {
ObjectNode node = ObjectMapperFactory.createJson().convertValue(s, ObjectNode.class);
walkAndSolve(node, node, oas);
return node;
}
private static void walkAndSolve(ObjectNode n, ObjectNode root, OpenAPI oas) {
if (n.has("$ref")) {
replaceRef(n, root, oas);
} else if (n.has("allOf")) {
Iterator it = n.get("allOf").iterator();
while (it.hasNext()) {
// We assert that parser validated allOf as array of objects
walkAndSolve((ObjectNode) it.next(), root, oas);
}
} else if (n.has("anyOf")) {
Iterator it = n.get("anyOf").iterator();
while (it.hasNext()) {
walkAndSolve((ObjectNode) it.next(), root, oas);
}
} else if (n.has("oneOf")) {
Iterator it = n.get("oneOf").iterator();
while (it.hasNext()) {
walkAndSolve((ObjectNode) it.next(), root, oas);
}
} else if (n.has("properties")) {
ObjectNode properties = (ObjectNode) n.get("properties");
Iterator it = properties.fieldNames();
while (it.hasNext()) {
walkAndSolve((ObjectNode) properties.get(it.next()), root, oas);
}
} else if (n.has("items")) {
walkAndSolve((ObjectNode) n.get("items"), root, oas);
}
}
private static void replaceRef(ObjectNode n, ObjectNode root, OpenAPI oas) {
/**
* If a ref is found, the structure of the schema is circular. The oas parser don't solve circular refs.
* So I bundle the schema:
* 1. I update the ref field with a #/definitions/schema_name uri
* 2. If #/definitions/schema_name is empty, I solve it
*/
String oldRef = n.get("$ref").asText();
Matcher m = COMPONENTS_REFS_MATCHER.matcher(oldRef);
if (m.lookingAt()) {
String schemaName = m.group(1);
String newRef = m.replaceAll(COMPONENTS_REFS_SUBSTITUTION);
n.remove("$ref");
n.put("$ref", newRef);
if (!root.has("definitions") || !root.get("definitions").has(schemaName)) {
Schema s = oas.getComponents().getSchemas().get(schemaName);
ObjectNode schema = ObjectMapperFactory.createJson().convertValue(s, ObjectNode.class);
// We need to search inside for other refs
if (!root.has("definitions")) {
ObjectNode definitions = JsonNodeFactory.instance.objectNode();
definitions.set(schemaName, schema);
root.putObject("definitions");
} else {
((ObjectNode)root.get("definitions")).set(schemaName, schema);
}
walkAndSolve(schema, root, oas);
}
} else throw new RuntimeException("Wrong ref! " + oldRef);
}
public static List extractTypesFromMediaTypesMap(Map types, Predicate matchingFunction) {
return types
.entrySet().stream()
.filter(e -> matchingFunction.test(e.getKey()))
.map(Map.Entry::getValue).collect(Collectors.toList());
}
}