OpenapiApi.java

  1. /*
  2.  * GovWay - A customizable API Gateway
  3.  * https://govway.org
  4.  *
  5.  * Copyright (c) 2005-2025 Link.it srl (https://link.it).
  6.  *
  7.  * This program is free software: you can redistribute it and/or modify
  8.  * it under the terms of the GNU General Public License version 3, as published by
  9.  * the Free Software Foundation.
  10.  *
  11.  * This program is distributed in the hope that it will be useful,
  12.  * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13.  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14.  * GNU General Public License for more details.
  15.  *
  16.  * You should have received a copy of the GNU General Public License
  17.  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  18.  *
  19.  */


  20. package org.openspcoop2.utils.openapi;

  21. import java.net.URL;
  22. import java.util.HashMap;
  23. import java.util.List;
  24. import java.util.Map;

  25. import org.apache.commons.lang.StringUtils;
  26. import org.openapi4j.core.model.v3.OAI3Context;
  27. import org.openapi4j.core.util.TreeUtil;
  28. import org.openapi4j.core.validation.ValidationResults;
  29. import org.openapi4j.parser.model.v3.OpenApi3;
  30. import org.openapi4j.parser.validation.v3.OpenApi3Validator;
  31. import org.openspcoop2.utils.Utilities;
  32. import org.openspcoop2.utils.json.JSONUtils;
  33. import org.openspcoop2.utils.json.JsonPathExpressionEngine;
  34. import org.openspcoop2.utils.json.JsonPathNotFoundException;
  35. import org.openspcoop2.utils.json.YAMLUtils;
  36. import org.openspcoop2.utils.rest.ApiFormats;
  37. import org.openspcoop2.utils.rest.ParseWarningException;
  38. import org.openspcoop2.utils.rest.ProcessingException;
  39. import org.openspcoop2.utils.rest.api.Api;

  40. import com.fasterxml.jackson.databind.JsonNode;

  41. import io.swagger.v3.oas.models.OpenAPI;
  42. import io.swagger.v3.oas.models.media.Schema;

  43. /**
  44.  * SwaggerApi
  45.  *
  46.  * @author Andrea Poli (apoli@link.it)
  47.  * @author $Author$
  48.  * @version $Rev$, $Date$
  49.  *
  50.  */
  51. public class OpenapiApi extends Api {
  52.    
  53.     /**
  54.      *
  55.      */
  56.     private static final long serialVersionUID = 1L;
  57.    
  58.     private transient OpenAPI api;
  59.     private transient Map<String, Schema<?>> definitions;
  60.     private String apiRaw;
  61.     private String parseWarningResult;
  62.     private ApiFormats format;

  63.     // struttura una volta che l'api è stata inizializzata per la validazione (e' serializzabile e cachabile)
  64.     private OpenapiApiValidatorStructure validationStructure;

  65.     public OpenapiApi(ApiFormats format, OpenAPI swagger, String apiRaw, String parseWarningResult) {
  66.         this.format = format;
  67.         this.api = swagger;
  68.         this.apiRaw = apiRaw;
  69.         this.parseWarningResult = parseWarningResult;
  70.         this.definitions = new HashMap<String, Schema<?>>();
  71.     }
  72.    
  73.     public ApiFormats getFormat() {
  74.         return this.format;
  75.     }
  76.    
  77.     public OpenAPI getApi() {
  78.         return this.api;
  79.     }

  80.     public String getApiRaw() {
  81.         return this.apiRaw;
  82.     }

  83.     public String getParseWarningResult() {
  84.         return this.parseWarningResult;
  85.     }
  86.    
  87.     public Map<String, Schema<?>> getDefinitions() {
  88.         return this.definitions;
  89.     }

  90.     public Map<String, Schema<?>> getAllDefinitions() {
  91.         Map<String, Schema<?>> map = new HashMap<>();
  92.         map.putAll(this.getDefinitions());
  93.         if(this.api.getComponents() != null && this.api.getComponents().getSchemas() != null)
  94.             for(String k: this.api.getComponents().getSchemas().keySet()) {
  95.                 map.put(k, this.api.getComponents().getSchemas().get(k));
  96.             }
  97.         return map;
  98.     }

  99.     public void setDefinitions(Map<String, Schema<?>> definitions) {
  100.         this.definitions = definitions;
  101.     }

  102.     public OpenapiApiValidatorStructure getValidationStructure() {
  103.         return this.validationStructure;
  104.     }

  105.     public void setValidationStructure(OpenapiApiValidatorStructure validationStructure) {
  106.         this.validationStructure = validationStructure;
  107.     }
  108.    
  109.     @Override
  110.     public void validate() throws ProcessingException,ParseWarningException {
  111.         this.validate(false);
  112.     }
  113.     @Override
  114.     public void validate(boolean validateBodyParameterElement) throws ProcessingException, ParseWarningException {
  115.         this.validate(false, false);
  116.     }
  117.     @Override
  118.     public void validate(boolean usingFromSetProtocolInfo, boolean validateBodyParameterElement) throws ProcessingException, ParseWarningException {
  119.        
  120.         // Primo valido l'openapi
  121.         if(!usingFromSetProtocolInfo) {
  122.             // Se valido poi non riesco a caricare OpenAPI che comunque non sono validi
  123.             if(ApiFormats.OPEN_API_3.equals(this.format)) {
  124.                 this._validateOpenAPI3();
  125.             }
  126.         }
  127.                
  128.         // Valido la struttura
  129.         super.validate(usingFromSetProtocolInfo, validateBodyParameterElement);
  130.        
  131.         // Se ho trovato dei warning li emetto
  132.         if(this.parseWarningResult!=null && StringUtils.isNotEmpty(this.parseWarningResult)) {
  133.             throw new ParseWarningException("\n"+this.parseWarningResult);
  134.         }
  135.     }
  136.    
  137.     private void _validateOpenAPI3() throws ProcessingException {
  138.        
  139.         try {
  140.        
  141.             YAMLUtils yamlUtils = YAMLUtils.getInstance();
  142.             JSONUtils jsonUtils = JSONUtils.getInstance();
  143.                                    
  144.             boolean apiRawIsYaml = yamlUtils.isYaml(this.apiRaw);
  145.             JsonNode schemaNodeRoot = null;
  146.             if(apiRawIsYaml) {
  147.                 schemaNodeRoot = yamlUtils.getAsNode(this.apiRaw);
  148.             }
  149.             else {
  150.                 schemaNodeRoot = jsonUtils.getAsNode(this.apiRaw);
  151.             }
  152.        
  153.             JsonPathExpressionEngine engine = new JsonPathExpressionEngine();
  154.             List<String> refPath = null;
  155.             try {
  156.                 refPath = engine.getStringMatchPattern(schemaNodeRoot, "$..$ref");
  157.             }catch(JsonPathNotFoundException notFound){
  158.                 //System.out.println("NOT FOUND: "+notFound.getMessage());
  159.             }
  160.             boolean refNonRisolvibili = false;
  161.             if(refPath!=null && !refPath.isEmpty()) {
  162.                 for (String refP : refPath) {
  163.                     if(refP!=null) {
  164.                         String ref = refP.trim();
  165.                         if(ref.startsWith("#")) {
  166.                             continue; // relativo verso il file stesso
  167.                         }
  168.                         else {
  169.                             refNonRisolvibili = true; // ref verso altri file (emettero solo il warning se c'è)
  170.                             break;
  171.                         }
  172.                     }
  173.                 }
  174.             }
  175.             if(!refNonRisolvibili) {
  176.                 // Costruisco OpenAPI3                  
  177.                 OAI3Context context = null;
  178.                 OpenApi3 openApi4j = null;
  179.                 try {
  180.                     context = new OAI3Context(new URL("file:/"), schemaNodeRoot, null);
  181.                     openApi4j = TreeUtil.json.convertValue(context.getBaseDocument(), OpenApi3.class);
  182.                     openApi4j.setContext(context);
  183.                 }catch(Throwable e) {
  184.                     throw new ProcessingException(e.getMessage(), e);
  185.                 }
  186.                 try {
  187.                     ValidationResults results = OpenApi3Validator.instance().validate(openApi4j);
  188.                     if(!results.isValid()) {
  189.                         throw new ProcessingException(results.toString());
  190.                     }
  191.                 }catch(org.openapi4j.core.validation.ValidationException valExc) {
  192.                     if(valExc.results()!=null) {
  193.                         throw new ProcessingException(valExc.results().toString());
  194.                     }
  195.                     else {
  196.                         throw new ProcessingException(valExc.getMessage());
  197.                     }
  198.                 }catch(ProcessingException pe) {
  199.                     throw pe;
  200.                 }catch(Throwable e) {
  201.                     e.printStackTrace(System.out);
  202.                     Throwable eAnalyze = Utilities.getInnerNotEmptyMessageException(e);
  203.                     throw new ProcessingException(eAnalyze!=null ? eAnalyze.getMessage(): e.getMessage(), e);
  204.                 }
  205.             }
  206.         }
  207.         catch(ProcessingException pe) {
  208.             if(this.parseWarningResult!=null && StringUtils.isNotEmpty(this.parseWarningResult)) {
  209.                 throw new ProcessingException("\n"+this.parseWarningResult+"\n"+pe.getMessage());
  210.             }
  211.             else {
  212.                 throw new ProcessingException(pe.getMessage());
  213.             }
  214.         }
  215.         catch(Exception e) {
  216.             throw new ProcessingException(e.getMessage(),e);
  217.         }
  218.     }
  219. }