UniqueInterfaceGenerator.java
/*
* GovWay - A customizable API Gateway
* https://govway.org
*
* Copyright (c) 2005-2024 Link.it srl (https://link.it).
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3, as published by
* the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
*/
package org.openspcoop2.utils.openapi;
import java.io.File;
import java.io.FileOutputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.apache.logging.log4j.Level;
import org.openspcoop2.utils.LoggerWrapperFactory;
import org.openspcoop2.utils.json.JSONUtils;
import org.openspcoop2.utils.json.JsonPathExpressionEngine;
import org.openspcoop2.utils.json.YAMLUtils;
import org.openspcoop2.utils.resources.FileSystemUtilities;
import org.openspcoop2.utils.rest.ApiFormats;
import org.slf4j.Logger;
import com.fasterxml.jackson.databind.JsonNode;
import io.swagger.v3.oas.models.Components;
import io.swagger.v3.oas.models.OpenAPI;
import io.swagger.v3.oas.models.callbacks.Callback;
import io.swagger.v3.oas.models.examples.Example;
import io.swagger.v3.oas.models.headers.Header;
import io.swagger.v3.oas.models.links.Link;
import io.swagger.v3.oas.models.media.Schema;
import io.swagger.v3.oas.models.parameters.Parameter;
import io.swagger.v3.oas.models.parameters.RequestBody;
import io.swagger.v3.oas.models.responses.ApiResponse;
import io.swagger.v3.oas.models.security.SecurityScheme;
import io.swagger.v3.parser.OpenAPIV3Parser;
import io.swagger.v3.parser.converter.SwaggerConverter;
import io.swagger.v3.parser.core.models.ParseOptions;
import io.swagger.v3.parser.core.models.SwaggerParseResult;
/**
* UniqueInterfaceGenerator
*
* @author Andrea Poli (apoli@link.it)
* @author $Author$
* @version $Rev$, $Date$
*/
public class UniqueInterfaceGenerator {
public static void main(String[] args) throws Exception {
LoggerWrapperFactory.setDefaultConsoleLogConfiguration(Level.ERROR);
if(args==null || args.length<4) {
throw new Exception("Use: UniqueInterfaceGenerator <versioneOpenAPI> <destFile> <master> <attachmentsDir>");
}
String tipo = args[0].trim();
ApiFormats format = ApiFormats.valueOf(tipo);
String fileDest = args[1].trim();
UniqueInterfaceGeneratorConfig config = new UniqueInterfaceGeneratorConfig();
config.format = format;
String fileMaster = args[2].trim();
config.master = FileSystemUtilities.readFile(fileMaster);
File fMaster = new File(fileMaster);
String ext = null;
try{
ext = fileMaster.substring(fileMaster.lastIndexOf(".")+1,fileMaster.length());
}catch(Exception e){
// ext undefined
}
config.yaml = "yaml".equalsIgnoreCase(ext);
HashMap<String,String> attachments = new HashMap<>();
File fDir = new File(args[3].trim());
if(fDir.isDirectory()==false) {
throw new Exception("attachmentsDir ["+fDir.getAbsolutePath()+"] is not directory");
}
File[] files = fDir.listFiles();
if(files!=null) {
for (int j = 0; j < files.length; j++) {
if(files[j].getName().equals(fMaster.getName())) {
continue;
}
if(files[j].isDirectory()) {
continue;
}
//System.out.println("READ ["+files[j]+"] ... ");
attachments.put(files[j].getName(), FileSystemUtilities.readFile(files[j]));
//System.out.println("READ ["+files[j]+"] ok");
}
}
config.attachments = attachments;
List<String> blackListParameters = null;
List<String> blackListComponents = null;
if(args.length>5) {
String blackListParametersArgs = args[4].trim();
if(blackListParametersArgs!=null) {
blackListParameters = new ArrayList<>();
if(blackListParametersArgs.contains(",")) {
String [] tmp = blackListParametersArgs.split(",");
for (String s : tmp) {
blackListParameters.add(s);
}
}else {
blackListParameters.add(blackListParametersArgs);
}
}
String blackListComponentsArgs = args[5].trim();
if(blackListComponentsArgs!=null) {
blackListComponents = new ArrayList<>();
if(blackListComponentsArgs.contains(",")) {
String [] tmp = blackListComponentsArgs.split(",");
for (String s : tmp) {
blackListComponents.add(s);
}
}else {
blackListComponents.add(blackListComponentsArgs);
}
}
}
generate(fileDest, config, blackListParameters, blackListComponents, true, null);
}
private static void debug(boolean debug, Logger log, String msg) {
if(debug) {
if(log!=null) {
log.debug(msg);
}
else {
System.out.println(msg);
}
}
}
public static void generate(String fileDest, UniqueInterfaceGeneratorConfig config,
List<String> blackListParameters, List<String> blackListComponents,
boolean debug, Logger log) throws Exception {
String schemaRebuild = generate(config, blackListParameters, blackListComponents, debug, log);
try(FileOutputStream fout = new FileOutputStream(fileDest)){
fout.write(schemaRebuild.getBytes());
fout.flush();
}
}
public static String generate(UniqueInterfaceGeneratorConfig config,
List<String> blackListParameters, List<String> blackListComponents,
boolean debug, Logger log) throws Exception {
SwaggerParseResult pr = null;
ParseOptions parseOptions = new ParseOptions();
if(ApiFormats.SWAGGER_2.equals(config.format)) {
pr = new SwaggerConverter().readContents(config.master, null, parseOptions);
}
else {
pr = new OpenAPIV3Parser().readContents(config.master, null, parseOptions);
}
StringBuilder sbParseWarningResult = new StringBuilder();
OpenAPI api = AbstractOpenapiApiReader.parseResult(LoggerWrapperFactory.getLogger(UniqueInterfaceGenerator.class), pr, sbParseWarningResult);
if(api.getComponents()==null) {
api.setComponents(new Components());
}
Map<String,String> attachments = config.attachments;
Iterator<String> attachmentNames = attachments.keySet().iterator();
while (attachmentNames.hasNext()) {
String attachName = (String) attachmentNames.next();
String attach = attachments.get(attachName);
debug(debug,log,"Merge ["+attachName+"] ...");
if(ApiFormats.SWAGGER_2.equals(config.format)) {
pr = new SwaggerConverter().readContents(attach, null, parseOptions);
}
else {
pr = new OpenAPIV3Parser().readContents(attach, null, parseOptions);
}
OpenAPI apiInternal = AbstractOpenapiApiReader.parseResult(LoggerWrapperFactory.getLogger(UniqueInterfaceGenerator.class), pr, sbParseWarningResult);
if(apiInternal.getComponents()!=null) {
if(apiInternal.getComponents().getCallbacks()!=null) {
Map<String, Callback> maps = apiInternal.getComponents().getCallbacks();
int mapsSize = 0;
if(maps!=null && !maps.isEmpty()) {
mapsSize = maps.size();
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
Callback value = maps.get(key);
api.getComponents().addCallbacks(key, value);
}
}
debug(debug,log,"\t"+mapsSize+" callback");
}
if(apiInternal.getComponents().getExamples()!=null) {
Map<String, Example> maps = apiInternal.getComponents().getExamples();
int mapsSize = 0;
if(maps!=null && !maps.isEmpty()) {
mapsSize = maps.size();
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
Example value = maps.get(key);
api.getComponents().addExamples(key, value);
}
}
debug(debug,log,"\t"+mapsSize+" example");
}
if(apiInternal.getComponents().getExtensions()!=null) {
Map<String, Object> maps = apiInternal.getComponents().getExtensions();
int mapsSize = 0;
if(maps!=null && !maps.isEmpty()) {
mapsSize = maps.size();
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
Object value = maps.get(key);
api.getComponents().addExtension(key, value);
}
}
debug(debug,log,"\t"+mapsSize+" extensions");
}
if(apiInternal.getComponents().getHeaders()!=null) {
Map<String, Header> maps = apiInternal.getComponents().getHeaders();
int mapsSize = 0;
if(maps!=null && !maps.isEmpty()) {
mapsSize = maps.size();
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
Header value = maps.get(key);
api.getComponents().addHeaders(key, value);
}
}
debug(debug,log,"\t"+mapsSize+" header");
}
if(apiInternal.getComponents().getLinks()!=null) {
Map<String, Link> maps = apiInternal.getComponents().getLinks();
int mapsSize = 0;
if(maps!=null && !maps.isEmpty()) {
mapsSize = maps.size();
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
Link value = maps.get(key);
api.getComponents().addLinks(key, value);
}
}
debug(debug,log,"\t"+mapsSize+" link");
}
if(apiInternal.getComponents().getParameters()!=null) {
Map<String, Parameter> maps = apiInternal.getComponents().getParameters();
int mapsSize = 0;
if(maps!=null && !maps.isEmpty()) {
mapsSize = maps.size();
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
if(blackListParameters!=null && blackListParameters.contains(key)) {
debug(debug,log,"Parameter '"+key+"' skipped");
continue;
}
Parameter value = maps.get(key);
api.getComponents().addParameters(key, value);
}
}
debug(debug,log,"\t"+mapsSize+" parameter");
}
if(apiInternal.getComponents().getRequestBodies()!=null) {
Map<String, RequestBody> maps = apiInternal.getComponents().getRequestBodies();
int mapsSize = 0;
if(maps!=null && !maps.isEmpty()) {
mapsSize = maps.size();
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
RequestBody value = maps.get(key);
api.getComponents().addRequestBodies(key, value);
}
}
debug(debug,log,"\t"+mapsSize+" requestBody");
}
if(apiInternal.getComponents().getResponses()!=null) {
Map<String, ApiResponse> maps = apiInternal.getComponents().getResponses();
int mapsSize = 0;
if(maps!=null && !maps.isEmpty()) {
mapsSize = maps.size();
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
ApiResponse value = maps.get(key);
api.getComponents().addResponses(key, value);
}
}
debug(debug,log,"\t"+mapsSize+"] response");
}
if(apiInternal.getComponents().getSchemas()!=null) {
@SuppressWarnings("rawtypes")
Map<String, Schema> maps = apiInternal.getComponents().getSchemas();
int mapsSize = 0;
if(maps!=null && !maps.isEmpty()) {
mapsSize = maps.size();
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
if(blackListComponents!=null && blackListComponents.contains(key)) {
debug(debug,log,"Component '"+key+"' skipped");
continue;
}
Schema<?> value = maps.get(key);
api.getComponents().addSchemas(key, value);
}
}
debug(debug,log,"\t"+mapsSize+" schema");
}
if(apiInternal.getComponents().getSecuritySchemes()!=null) {
Map<String, SecurityScheme> maps = apiInternal.getComponents().getSecuritySchemes();
int mapsSize = 0;
if(maps!=null && !maps.isEmpty()) {
mapsSize = maps.size();
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
SecurityScheme value = maps.get(key);
api.getComponents().addSecuritySchemes(key, value);
}
}
debug(debug,log,"\t"+mapsSize+" security schema");
}
}
debug(debug,log,"Merge ["+attachName+"] ok");
}
// clean attributi non permessi in swagger editor
api.setExtensions(null);
api.getComponents().setExtensions(null);
if(api.getComponents().getHeaders()!=null) {
Map<String, Header> maps = api.getComponents().getHeaders();
if(maps!=null && !maps.isEmpty()) {
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
Header value = maps.get(key);
value.setExplode(null);
value.setStyle(null);
}
}
}
if(api.getComponents().getParameters()!=null) {
Map<String, Parameter> maps = api.getComponents().getParameters();
if(maps!=null && !maps.isEmpty()) {
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
Parameter value = maps.get(key);
value.setExplode(null);
value.setStyle(null);
//debug(debug,log,"PARAMETRO *"+key+"* ["+value.getName()+"] ["+value.getExample()+"] ["+value.getExamples()+"] ref["+value.get$ref()+"] tipo["+value.getClass().getName()+"]");
checkSchema(0,("Parameter-"+key), value.getSchema());
}
}
}
if(api.getComponents().getSchemas()!=null) {
@SuppressWarnings("rawtypes")
Map<String, Schema> maps = api.getComponents().getSchemas();
if(maps!=null && !maps.isEmpty()) {
Iterator<String> keys = maps.keySet().iterator();
while (keys.hasNext()) {
String key = (String) keys.next();
Schema<?> value = maps.get(key);
String sorgente = "";
if(value.getName()!=null) {
sorgente = sorgente + value.getName();
}
else {
sorgente = sorgente + "RootSchema";
}
checkSchema(0, sorgente, value);
}
}
}
JsonNode jsonNode = null;
String s = null;
if(config.yaml) {
s = YAMLUtils.getObjectWriter().writeValueAsString(api);
jsonNode = YAMLUtils.getInstance().getAsNode(s);
}
else {
s = JSONUtils.getObjectWriter().writeValueAsString(api);
jsonNode = JSONUtils.getInstance().getAsNode(s);
}
JsonPathExpressionEngine engine = new JsonPathExpressionEngine();
List<String> refPath = engine.getStringMatchPattern(jsonNode, "$..$ref");
String schemaRebuild = s;
// Faccio due passate, prima con i caratteri " e ' in modo da risolvere le ref precisamente,
// poiche' l'algoritmo e' soggetto a problemi quando ci sono nomi inclusi in altri ref. Es.:
// test.yaml#...
// http://test/test.yaml#....
schemaRebuild = replace(refPath, schemaRebuild, true);
schemaRebuild = replace(refPath, schemaRebuild, false);
/*
Object oDescr = engine.getMatchPattern(jsonNode, "$.info.description", JsonPathReturnType.NODE);
if(oDescr!=null) {
String descr = null;
if(oDescr instanceof List<?>) {
@SuppressWarnings("unchecked")
List<String> l = (List<String>) oDescr;
if(!l.isEmpty()) {
descr = l.get(0);
}
}
else if(oDescr instanceof String) {
descr = (String) oDescr;
}
else if(oDescr instanceof JsonNode) {
JsonNode jN = (JsonNode) oDescr;
descr = jN.asText();
}
else {
debug(debug,log,"Description type unknown ["+oDescr.getClass().getName()+"]");
}
if(descr!=null && org.apache.commons.lang.StringUtils.isNotEmpty(descr)) {
schemaRebuild = schemaRebuild.replace("info:", "info:\n x-summary: \""+descr+"\"");
}
}
*/
if(schemaRebuild.startsWith("---")) {
schemaRebuild = schemaRebuild.substring("---".length());
}
if(schemaRebuild.startsWith("\n")) {
schemaRebuild = schemaRebuild.substring("\n".length());
}
String extensions = "extensions:\n" +" ";
schemaRebuild = schemaRebuild.replace(extensions, "");
String ext = " x-";
String extCorrect = " x-";
while(schemaRebuild.contains(ext)) {
schemaRebuild = schemaRebuild.replace(ext, extCorrect);
}
return schemaRebuild;
}
private static String replace(List<String> refPath, String schemaRebuild, boolean usePrefixChar) {
if(refPath!=null && !refPath.isEmpty()) {
for (String ref : refPath) {
//System.out.println("...............ANALIZZO REF ["+ref+"]");
if(schemaRebuild.contains(ref)) {
//System.out.println(" PROCESS ["+ref+"]");
if(ref.startsWith("#")==false) {
//System.out.println(" PROCESS INTERNAL ["+ref+"]");
String destra = ref.substring(ref.indexOf("#"));
String refForReplace = ref;
//System.out.println("destra ["+destra+"]");
//System.out.println("destra ["+refForReplace+"]");
if(usePrefixChar) {
String rep = "\""+refForReplace+"\"";
String destraRep = "\""+destra+"\"";
while(schemaRebuild.contains(rep)) {
schemaRebuild = schemaRebuild.replace(rep, destraRep);
}
rep = "'"+refForReplace+"'";
destraRep = "'"+destra+"'";
while(schemaRebuild.contains(rep)) {
schemaRebuild = schemaRebuild.replace(rep, destraRep);
}
}
else {
while(schemaRebuild.contains(refForReplace)) {
schemaRebuild = schemaRebuild.replace(refForReplace, destra);
}
}
if(refForReplace.startsWith("./") && refForReplace.length()>2) {
//System.out.println("CASO SPECIALE!");
refForReplace = refForReplace.substring(2);
if(usePrefixChar) {
String rep = "\""+refForReplace+"\"";
String destraRep = "\""+destra+"\"";
while(schemaRebuild.contains(rep)) {
schemaRebuild = schemaRebuild.replace(rep, destraRep);
}
rep = "'"+refForReplace+"'";
destraRep = "'"+destra+"'";
while(schemaRebuild.contains(rep)) {
schemaRebuild = schemaRebuild.replace(rep, destraRep);
}
}
else {
while(schemaRebuild.contains(refForReplace)) {
schemaRebuild = schemaRebuild.replace(refForReplace, destra);
}
}
}
}
}
}
}
return schemaRebuild;
}
private static int checkSchema(int profondita, String sorgente, Schema<?> schema) {
if(profondita>1000) {
return profondita; // evitare stack overflow
}
@SuppressWarnings("rawtypes")
Map<String, Schema> properties = schema.getProperties();
if(properties!=null && !properties.isEmpty()) {
for (String key : properties.keySet()) {
Schema<?> value = properties.get(key);
String sorgenteInterno = sorgente+".";
if(value.getName()!=null) {
sorgenteInterno = sorgenteInterno + value.getName();
}
else {
sorgenteInterno = sorgenteInterno + "schemaProfondita"+profondita;
}
//debug(debug,log,"SCHEMA ("+sorgente+") *"+key+"* ["+value.getName()+"] ["+value.getType()+"] ["+value.getFormat()+"] ["+value.getExample()+"] ref["+value.get$ref()+"] schema["+value.getClass().getName()+"]");
@SuppressWarnings("unused")
int p = checkSchema((profondita+1),sorgenteInterno,value);
}
}
return profondita;
}
}