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.
com.zuunr.openapi.OpenApiMerger Maven / Gradle / Ivy
/*
* Copyright 2020 Zuunr AB
*
* 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 com.zuunr.openapi;
import com.zuunr.forms.Form;
import com.zuunr.forms.FormFields;
import com.zuunr.forms.util.JsonValuePathsFinder;
import com.zuunr.json.*;
import com.zuunr.jsonschema.JsonSchemaMerger;
import java.util.Iterator;
/**
* @author Niklas Eldberger
*/
public class OpenApiMerger {
private final JsonSchemaMerger jsonSchemaMerger = JsonSchemaMerger.OPENAPI_3_0_STYLE;
private final JsonValuePathsFinder pathsFinder = new JsonValuePathsFinder();
private final OpenApiSchemaConverter openApiSchemaConverter = new OpenApiSchemaConverter();
private final OpenApiUtil openApiUtil = new OpenApiUtil();
private final OpenApiUnionMaker unionMaker = new OpenApiUnionMaker();
public JsonObject unionOf(JsonObject openApiDoc1, JsonObject openApiDoc2) {
return unionMaker.unionOf(openApiDoc1, openApiDoc2);
}
private JsonArray openApiParameters(JsonValue parametersJsonSchema, JsonArray parameters) {
JsonArrayBuilder builder = JsonArray.EMPTY.builder();
for (JsonValue parameterJsonValue : parameters) {
JsonObject parameter = parameterJsonValue.getValue(JsonObject.class);
String in = parameter.get("in").getString();
String parameterName = parameter.get("name").getString();
JsonValue parameterTypeSchema = parametersJsonSchema.get("properties").get(in);
if (!parameterTypeSchema.is(Boolean.class) &&
!parameterTypeSchema.get(JsonArray.of("properties", parameterName), JsonValue.FALSE).is(Boolean.class)) {
boolean required = parameterTypeSchema.get("required", JsonArray.EMPTY).getValue(JsonArray.class).contains(parameterName);
builder.add(parameter
.put("schema", parameterTypeSchema.get("properties").get(parameterName))
.put("required", required));
}
}
return builder.build();
}
public JsonObject getRequestBodySchemaPaths(JsonObject jsonSchemaDoc) {
JsonArray pathsPattern = JsonArray.EMPTY
.add("paths")
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
.add("requestBody")
.add("content")
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
.add("schema");
return getPathsAndValuesPerPath(pathsPattern, jsonSchemaDoc);
}
public JsonObject getPathsAndValuesPerPath(JsonArray pathsPattern, JsonObject jsonSchemaDoc) {
JsonObjectBuilder schemaPerPath = JsonObject.EMPTY.builder();
JsonArray paths = pathsFinder.getPaths(jsonSchemaDoc.jsonValue(), pathsPattern, true);
for (JsonValue path : paths) {
schemaPerPath.put(path.getValue(JsonArray.class).allButLast().asJson(), path.getValue(JsonArray.class));
}
return schemaPerPath.build();
}
public JsonObject getResponseBodySchemaPaths(JsonObject jsonSchemaDoc) {
JsonObjectBuilder schemaPerPath = JsonObject.EMPTY.builder();
JsonArray pathsPattern = JsonArray.EMPTY
.add("paths")
// path template
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
// method
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
.add("responses")
// status code
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
.add("content")
// content type
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
.add("schema");
JsonArray paths = pathsFinder.getPaths(jsonSchemaDoc.jsonValue(), pathsPattern, true);
for (JsonValue path : paths) {
schemaPerPath.put(path.getValue(JsonArray.class).allButLast().asJson(), path.getValue(JsonArray.class));
}
return schemaPerPath.build();
}
public JsonArray pathAndMethodPlusValue(JsonObject jsonSchemaDoc) {
JsonArray pathsPattern = JsonArray.EMPTY
.add("paths")
// path template
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
// method
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()));
JsonArray paths = pathsFinder.getPaths(jsonSchemaDoc.jsonValue(), pathsPattern, true);
return paths;
}
public JsonObject pathAndMethodAndValuePerPathAndMethod(JsonObject jsonSchemaDoc) {
JsonObject result = JsonObject.EMPTY;
for (JsonValue jsonValue : pathAndMethodPlusValue(jsonSchemaDoc)) {
result = result.put(jsonValue.getValue(JsonArray.class).subArray(0, 3).asJson(), jsonValue);
}
return result;
}
public JsonObject intersectionOf(JsonObject jsonSchemaDoc1, JsonObject jsonSchemaDoc2) {
JsonObject result = jsonSchemaDoc1.put("paths", JsonObject.EMPTY); // defaults to empty
JsonObject pathsObject1 = pathAndMethodAndValuePerPathAndMethod(jsonSchemaDoc1);
JsonObject pathsObject2 = pathAndMethodAndValuePerPathAndMethod(jsonSchemaDoc2);
Iterator valuesIter = pathsObject1.values().iterator();
for (String path : pathsObject1.keys().asList(String.class)) {
JsonArray pathAndValue2 = pathsObject2.get(path, JsonValue.NULL).getJsonArray();
if (pathAndValue2 == null) {
continue;
}
//JsonArray pathAndValue1old = valuesIter.next().getValue(JsonArray.class); // TODO: Check why order of iterator was wrong!
JsonArray pathAndValue1 = pathsObject1.get(path).getJsonArray();
JsonObject resultOfUpdate = updatePath(pathAndValue1, pathAndValue2, result);
if (resultOfUpdate != null) {
result = resultOfUpdate;
}
}
return result;
}
private JsonObject updatePath(JsonArray pathAndValue1, JsonArray pathAndValue2, JsonObject resultSoFar) {
String method = pathAndValue1.get(2).getString();
JsonObject originalPerMethodSpec1 = pathAndValue1.last().getValue(JsonObject.class);
JsonObject originalPerMethodSpec2 = pathAndValue2.last().getValue(JsonObject.class);
JsonObject perMethodSpec = originalPerMethodSpec1
.remove("parameters")
.remove("requestBody")
.remove("responses");
//merge parameters
JsonArray parameters1 = originalPerMethodSpec1.get("parameters", JsonArray.EMPTY).getValue(JsonArray.class);
JsonArray parameters2 = originalPerMethodSpec2.get("parameters", JsonArray.EMPTY).getValue(JsonArray.class);
if (!parameters1.isEmpty() && !parameters2.isEmpty()) {
JsonObject parametersJsonSchema1 = openApiUtil.openApiParametersAsJsonObjectSchema(parameters1);
JsonObject parametersJsonSchema2 = openApiUtil.openApiParametersAsJsonObjectSchema(parameters2);
JsonValue mergedSchema = jsonSchemaMerger.intersectionOf(parametersJsonSchema1.jsonValue(), parametersJsonSchema2.jsonValue(), true);
if (mergedSchema.is(Boolean.class) && JsonValue.FALSE.equals(mergedSchema)) {
return null; // This method for this URL path is not possible to call
} else {
perMethodSpec = perMethodSpec.put("parameters", openApiParameters(mergedSchema, parameters1));
}
}
// Request body
if (mayHaveRequestBody(method)) {
JsonArray requestBodyPathsAndSchemas = requestBodies(originalPerMethodSpec1, pathAndValue2.last().getValue(JsonObject.class));
for (JsonValue pathAndSchemaJsonValue : requestBodyPathsAndSchemas) {
perMethodSpec = perMethodSpec.put(pathAndSchemaJsonValue.getValue(JsonArray.class).allButLast(), pathAndSchemaJsonValue.getValue(JsonArray.class).last());
}
if (perMethodSpec.get("requestBody") == null) {
return null; // This method for this URL path is not possible to call
}
}
// Responses
for (JsonValue pathAndSchemaJsonValue : responses(originalPerMethodSpec1, pathAndValue2.last().getValue(JsonObject.class))) {
JsonArray pathToDescriptionOfStatusCode = pathAndSchemaJsonValue.getValue(JsonArray.class).subArray(0, 2).add("description");
perMethodSpec = perMethodSpec
.put(pathAndSchemaJsonValue.getValue(JsonArray.class).allButLast(), pathAndSchemaJsonValue.getValue(JsonArray.class).last())
.put(pathToDescriptionOfStatusCode, originalPerMethodSpec1.get(pathToDescriptionOfStatusCode));
}
JsonObject defaultResponses = perMethodSpec.get(JsonArray.of("responses", "default"), JsonObject.EMPTY.put("description", "")).getValue(JsonObject.class);
perMethodSpec = perMethodSpec.put(JsonArray.of("responses", "default"), defaultResponses);
resultSoFar = resultSoFar.put(pathAndValue1.allButLast(), perMethodSpec);
return resultSoFar;
}
private boolean mayHaveRequestBody(String method) {
return method.equals("post") || method.equals("patch") || method.equals("put");
}
private JsonArray requestBodies(JsonObject perMethodSpec1, JsonObject perMethodSpec2) {
JsonArray responsesBodyPathsPattern = JsonArray.EMPTY
.add("requestBody")
.add("content")
// content type
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
.add("schema");
return methodSpecPathsAndSchemas(perMethodSpec1, perMethodSpec2, responsesBodyPathsPattern, true);
}
private JsonArray responses(JsonObject perMethodSpec1, JsonObject perMethodSpec2) {
JsonArray responsesBodyPathsPattern = JsonArray.EMPTY
.add("responses")
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
.add("content")
// content type
.add(JsonObject.EMPTY
.put("objectFormat", Form.EMPTY.builder()
.exclusive(false)
.value(FormFields.EMPTY)
.build()))
.add("schema");
return methodSpecPathsAndSchemas(perMethodSpec1, perMethodSpec2, responsesBodyPathsPattern, false);
}
private JsonArray methodSpecPathsAndSchemas(JsonObject perMethodSpec1, JsonObject perMethodSpec2, JsonArray perMethodPathPattern, boolean simplifyAlwaysFailingSchema) {
JsonArrayBuilder perMethodSpecPathAndValues = JsonArray.EMPTY.builder(); // ['responses', '200', 'content', 'application/json', 'schema', {jsonSchema}]
JsonArray responsesSchemaPaths1 = pathsFinder.getPaths(perMethodSpec1.jsonValue(), perMethodPathPattern, true);
JsonArray responsesSchemaPaths2 = pathsFinder.getPaths(perMethodSpec2.jsonValue(), perMethodPathPattern, true);
JsonObjectBuilder perResponseContentPathBuilder2 = JsonObject.EMPTY.builder();
for (JsonValue schemaPath2 : responsesSchemaPaths2) {
perResponseContentPathBuilder2.put(schemaPath2.getValue(JsonArray.class).allButLast().asJson(), schemaPath2);
}
JsonObject perResponseContentPath2 = perResponseContentPathBuilder2.build();
for (JsonValue schemaPath1 : responsesSchemaPaths1) {
JsonArray responsesContentPath = schemaPath1.getValue(JsonArray.class).allButLast();
JsonValue schemaPath2 = perResponseContentPath2.get(responsesContentPath.asJson());
if (schemaPath2 == null) {
continue;
}
JsonObject openApiSchema1 = schemaPath1.getValue(JsonArray.class).last().getValue(JsonObject.class);
JsonObject openApiSchema2 = schemaPath2.getValue(JsonArray.class).last().getValue(JsonObject.class);
JsonValue schema1 = openApiSchemaConverter.toJsonSchema(openApiSchema1);
JsonValue schema2 = openApiSchemaConverter.toJsonSchema(openApiSchema2);
JsonValue mergedJsonSchema = jsonSchemaMerger.intersectionOf(schema1, schema2, simplifyAlwaysFailingSchema);
if (!JsonValue.FALSE.equals(mergedJsonSchema)) {
perMethodSpecPathAndValues.add(responsesContentPath.add(mergedJsonSchema));
}
}
return perMethodSpecPathAndValues.build();
}
}